Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 96fb7d5040 | |||
| 47cf8abbda |
@@ -9,7 +9,7 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
VERSION: "5.3.3"
|
VERSION: "5.3.4"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-windows-qt:
|
build-windows-qt:
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
# Generated by qmake (3.1) (Qt 5.15.18)
|
# Generated by qmake (3.1) (Qt 5.15.18)
|
||||||
# Project: triangles-qt.pro
|
# Project: triangles-qt.pro
|
||||||
# Template: app
|
# Template: app
|
||||||
# Command: C:/msys64/mingw64/bin/qmake-qt5.exe -o Makefile triangles-qt.pro USE_QRCODE=1 USE_UPNP=-
|
# Command: C:/msys64/mingw64/bin/qmake-qt5.exe -o Makefile triangles-qt.pro -spec win32-g++ CONFIG+=release
|
||||||
#############################################################################
|
#############################################################################
|
||||||
|
|
||||||
MAKEFILE = Makefile
|
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/lib/qtmain.prl \
|
||||||
C:/msys64/mingw64/share/qt5/mkspecs/features/build_pass.prf \
|
C:/msys64/mingw64/share/qt5/mkspecs/features/build_pass.prf \
|
||||||
src/qt/triangles.qrc
|
src/qt/triangles.qrc
|
||||||
$(QMAKE) -o Makefile triangles-qt.pro USE_QRCODE=1 USE_UPNP=-
|
$(QMAKE) -o Makefile triangles-qt.pro -spec win32-g++ CONFIG+=release
|
||||||
C:/msys64/mingw64/share/qt5/mkspecs/features/spec_pre.prf:
|
C:/msys64/mingw64/share/qt5/mkspecs/features/spec_pre.prf:
|
||||||
C:/msys64/mingw64/share/qt5/mkspecs/qdevice.pri:
|
C:/msys64/mingw64/share/qt5/mkspecs/qdevice.pri:
|
||||||
C:/msys64/mingw64/share/qt5/mkspecs/features/device_config.prf:
|
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:
|
C:/msys64/mingw64/share/qt5/mkspecs/features/build_pass.prf:
|
||||||
src/qt/triangles.qrc:
|
src/qt/triangles.qrc:
|
||||||
qmake: FORCE
|
qmake: FORCE
|
||||||
@$(QMAKE) -o Makefile triangles-qt.pro USE_QRCODE=1 USE_UPNP=-
|
@$(QMAKE) -o Makefile triangles-qt.pro -spec win32-g++ CONFIG+=release
|
||||||
|
|
||||||
qmake_all: FORCE
|
qmake_all: FORCE
|
||||||
|
|
||||||
|
|||||||
@@ -8,9 +8,12 @@
|
|||||||
#include <boost/filesystem/fstream.hpp>
|
#include <boost/filesystem/fstream.hpp>
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
|
|
||||||
|
#include <zlib.h>
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
namespace fs = boost::filesystem;
|
namespace fs = boost::filesystem;
|
||||||
using boost::asio::ip::tcp;
|
using boost::asio::ip::tcp;
|
||||||
@@ -181,4 +184,195 @@ bool FetchFileList(const std::string& host,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- tar.gz bootstrap support ---
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// Parse a tar octal field (ASCII octal, null/space terminated)
|
||||||
|
static int64_t ParseTarOctal(const char* field, size_t len)
|
||||||
|
{
|
||||||
|
int64_t result = 0;
|
||||||
|
for (size_t i = 0; i < len && field[i] != '\0' && field[i] != ' '; i++) {
|
||||||
|
if (field[i] < '0' || field[i] > '7') continue;
|
||||||
|
result = (result << 3) | (field[i] - '0');
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract a tar.gz file to a destination directory
|
||||||
|
static bool ExtractTarGz(const fs::path& tarGzPath,
|
||||||
|
const fs::path& destDir,
|
||||||
|
std::string& strError)
|
||||||
|
{
|
||||||
|
gzFile gz = gzopen(tarGzPath.string().c_str(), "rb");
|
||||||
|
if (!gz) {
|
||||||
|
strError = "Cannot open " + tarGzPath.string();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
gzbuffer(gz, 262144); // 256 KB buffer for performance
|
||||||
|
|
||||||
|
char header[512];
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
int bytesRead = gzread(gz, header, 512);
|
||||||
|
if (bytesRead == 0) break; // EOF
|
||||||
|
if (bytesRead != 512) {
|
||||||
|
strError = "Truncated tar header";
|
||||||
|
gzclose(gz);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// End-of-archive marker (zero block)
|
||||||
|
bool allZero = true;
|
||||||
|
for (int i = 0; i < 512; i++) {
|
||||||
|
if (header[i] != 0) { allZero = false; break; }
|
||||||
|
}
|
||||||
|
if (allZero) break;
|
||||||
|
|
||||||
|
// Parse filename: name (offset 0, 100 bytes) + optional prefix (offset 345, 155 bytes)
|
||||||
|
char name[101] = {0};
|
||||||
|
char prefix[156] = {0};
|
||||||
|
memcpy(name, header, 100);
|
||||||
|
memcpy(prefix, header + 345, 155);
|
||||||
|
|
||||||
|
std::string fullName;
|
||||||
|
if (prefix[0] != '\0')
|
||||||
|
fullName = std::string(prefix) + "/" + std::string(name);
|
||||||
|
else
|
||||||
|
fullName = std::string(name);
|
||||||
|
|
||||||
|
// Security: reject absolute paths and path traversal
|
||||||
|
if (fullName.empty() || fullName[0] == '/' || fullName.find("..") != std::string::npos) {
|
||||||
|
strError = "Unsafe path in tar archive: " + fullName;
|
||||||
|
gzclose(gz);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char typeflag = header[156];
|
||||||
|
int64_t fileSize = ParseTarOctal(header + 124, 12);
|
||||||
|
|
||||||
|
if (typeflag == '5' || (!fullName.empty() && fullName.back() == '/')) {
|
||||||
|
// Directory entry
|
||||||
|
fs::create_directories(destDir / fullName);
|
||||||
|
} else if (typeflag == '0' || typeflag == '\0') {
|
||||||
|
// Regular file
|
||||||
|
fs::path filePath = destDir / fullName;
|
||||||
|
fs::create_directories(filePath.parent_path());
|
||||||
|
|
||||||
|
FILE* outFile = fopen(filePath.string().c_str(), "wb");
|
||||||
|
if (!outFile) {
|
||||||
|
strError = "Cannot create file: " + filePath.string();
|
||||||
|
gzclose(gz);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t remaining = fileSize;
|
||||||
|
char buf[65536];
|
||||||
|
while (remaining > 0) {
|
||||||
|
int toRead = (remaining > (int64_t)sizeof(buf)) ? (int)sizeof(buf) : (int)remaining;
|
||||||
|
int n = gzread(gz, buf, toRead);
|
||||||
|
if (n <= 0) {
|
||||||
|
fclose(outFile);
|
||||||
|
strError = "Truncated tar data for: " + fullName;
|
||||||
|
gzclose(gz);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
fwrite(buf, 1, n, outFile);
|
||||||
|
remaining -= n;
|
||||||
|
}
|
||||||
|
fclose(outFile);
|
||||||
|
|
||||||
|
// Skip padding to next 512-byte boundary
|
||||||
|
int64_t pad = (512 - (fileSize % 512)) % 512;
|
||||||
|
if (pad > 0) {
|
||||||
|
char padBuf[512];
|
||||||
|
if (gzread(gz, padBuf, (unsigned)pad) != (int)pad) {
|
||||||
|
strError = "Truncated tar padding for: " + fullName;
|
||||||
|
gzclose(gz);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Unknown entry type - skip its data
|
||||||
|
int64_t totalSkip = fileSize + ((512 - (fileSize % 512)) % 512);
|
||||||
|
char skipBuf[512];
|
||||||
|
while (totalSkip > 0) {
|
||||||
|
int toRead = (totalSkip > 512) ? 512 : (int)totalSkip;
|
||||||
|
if (gzread(gz, skipBuf, toRead) != toRead) break;
|
||||||
|
totalSkip -= toRead;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gzclose(gz);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
bool DownloadBootstrap(const std::string& host,
|
||||||
|
const fs::path& dataDir,
|
||||||
|
ProgressCallback progressFn,
|
||||||
|
std::string& strError)
|
||||||
|
{
|
||||||
|
bool gotBlockFile = false;
|
||||||
|
|
||||||
|
// Try downloading bootstrap.tar.gz first
|
||||||
|
fs::path tmpTarGz = dataDir / "bootstrap.tar.gz.tmp";
|
||||||
|
std::string tarUrl = std::string(BASE_PATH) + "bootstrap.tar.gz";
|
||||||
|
|
||||||
|
bool tarDownloaded = DownloadFile(host, tarUrl, tmpTarGz, progressFn, strError);
|
||||||
|
|
||||||
|
if (tarDownloaded) {
|
||||||
|
bool extractOk = ExtractTarGz(tmpTarGz, dataDir, strError);
|
||||||
|
fs::remove(tmpTarGz);
|
||||||
|
|
||||||
|
if (extractOk && fs::exists(dataDir / "blk0001.dat"))
|
||||||
|
gotBlockFile = true;
|
||||||
|
// If extraction failed, fall through to legacy path
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gotBlockFile) {
|
||||||
|
// Fallback: try filelist.txt + individual file downloads
|
||||||
|
std::string fallbackError;
|
||||||
|
std::vector<std::string> files;
|
||||||
|
if (!FetchFileList(host, files, fallbackError)) {
|
||||||
|
if (!tarDownloaded)
|
||||||
|
strError = strError + " (fallback also failed: " + fallbackError + ")";
|
||||||
|
else
|
||||||
|
strError = "Extraction failed: " + strError + " (fallback also failed: " + fallbackError + ")";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < files.size(); i++) {
|
||||||
|
fs::path destPath = dataDir / files[i];
|
||||||
|
fs::create_directories(destPath.parent_path());
|
||||||
|
|
||||||
|
std::string urlPath = std::string(BASE_PATH) + files[i];
|
||||||
|
if (!DownloadFile(host, urlPath, destPath, progressFn, strError))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
gotBlockFile = fs::exists(dataDir / "blk0001.dat");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gotBlockFile) {
|
||||||
|
strError = "No blk0001.dat after download";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove any extracted txleveldb/ and database/ - they were built on
|
||||||
|
// a different machine and won't work here. FastImportBlockFile() will
|
||||||
|
// rebuild the index directly from blk0001.dat on next startup.
|
||||||
|
fs::path txleveldb = dataDir / "txleveldb";
|
||||||
|
fs::path database = dataDir / "database";
|
||||||
|
if (fs::exists(txleveldb))
|
||||||
|
fs::remove_all(txleveldb);
|
||||||
|
if (fs::exists(database))
|
||||||
|
fs::remove_all(database);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Bootstrap
|
} // namespace Bootstrap
|
||||||
|
|||||||
@@ -34,6 +34,13 @@ namespace Bootstrap {
|
|||||||
std::vector<std::string>& files,
|
std::vector<std::string>& files,
|
||||||
std::string& strError);
|
std::string& strError);
|
||||||
|
|
||||||
|
// Download bootstrap.tar.gz and extract to dataDir.
|
||||||
|
// Falls back to filelist.txt + individual file download if tar.gz unavailable.
|
||||||
|
bool DownloadBootstrap(const std::string& host,
|
||||||
|
const boost::filesystem::path& dataDir,
|
||||||
|
ProgressCallback progressFn,
|
||||||
|
std::string& strError);
|
||||||
|
|
||||||
} // namespace Bootstrap
|
} // namespace Bootstrap
|
||||||
|
|
||||||
#endif // TRIANGLES_BOOTSTRAP_H
|
#endif // TRIANGLES_BOOTSTRAP_H
|
||||||
|
|||||||
+1
-1
@@ -8,7 +8,7 @@
|
|||||||
// These need to be macros, as version.cpp's and triangles-qt.rc's voodoo requires it
|
// 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_MAJOR 5
|
||||||
#define CLIENT_VERSION_MINOR 3
|
#define CLIENT_VERSION_MINOR 3
|
||||||
#define CLIENT_VERSION_REVISION 3
|
#define CLIENT_VERSION_REVISION 4
|
||||||
#define CLIENT_VERSION_BUILD 0
|
#define CLIENT_VERSION_BUILD 0
|
||||||
|
|
||||||
// Converts the parameter X to a string after macro replacement on X has been performed.
|
// Converts the parameter X to a string after macro replacement on X has been performed.
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include "checkpoints.h"
|
#include "checkpoints.h"
|
||||||
#include "smessage.h"
|
#include "smessage.h"
|
||||||
#include "openssl_compat.h"
|
#include "openssl_compat.h"
|
||||||
|
#include "bootstrap.h"
|
||||||
#include "tor/tor_embedded.h"
|
#include "tor/tor_embedded.h"
|
||||||
#include "tor/onion_v3.h"
|
#include "tor/onion_v3.h"
|
||||||
#include "tor/tor_process.h"
|
#include "tor/tor_process.h"
|
||||||
@@ -797,6 +798,43 @@ bool AppInit2()
|
|||||||
for (string strDest : mapMultiArgs["-seednode"])
|
for (string strDest : mapMultiArgs["-seednode"])
|
||||||
AddOneShot(strDest);
|
AddOneShot(strDest);
|
||||||
|
|
||||||
|
// ********************************************************* Step 6b: bootstrap download (daemon)
|
||||||
|
#ifndef QT_GUI
|
||||||
|
if (GetBoolArg("-bootstrap", false))
|
||||||
|
{
|
||||||
|
fs::path dataPath = GetDataDir();
|
||||||
|
std::string host = Bootstrap::DEFAULT_HOST;
|
||||||
|
std::string strError;
|
||||||
|
|
||||||
|
uiInterface.InitMessage(_("Downloading blockchain snapshot..."));
|
||||||
|
printf("Bootstrap: contacting %s...\n", host.c_str());
|
||||||
|
|
||||||
|
auto progressFn = [](int64_t bytesDownloaded, int64_t totalBytes) {
|
||||||
|
if (totalBytes > 0) {
|
||||||
|
printf("\rBootstrap: %lld / %lld MB (%lld%%)",
|
||||||
|
(long long)(bytesDownloaded / (1024*1024)),
|
||||||
|
(long long)(totalBytes / (1024*1024)),
|
||||||
|
(long long)((bytesDownloaded * 100) / totalBytes));
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
bool success = Bootstrap::DownloadBootstrap(host, dataPath, progressFn, strError);
|
||||||
|
if (!success) {
|
||||||
|
host = Bootstrap::FALLBACK_HOST;
|
||||||
|
printf("\nBootstrap: primary host failed, trying fallback %s...\n", host.c_str());
|
||||||
|
success = Bootstrap::DownloadBootstrap(host, dataPath, progressFn, strError);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
printf("\nBootstrap: failed: %s\n", strError.c_str());
|
||||||
|
printf("Bootstrap: skipping, will sync from network.\n");
|
||||||
|
} else {
|
||||||
|
printf("\nBootstrap: done.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// ********************************************************* Step 7: load blockchain
|
// ********************************************************* Step 7: load blockchain
|
||||||
|
|
||||||
if (!bitdb.Open(GetDataDir()))
|
if (!bitdb.Open(GetDataDir()))
|
||||||
@@ -821,6 +859,16 @@ bool AppInit2()
|
|||||||
if (!LoadBlockIndex())
|
if (!LoadBlockIndex())
|
||||||
return InitError(_("Error loading blkindex.dat"));
|
return InitError(_("Error loading blkindex.dat"));
|
||||||
|
|
||||||
|
// If the block index is empty but blk0001.dat exists (bootstrap download),
|
||||||
|
// fast-import: build the index directly from the block file without re-writing
|
||||||
|
// data. Batches LevelDB commits every 200K blocks for speed.
|
||||||
|
if (nBestHeight == 0 && boost::filesystem::exists(GetDataDir() / "blk0001.dat")
|
||||||
|
&& mapBlockIndex.size() <= 1)
|
||||||
|
{
|
||||||
|
uiInterface.InitMessage(_("Importing bootstrap blocks..."));
|
||||||
|
printf("Block index empty but blk0001.dat exists - running fast import...\n");
|
||||||
|
FastImportBlockFile();
|
||||||
|
}
|
||||||
|
|
||||||
// as LoadBlockIndex can take several minutes, it's possible the user
|
// as LoadBlockIndex can take several minutes, it's possible the user
|
||||||
// requested to kill triangles-qt during the last operation. If so, exit.
|
// requested to kill triangles-qt during the last operation. If so, exit.
|
||||||
|
|||||||
+239
-9
@@ -3023,7 +3023,14 @@ bool LoadExternalBlockFile(FILE* fileIn)
|
|||||||
{
|
{
|
||||||
int64_t nStart = GetTimeMillis();
|
int64_t nStart = GetTimeMillis();
|
||||||
|
|
||||||
|
// Get file size for progress reporting
|
||||||
|
int64_t nFileSize = 0;
|
||||||
|
fseek(fileIn, 0, SEEK_END);
|
||||||
|
nFileSize = ftell(fileIn);
|
||||||
|
fseek(fileIn, 0, SEEK_SET);
|
||||||
|
|
||||||
int nLoaded = 0;
|
int nLoaded = 0;
|
||||||
|
int64_t nLastProgressReport = 0;
|
||||||
{
|
{
|
||||||
LOCK(cs_main);
|
LOCK(cs_main);
|
||||||
try {
|
try {
|
||||||
@@ -3074,6 +3081,20 @@ bool LoadExternalBlockFile(FILE* fileIn)
|
|||||||
nLoaded++;
|
nLoaded++;
|
||||||
nPos += 4 + nSize;
|
nPos += 4 + nSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Report progress every 1000 blocks
|
||||||
|
if (nLoaded - nLastProgressReport >= 1000)
|
||||||
|
{
|
||||||
|
nLastProgressReport = nLoaded;
|
||||||
|
if (nFileSize > 0) {
|
||||||
|
int pct = (int)((int64_t)nPos * 100 / nFileSize);
|
||||||
|
printf("Importing blocks... %d blocks loaded (%d%%)\n", nLoaded, pct);
|
||||||
|
uiInterface.InitMessage(strprintf(_("Importing blocks... %d loaded (%d%%)"), nLoaded, pct));
|
||||||
|
} else {
|
||||||
|
printf("Importing blocks... %d blocks loaded\n", nLoaded);
|
||||||
|
uiInterface.InitMessage(strprintf(_("Importing blocks... %d loaded"), nLoaded));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (std::exception &e) {
|
catch (std::exception &e) {
|
||||||
@@ -3085,6 +3106,210 @@ bool LoadExternalBlockFile(FILE* fileIn)
|
|||||||
return nLoaded > 0;
|
return nLoaded > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FastImportBlockFile()
|
||||||
|
{
|
||||||
|
// Fast block import: reads blk0001.dat and builds the block index
|
||||||
|
// directly without re-writing block data. LevelDB writes are batched
|
||||||
|
// every 200K blocks for speed. Only used for trusted bootstrap data
|
||||||
|
// (blocks below the hardcoded checkpoint).
|
||||||
|
|
||||||
|
fs::path blkPath = GetDataDir() / "blk0001.dat";
|
||||||
|
if (!fs::exists(blkPath))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
printf("FastImportBlockFile: starting from %s\n", blkPath.string().c_str());
|
||||||
|
int64_t nStart = GetTimeMillis();
|
||||||
|
|
||||||
|
FILE* fileIn = fopen(blkPath.string().c_str(), "rb");
|
||||||
|
if (!fileIn)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Get file size for progress
|
||||||
|
fseek(fileIn, 0, SEEK_END);
|
||||||
|
int64_t nFileSize = ftell(fileIn);
|
||||||
|
fseek(fileIn, 0, SEEK_SET);
|
||||||
|
|
||||||
|
int nLoaded = 0;
|
||||||
|
int64_t nLastProgressReport = 0;
|
||||||
|
|
||||||
|
{
|
||||||
|
LOCK(cs_main);
|
||||||
|
CAutoFile blkdat(fileIn, SER_DISK, CLIENT_VERSION);
|
||||||
|
|
||||||
|
CTxDB txdb;
|
||||||
|
txdb.TxnBegin();
|
||||||
|
|
||||||
|
unsigned int nPos = 0;
|
||||||
|
while (nPos != (unsigned int)-1 && blkdat.good() && !fRequestShutdown)
|
||||||
|
{
|
||||||
|
// Find message start bytes (same scan as LoadExternalBlockFile)
|
||||||
|
unsigned char pchData[65536];
|
||||||
|
do {
|
||||||
|
fseek(blkdat, nPos, SEEK_SET);
|
||||||
|
int nRead = fread(pchData, 1, sizeof(pchData), blkdat);
|
||||||
|
if (nRead <= 8)
|
||||||
|
{
|
||||||
|
nPos = (unsigned int)-1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
void* nFind = memchr(pchData, pchMessageStart[0], nRead+1-sizeof(pchMessageStart));
|
||||||
|
if (nFind)
|
||||||
|
{
|
||||||
|
if (memcmp(nFind, pchMessageStart, sizeof(pchMessageStart))==0)
|
||||||
|
{
|
||||||
|
nPos += ((unsigned char*)nFind - pchData) + sizeof(pchMessageStart);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
nPos += ((unsigned char*)nFind - pchData) + 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
nPos += sizeof(pchData) - sizeof(pchMessageStart) + 1;
|
||||||
|
} while(!fRequestShutdown);
|
||||||
|
|
||||||
|
if (nPos == (unsigned int)-1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
fseek(blkdat, nPos, SEEK_SET);
|
||||||
|
unsigned int nSize;
|
||||||
|
blkdat >> nSize;
|
||||||
|
|
||||||
|
if (nSize == 0 || nSize > MAX_BLOCK_SIZE)
|
||||||
|
{
|
||||||
|
nPos += 4 + nSize;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// nBlockPos = file position where the block data starts
|
||||||
|
// (after 4-byte message start + 4-byte size)
|
||||||
|
unsigned int nBlockPos = nPos + 4;
|
||||||
|
|
||||||
|
CBlock block;
|
||||||
|
blkdat >> block;
|
||||||
|
|
||||||
|
uint256 hash = block.GetHash();
|
||||||
|
if (mapBlockIndex.count(hash))
|
||||||
|
{
|
||||||
|
nPos += 4 + nSize;
|
||||||
|
continue; // already indexed
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create CBlockIndex
|
||||||
|
CBlockIndex* pindexNew = new CBlockIndex(1, nBlockPos, block);
|
||||||
|
if (!pindexNew)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Link to previous block
|
||||||
|
map<uint256, CBlockIndex*>::iterator miPrev = mapBlockIndex.find(block.hashPrevBlock);
|
||||||
|
if (miPrev != mapBlockIndex.end())
|
||||||
|
{
|
||||||
|
pindexNew->pprev = (*miPrev).second;
|
||||||
|
pindexNew->nHeight = pindexNew->pprev->nHeight + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Chain trust
|
||||||
|
pindexNew->nChainTrust = (pindexNew->pprev ? pindexNew->pprev->nChainTrust : 0) + pindexNew->GetBlockTrust();
|
||||||
|
|
||||||
|
// Stake entropy bit
|
||||||
|
pindexNew->SetStakeEntropyBit(block.GetStakeEntropyBit());
|
||||||
|
|
||||||
|
// Stake modifier (minimal for blocks far below checkpoint)
|
||||||
|
int nCheckpointHeight = Checkpoints::GetTotalBlocksEstimate();
|
||||||
|
if (pindexNew->nHeight >= nCheckpointHeight - 1000)
|
||||||
|
{
|
||||||
|
uint64_t nStakeModifier = 0;
|
||||||
|
bool fGeneratedStakeModifier = false;
|
||||||
|
ComputeNextStakeModifier(pindexNew->pprev, nStakeModifier, fGeneratedStakeModifier);
|
||||||
|
pindexNew->SetStakeModifier(nStakeModifier, fGeneratedStakeModifier);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pindexNew->SetStakeModifier(0, pindexNew->nHeight == 0);
|
||||||
|
}
|
||||||
|
pindexNew->nStakeModifierChecksum = GetStakeModifierChecksum(pindexNew);
|
||||||
|
|
||||||
|
// Money supply tracking
|
||||||
|
pindexNew->nMint = 0;
|
||||||
|
pindexNew->nMoneySupply = (pindexNew->pprev ? pindexNew->pprev->nMoneySupply : 0);
|
||||||
|
|
||||||
|
// PoS stake seen set
|
||||||
|
if (pindexNew->IsProofOfStake())
|
||||||
|
setStakeSeen.insert(make_pair(pindexNew->prevoutStake, pindexNew->nStakeTime));
|
||||||
|
|
||||||
|
// Insert into mapBlockIndex
|
||||||
|
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first;
|
||||||
|
pindexNew->phashBlock = &((*mi).first);
|
||||||
|
|
||||||
|
// Link pnext for previous block
|
||||||
|
if (pindexNew->pprev)
|
||||||
|
pindexNew->pprev->pnext = pindexNew;
|
||||||
|
|
||||||
|
// Write block index to batch
|
||||||
|
txdb.WriteBlockIndex(CDiskBlockIndex(pindexNew));
|
||||||
|
|
||||||
|
// Build tx index entries
|
||||||
|
unsigned int nTxPos = nBlockPos + ::GetSerializeSize(CBlock(), SER_DISK, CLIENT_VERSION)
|
||||||
|
- (2 * GetSizeOfCompactSize(0)) + GetSizeOfCompactSize(block.vtx.size());
|
||||||
|
for (unsigned int i = 0; i < block.vtx.size(); i++)
|
||||||
|
{
|
||||||
|
const CTransaction& tx = block.vtx[i];
|
||||||
|
CDiskTxPos posThisTx(1, nBlockPos, nTxPos);
|
||||||
|
txdb.UpdateTxIndex(tx.GetHash(), CTxIndex(posThisTx, tx.vout.size()));
|
||||||
|
nTxPos += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update best chain
|
||||||
|
if (pindexNew->nChainTrust > nBestChainTrust)
|
||||||
|
{
|
||||||
|
hashBestChain = hash;
|
||||||
|
pindexBest = pindexNew;
|
||||||
|
pblockindexFBBHLast = NULL;
|
||||||
|
nBestHeight = pindexNew->nHeight;
|
||||||
|
nBestChainTrust = pindexNew->nChainTrust;
|
||||||
|
nTimeBestReceived = GetTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set genesis block
|
||||||
|
if (pindexGenesisBlock == NULL && pindexNew->nHeight == 0)
|
||||||
|
pindexGenesisBlock = pindexNew;
|
||||||
|
|
||||||
|
nLoaded++;
|
||||||
|
nPos += 4 + nSize;
|
||||||
|
|
||||||
|
// Batch commit every 200K blocks for LevelDB efficiency
|
||||||
|
if (nLoaded % 200000 == 0)
|
||||||
|
{
|
||||||
|
txdb.WriteHashBestChain(hashBestChain);
|
||||||
|
txdb.TxnCommit();
|
||||||
|
txdb.TxnBegin();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Report progress every 5000 blocks to keep GUI responsive.
|
||||||
|
// AppInit2 runs on the GUI thread, so uiInterface.InitMessage
|
||||||
|
// triggers processEvents() which prevents the window from freezing.
|
||||||
|
if (nLoaded % 5000 == 0)
|
||||||
|
{
|
||||||
|
int pct = (nFileSize > 0) ? (int)((int64_t)nPos * 100 / nFileSize) : 0;
|
||||||
|
printf("FastImport: %d blocks indexed (%d%%)\n", nLoaded, pct);
|
||||||
|
uiInterface.InitMessage(strprintf(_("Importing blocks... %d indexed (%d%%)"), nLoaded, pct));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Final commit
|
||||||
|
if (pindexBest)
|
||||||
|
{
|
||||||
|
txdb.WriteHashBestChain(hashBestChain);
|
||||||
|
|
||||||
|
// Write sync checkpoint
|
||||||
|
Checkpoints::WriteSyncCheckpoint(hashBestChain);
|
||||||
|
}
|
||||||
|
txdb.TxnCommit();
|
||||||
|
}
|
||||||
|
|
||||||
|
nTransactionsUpdated++;
|
||||||
|
printf("FastImportBlockFile: indexed %d blocks in %"PRId64"ms\n", nLoaded, GetTimeMillis() - nStart);
|
||||||
|
return nLoaded > 0;
|
||||||
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// CAlert
|
// CAlert
|
||||||
@@ -3497,11 +3722,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
|
|||||||
// Trigger them to send a getblocks request for the next batch of inventory
|
// Trigger them to send a getblocks request for the next batch of inventory
|
||||||
if (inv.hash == pfrom->hashContinue)
|
if (inv.hash == pfrom->hashContinue)
|
||||||
{
|
{
|
||||||
// triangles: send latest proof-of-work block to allow the
|
// Send the best block hash to trigger the next getblocks.
|
||||||
// download node to accept as orphan (proof-of-stake
|
// Original code sent the last PoW block, but since PoW ended
|
||||||
// block might be rejected by stake connection check)
|
// at block 9000, that always sent an ancient block causing
|
||||||
|
// thousands of redundant round-trips through known blocks.
|
||||||
vector<CInv> vInv;
|
vector<CInv> vInv;
|
||||||
vInv.push_back(CInv(MSG_BLOCK, GetLastBlockIndex(pindexBest, false)->GetBlockHash()));
|
vInv.push_back(CInv(MSG_BLOCK, hashBestChain));
|
||||||
pfrom->PushMessage("inv", vInv);
|
pfrom->PushMessage("inv", vInv);
|
||||||
pfrom->hashContinue = 0;
|
pfrom->hashContinue = 0;
|
||||||
}
|
}
|
||||||
@@ -3549,7 +3775,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
|
|||||||
// Send the rest of the chain
|
// Send the rest of the chain
|
||||||
if (pindex)
|
if (pindex)
|
||||||
pindex = pindex->pnext;
|
pindex = pindex->pnext;
|
||||||
int nLimit = IsInitialBlockDownload() ? 20000 : 500;
|
// Send larger batches when the requester is far behind (syncing).
|
||||||
|
// The original check used our own IBD state, but we're the seed node
|
||||||
|
// (fully synced), so it always returned 500. Check how far behind
|
||||||
|
// the requester is instead.
|
||||||
|
int nLimit = (pindex && pindexBest && pindexBest->nHeight - pindex->nHeight > 1000) ? 10000 : 500;
|
||||||
printf("IBD-DIAG: getblocks request from peer %s: start=%d stop=%s limit=%d\n",
|
printf("IBD-DIAG: getblocks request from peer %s: start=%d stop=%s limit=%d\n",
|
||||||
pfrom->addr.ToString().c_str(), (pindex ? pindex->nHeight : -1),
|
pfrom->addr.ToString().c_str(), (pindex ? pindex->nHeight : -1),
|
||||||
hashStop.ToString().substr(0,20).c_str(), nLimit);
|
hashStop.ToString().substr(0,20).c_str(), nLimit);
|
||||||
@@ -3775,7 +4005,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
|
|||||||
if (IsInitialBlockDownload())
|
if (IsInitialBlockDownload())
|
||||||
{
|
{
|
||||||
static int nBlocksSinceRequest = 0;
|
static int nBlocksSinceRequest = 0;
|
||||||
if (++nBlocksSinceRequest >= 1000)
|
if (++nBlocksSinceRequest >= 5000)
|
||||||
{
|
{
|
||||||
nBlocksSinceRequest = 0;
|
nBlocksSinceRequest = 0;
|
||||||
pfrom->pindexLastGetBlocksBegin = NULL;
|
pfrom->pindexLastGetBlocksBegin = NULL;
|
||||||
@@ -4231,7 +4461,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
|
|||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Stall detection: if IBD and no new blocks for 5 seconds, re-request
|
// Stall detection: if IBD and no new blocks for 10 seconds, re-request
|
||||||
//
|
//
|
||||||
if (IsInitialBlockDownload() && !pto->fClient)
|
if (IsInitialBlockDownload() && !pto->fClient)
|
||||||
{
|
{
|
||||||
@@ -4241,8 +4471,8 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
|
|||||||
if (nBestHeight > nLastHeight) {
|
if (nBestHeight > nLastHeight) {
|
||||||
nLastHeight = nBestHeight;
|
nLastHeight = nBestHeight;
|
||||||
nLastBlockReceived = GetTime();
|
nLastBlockReceived = GetTime();
|
||||||
} else if (nLastBlockReceived > 0 && GetTime() - nLastBlockReceived > 2) {
|
} else if (nLastBlockReceived > 0 && GetTime() - nLastBlockReceived > 10) {
|
||||||
if (GetTime() - nLastStallLog >= 10) { // log every 10s max
|
if (GetTime() - nLastStallLog >= 30) { // log every 30s max
|
||||||
printf("IBD-DIAG: STALL at height %d for %ds, peer=%s askfor_queue=%d send_size=%d\n",
|
printf("IBD-DIAG: STALL at height %d for %ds, peer=%s askfor_queue=%d send_size=%d\n",
|
||||||
nBestHeight, (int)(GetTime() - nLastBlockReceived),
|
nBestHeight, (int)(GetTime() - nLastBlockReceived),
|
||||||
pto->addr.ToString().c_str(),
|
pto->addr.ToString().c_str(),
|
||||||
|
|||||||
@@ -112,6 +112,7 @@ CBlockIndex* FindBlockByHeight(int nHeight);
|
|||||||
bool ProcessMessages(CNode* pfrom);
|
bool ProcessMessages(CNode* pfrom);
|
||||||
bool SendMessages(CNode* pto, bool fSendTrickle);
|
bool SendMessages(CNode* pto, bool fSendTrickle);
|
||||||
bool LoadExternalBlockFile(FILE* fileIn);
|
bool LoadExternalBlockFile(FILE* fileIn);
|
||||||
|
bool FastImportBlockFile();
|
||||||
|
|
||||||
bool CheckProofOfWork(uint256 hash, unsigned int nBits);
|
bool CheckProofOfWork(uint256 hash, unsigned int nBits);
|
||||||
unsigned int GetNextTargetRequired(const CBlockIndex* pindexLast, bool fProofOfStake);
|
unsigned int GetNextTargetRequired(const CBlockIndex* pindexLast, bool fProofOfStake);
|
||||||
|
|||||||
@@ -111,6 +111,7 @@ OBJS= \
|
|||||||
obj/miner.o \
|
obj/miner.o \
|
||||||
obj/main.o \
|
obj/main.o \
|
||||||
obj/net.o \
|
obj/net.o \
|
||||||
|
obj/bootstrap.o \
|
||||||
obj/net_bootstrap.o \
|
obj/net_bootstrap.o \
|
||||||
obj/protocol.o \
|
obj/protocol.o \
|
||||||
obj/trianglesrpc.o \
|
obj/trianglesrpc.o \
|
||||||
@@ -232,6 +233,13 @@ obj/tor_embedded.o: tor/tor_embedded.cpp
|
|||||||
-e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \
|
-e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \
|
||||||
rm -f $(@:%.o=%.d)
|
rm -f $(@:%.o=%.d)
|
||||||
|
|
||||||
|
obj/bootstrap.o: bootstrap.cpp
|
||||||
|
$(CXX) -c $(xCXXFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $<
|
||||||
|
@cp $(@:%.o=%.d) $(@:%.o=%.P); \
|
||||||
|
sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
|
||||||
|
-e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \
|
||||||
|
rm -f $(@:%.o=%.d)
|
||||||
|
|
||||||
obj/net_bootstrap.o: net_bootstrap.cpp
|
obj/net_bootstrap.o: net_bootstrap.cpp
|
||||||
$(CXX) -c $(xCXXFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $<
|
$(CXX) -c $(xCXXFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $<
|
||||||
@cp $(@:%.o=%.d) $(@:%.o=%.P); \
|
@cp $(@:%.o=%.d) $(@:%.o=%.P); \
|
||||||
|
|||||||
@@ -146,6 +146,7 @@ OBJS= \
|
|||||||
obj/miner.o \
|
obj/miner.o \
|
||||||
obj/main.o \
|
obj/main.o \
|
||||||
obj/net.o \
|
obj/net.o \
|
||||||
|
obj/bootstrap.o \
|
||||||
obj/net_bootstrap.o \
|
obj/net_bootstrap.o \
|
||||||
obj/protocol.o \
|
obj/protocol.o \
|
||||||
obj/trianglesrpc.o \
|
obj/trianglesrpc.o \
|
||||||
@@ -279,6 +280,13 @@ obj/tor_embedded.o: tor/tor_embedded.cpp
|
|||||||
-e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \
|
-e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \
|
||||||
rm -f $(@:%.o=%.d)
|
rm -f $(@:%.o=%.d)
|
||||||
|
|
||||||
|
obj/bootstrap.o: bootstrap.cpp
|
||||||
|
$(CXX) -c $(xCXXFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $<
|
||||||
|
@cp $(@:%.o=%.d) $(@:%.o=%.P); \
|
||||||
|
sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
|
||||||
|
-e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \
|
||||||
|
rm -f $(@:%.o=%.d)
|
||||||
|
|
||||||
obj/net_bootstrap.o: net_bootstrap.cpp
|
obj/net_bootstrap.o: net_bootstrap.cpp
|
||||||
$(CXX) -c $(xCXXFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $<
|
$(CXX) -c $(xCXXFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $<
|
||||||
@cp $(@:%.o=%.d) $(@:%.o=%.P); \
|
@cp $(@:%.o=%.d) $(@:%.o=%.P); \
|
||||||
|
|||||||
@@ -558,6 +558,16 @@ void StakeMiner(CWallet *pwallet)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Update cached stake weight for UI display (avoids heavy work on UI thread)
|
||||||
|
//
|
||||||
|
{
|
||||||
|
uint64_t nMinWeight = 0, nMaxWeight = 0, nWeight = 0;
|
||||||
|
pwallet->GetStakeWeight(*pwallet, nMinWeight, nMaxWeight, nWeight);
|
||||||
|
pwallet->nCachedStakeWeight = nWeight;
|
||||||
|
pwallet->nCachedStakeWeightTime = GetTime();
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Create new block
|
// Create new block
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ static const int64_t nClientStartupTime = GetTime();
|
|||||||
|
|
||||||
ClientModel::ClientModel(OptionsModel *optionsModel, QObject *parent) :
|
ClientModel::ClientModel(OptionsModel *optionsModel, QObject *parent) :
|
||||||
QObject(parent), optionsModel(optionsModel),
|
QObject(parent), optionsModel(optionsModel),
|
||||||
cachedNumBlocks(0), cachedNumBlocksOfPeers(0), pollTimer(0)
|
cachedNumBlocks(0), cachedNumBlocksOfPeers(0), cachedNumConnections(0), pollTimer(0)
|
||||||
{
|
{
|
||||||
numBlocksAtStartup = -1;
|
numBlocksAtStartup = -1;
|
||||||
|
|
||||||
@@ -34,7 +34,14 @@ ClientModel::~ClientModel()
|
|||||||
|
|
||||||
int ClientModel::getNumConnections() const
|
int ClientModel::getNumConnections() const
|
||||||
{
|
{
|
||||||
return vNodes.size();
|
// Use TRY_LOCK to avoid blocking the UI thread when the network
|
||||||
|
// thread holds cs_vNodes (e.g. during DNS resolution or connections).
|
||||||
|
// Return the cached value if the lock is busy.
|
||||||
|
TRY_LOCK(cs_vNodes, lockNodes);
|
||||||
|
if (lockNodes) {
|
||||||
|
cachedNumConnections = vNodes.size();
|
||||||
|
}
|
||||||
|
return cachedNumConnections;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ClientModel::getNumBlocks() const
|
int ClientModel::getNumBlocks() const
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ private:
|
|||||||
|
|
||||||
int cachedNumBlocks;
|
int cachedNumBlocks;
|
||||||
int cachedNumBlocksOfPeers;
|
int cachedNumBlocksOfPeers;
|
||||||
|
mutable int cachedNumConnections;
|
||||||
|
|
||||||
int numBlocksAtStartup;
|
int numBlocksAtStartup;
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
#define GUICONSTANTS_H
|
#define GUICONSTANTS_H
|
||||||
|
|
||||||
/* Milliseconds between model updates */
|
/* Milliseconds between model updates */
|
||||||
static const int MODEL_UPDATE_DELAY = 500;
|
static const int MODEL_UPDATE_DELAY = 2500;
|
||||||
|
|
||||||
/* AskPassphraseDialog -- Maximum passphrase length */
|
/* AskPassphraseDialog -- Maximum passphrase length */
|
||||||
static const int MAX_PASSPHRASE_SIZE = 1024;
|
static const int MAX_PASSPHRASE_SIZE = 1024;
|
||||||
|
|||||||
+47
-54
@@ -11,6 +11,7 @@
|
|||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QDialogButtonBox>
|
#include <QDialogButtonBox>
|
||||||
#include <QProgressDialog>
|
#include <QProgressDialog>
|
||||||
|
#include <QCheckBox>
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
|
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem.hpp>
|
||||||
@@ -204,79 +205,71 @@ bool IntroDialog::pickDataDirectory()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Offer bootstrap download once (tracks via QSettings so it only asks once)
|
// Offer bootstrap download on each startup (unless user checked "don't ask again")
|
||||||
fs::path dataDirPath(dataDir.toStdString());
|
fs::path dataDirPath(dataDir.toStdString());
|
||||||
if (!settings.value("bootstrapOffered", false).toBool())
|
if (!settings.value("bootstrapDontAsk", false).toBool())
|
||||||
{
|
{
|
||||||
settings.setValue("bootstrapOffered", true);
|
QMessageBox msgBox;
|
||||||
|
msgBox.setWindowTitle("Triangles");
|
||||||
int ret = QMessageBox::question(0, "Triangles",
|
msgBox.setText(
|
||||||
"Would you like to download the latest blockchain snapshot?\n\n"
|
"Would you like to download the latest blockchain snapshot?\n\n"
|
||||||
"This will download the blockchain data from the Triangles network "
|
"This will download the blockchain data from the Triangles network "
|
||||||
"and replace any existing chain data in your data directory.\n\n"
|
"and replace any existing chain data in your data directory.\n\n"
|
||||||
"Click Yes to download, or No to sync from the network.",
|
"Click Yes to download, or No to sync from the network.");
|
||||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
|
msgBox.setIcon(QMessageBox::Question);
|
||||||
|
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
|
||||||
|
msgBox.setDefaultButton(QMessageBox::Yes);
|
||||||
|
QCheckBox *dontAskBox = new QCheckBox("Don't show this again");
|
||||||
|
msgBox.setCheckBox(dontAskBox);
|
||||||
|
|
||||||
|
int ret = msgBox.exec();
|
||||||
|
|
||||||
|
if (dontAskBox->isChecked())
|
||||||
|
settings.setValue("bootstrapDontAsk", true);
|
||||||
|
|
||||||
if (ret == QMessageBox::Yes)
|
if (ret == QMessageBox::Yes)
|
||||||
{
|
{
|
||||||
std::string host = Bootstrap::DEFAULT_HOST;
|
std::string host = Bootstrap::DEFAULT_HOST;
|
||||||
std::string strError;
|
std::string strError;
|
||||||
std::vector<std::string> files;
|
|
||||||
|
|
||||||
// Fetch file list (try domain first, then fallback to IP)
|
QProgressDialog progress("Downloading blockchain snapshot...", "Cancel",
|
||||||
if (!Bootstrap::FetchFileList(host, files, strError)) {
|
0, 100, 0);
|
||||||
host = Bootstrap::FALLBACK_HOST;
|
|
||||||
if (!Bootstrap::FetchFileList(host, files, strError)) {
|
|
||||||
QMessageBox::warning(0, "Triangles",
|
|
||||||
QString("Could not reach bootstrap server:\n%1\n\n"
|
|
||||||
"The wallet will sync from the network instead.")
|
|
||||||
.arg(QString::fromStdString(strError)));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show progress dialog
|
|
||||||
QProgressDialog progress("Downloading blockchain data...", "Cancel",
|
|
||||||
0, (int)files.size(), 0);
|
|
||||||
progress.setWindowTitle("Triangles - Bootstrap");
|
progress.setWindowTitle("Triangles - Bootstrap");
|
||||||
progress.setWindowModality(Qt::ApplicationModal);
|
progress.setWindowModality(Qt::ApplicationModal);
|
||||||
progress.setMinimumDuration(0);
|
progress.setMinimumDuration(0);
|
||||||
progress.setValue(0);
|
progress.setValue(0);
|
||||||
|
|
||||||
bool failed = false;
|
auto progressFn = [&progress](int64_t bytesDownloaded, int64_t totalBytes) {
|
||||||
for (int i = 0; i < (int)files.size(); i++) {
|
if (totalBytes > 0) {
|
||||||
if (progress.wasCanceled())
|
int pct = (int)((bytesDownloaded * 100) / totalBytes);
|
||||||
break;
|
progress.setValue(pct);
|
||||||
|
progress.setLabelText(
|
||||||
QString filename = QString::fromStdString(files[i]);
|
QString("Downloading blockchain snapshot... %1 MB / %2 MB")
|
||||||
progress.setLabelText(
|
.arg(bytesDownloaded / (1024*1024))
|
||||||
QString("Downloading %1 (%2 of %3)...")
|
.arg(totalBytes / (1024*1024)));
|
||||||
.arg(filename).arg(i + 1).arg((int)files.size()));
|
} else {
|
||||||
progress.setValue(i);
|
progress.setLabelText(
|
||||||
QApplication::processEvents();
|
QString("Downloading blockchain snapshot... %1 MB")
|
||||||
|
.arg(bytesDownloaded / (1024*1024)));
|
||||||
// Create subdirectories if needed (e.g. txleveldb/)
|
|
||||||
fs::path destPath = dataDirPath / files[i];
|
|
||||||
fs::create_directories(destPath.parent_path());
|
|
||||||
|
|
||||||
// Download this file
|
|
||||||
std::string urlPath = std::string(Bootstrap::BASE_PATH) + files[i];
|
|
||||||
if (!Bootstrap::DownloadFile(host, urlPath, destPath,
|
|
||||||
[](int64_t, int64_t) {
|
|
||||||
QApplication::processEvents();
|
|
||||||
}, strError))
|
|
||||||
{
|
|
||||||
QMessageBox::warning(0, "Triangles",
|
|
||||||
QString("Download failed for %1:\n%2\n\n"
|
|
||||||
"The wallet will sync remaining data from the network.")
|
|
||||||
.arg(filename).arg(QString::fromStdString(strError)));
|
|
||||||
failed = true;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
QApplication::processEvents();
|
||||||
|
};
|
||||||
|
|
||||||
|
bool success = Bootstrap::DownloadBootstrap(host, dataDirPath, progressFn, strError);
|
||||||
|
if (!success) {
|
||||||
|
host = Bootstrap::FALLBACK_HOST;
|
||||||
|
progress.setValue(0);
|
||||||
|
success = Bootstrap::DownloadBootstrap(host, dataDirPath, progressFn, strError);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!failed)
|
if (!success) {
|
||||||
progress.setValue((int)files.size());
|
QMessageBox::warning(0, "Triangles",
|
||||||
|
QString("Could not download blockchain snapshot:\n%1\n\n"
|
||||||
|
"The wallet will sync from the network instead.")
|
||||||
|
.arg(QString::fromStdString(strError)));
|
||||||
|
} else {
|
||||||
|
progress.setValue(100);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -126,6 +126,8 @@ OverviewPage::~OverviewPage()
|
|||||||
|
|
||||||
void OverviewPage::setBalance(qint64 balance, qint64 stake, qint64 unconfirmedBalance, qint64 immatureBalance)
|
void OverviewPage::setBalance(qint64 balance, qint64 stake, qint64 unconfirmedBalance, qint64 immatureBalance)
|
||||||
{
|
{
|
||||||
|
if (!model || !model->getOptionsModel())
|
||||||
|
return;
|
||||||
int unit = model->getOptionsModel()->getDisplayUnit();
|
int unit = model->getOptionsModel()->getDisplayUnit();
|
||||||
currentBalance = balance;
|
currentBalance = balance;
|
||||||
currentStake = stake;
|
currentStake = stake;
|
||||||
|
|||||||
+42
-24
@@ -799,19 +799,23 @@ void TrianglesGUI::setNumConnections(int count)
|
|||||||
|
|
||||||
void TrianglesGUI::setNumBlocks(int count, int nTotalBlocks)
|
void TrianglesGUI::setNumBlocks(int count, int nTotalBlocks)
|
||||||
{
|
{
|
||||||
// don't show / hide progress bar and its label if we have no connection to the network
|
if (!clientModel)
|
||||||
if (!clientModel || clientModel->getNumConnections() == 0)
|
return;
|
||||||
|
|
||||||
|
int nConnections = clientModel->getNumConnections();
|
||||||
|
|
||||||
|
// Hide progress bar when disconnected, but don't return early -
|
||||||
|
// we still need to update sync state and the out-of-sync warning
|
||||||
|
if (nConnections == 0)
|
||||||
{
|
{
|
||||||
progressBarLabel->setVisible(false);
|
progressBarLabel->setVisible(false);
|
||||||
progressBar->setVisible(false);
|
progressBar->setVisible(false);
|
||||||
ui->label_blocks->setVisible(false);
|
ui->label_blocks->setVisible(false);
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString tooltip;
|
QString tooltip;
|
||||||
|
|
||||||
if(count < nTotalBlocks)
|
if(nConnections > 0 && count < nTotalBlocks)
|
||||||
{
|
{
|
||||||
// Calculate blocks/sec - only update rate when new blocks arrive
|
// Calculate blocks/sec - only update rate when new blocks arrive
|
||||||
static int lastCount = 0;
|
static int lastCount = 0;
|
||||||
@@ -899,20 +903,19 @@ void TrianglesGUI::setNumBlocks(int count, int nTotalBlocks)
|
|||||||
text = tr("%n day(s) ago","",secs/(60*60*24));
|
text = tr("%n day(s) ago","",secs/(60*60*24));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set icon state: spinning if catching up, tick otherwise
|
// Set icon state: spinning if catching up, tick otherwise.
|
||||||
if(secs < 90*60 && count >= nTotalBlocks)
|
// Use a generous threshold (6 hours) for PoS chains where block intervals
|
||||||
|
// can be long during difficulty adjustment with few stakers.
|
||||||
|
if(secs < 6*60*60 && count >= nTotalBlocks)
|
||||||
{
|
{
|
||||||
tooltip = tr("Up to date") + QString(".<br>") + tooltip;
|
tooltip = tr("Up to date") + QString(".<br>") + tooltip;
|
||||||
labelBlocksIcon->setPixmap(QIcon(":/icons/synced").pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE));
|
labelBlocksIcon->setPixmap(QIcon(":/icons/synced").pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE));
|
||||||
|
|
||||||
overviewPage->showOutOfSyncWarning(false);
|
overviewPage->showOutOfSyncWarning(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
tooltip = tr("Catching up...") + QString("<br>") + tooltip;
|
tooltip = tr("Catching up...") + QString("<br>") + tooltip;
|
||||||
//syncIconMovie doesn't work for some reason - using fallback png
|
|
||||||
//labelBlocksIcon->setMovie(syncIconMovie);
|
|
||||||
//syncIconMovie->start();
|
|
||||||
labelBlocksIcon->setPixmap(QIcon(":/icons/notsynced").pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE));
|
labelBlocksIcon->setPixmap(QIcon(":/icons/notsynced").pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE));
|
||||||
|
|
||||||
overviewPage->showOutOfSyncWarning(true);
|
overviewPage->showOutOfSyncWarning(true);
|
||||||
@@ -1590,32 +1593,47 @@ void TrianglesGUI::toggleHidden()
|
|||||||
|
|
||||||
void TrianglesGUI::updateStakingIcon()
|
void TrianglesGUI::updateStakingIcon()
|
||||||
{
|
{
|
||||||
uint64_t nMinWeight = 0, nMaxWeight = 0, nWeight = 0;
|
// Read cached staking info computed by the staking thread.
|
||||||
|
// No locks needed - these are volatile values written by the miner thread
|
||||||
|
// and are display-only. This keeps the UI thread completely non-blocking.
|
||||||
|
|
||||||
|
uint64_t nWeight = 0;
|
||||||
|
bool fWalletLocked = false;
|
||||||
|
bool fHasPeers = false;
|
||||||
|
|
||||||
if (pwalletMain)
|
if (pwalletMain)
|
||||||
pwalletMain->GetStakeWeight(*pwalletMain, nMinWeight, nMaxWeight, nWeight);
|
{
|
||||||
|
TRY_LOCK(pwalletMain->cs_wallet, lockWallet);
|
||||||
|
if (lockWallet)
|
||||||
|
fWalletLocked = pwalletMain->IsLocked();
|
||||||
|
else
|
||||||
|
return; // Skip this cycle, try again in 30 seconds
|
||||||
|
|
||||||
|
// Use cached weight from the staking thread instead of computing on UI thread.
|
||||||
|
// The staking thread updates this every ~500ms-1s loop iteration.
|
||||||
|
nWeight = pwalletMain->nCachedStakeWeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
TRY_LOCK(cs_vNodes, lockNodes);
|
||||||
|
if (lockNodes)
|
||||||
|
fHasPeers = !vNodes.empty();
|
||||||
|
}
|
||||||
|
|
||||||
if (nLastCoinStakeSearchInterval && nWeight)
|
if (nLastCoinStakeSearchInterval && nWeight)
|
||||||
{
|
{
|
||||||
uint64_t nNetworkWeight = GetPoSKernelPS();
|
uint64_t nNetworkWeight = GetPoSKernelPS();
|
||||||
unsigned nEstimateTime = nTargetSpacing * nNetworkWeight / nWeight;
|
unsigned nEstimateTime = nWeight > 0 ? nTargetSpacing * nNetworkWeight / nWeight : 0;
|
||||||
|
|
||||||
QString text;
|
QString text;
|
||||||
if (nEstimateTime < 60)
|
if (nEstimateTime < 60)
|
||||||
{
|
|
||||||
text = tr("%n second(s)", "", nEstimateTime);
|
text = tr("%n second(s)", "", nEstimateTime);
|
||||||
}
|
|
||||||
else if (nEstimateTime < 60*60)
|
else if (nEstimateTime < 60*60)
|
||||||
{
|
|
||||||
text = tr("%n minute(s)", "", nEstimateTime/60);
|
text = tr("%n minute(s)", "", nEstimateTime/60);
|
||||||
}
|
|
||||||
else if (nEstimateTime < 24*60*60)
|
else if (nEstimateTime < 24*60*60)
|
||||||
{
|
|
||||||
text = tr("%n hour(s)", "", nEstimateTime/(60*60));
|
text = tr("%n hour(s)", "", nEstimateTime/(60*60));
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
|
||||||
text = tr("%n day(s)", "", nEstimateTime/(60*60*24));
|
text = tr("%n day(s)", "", nEstimateTime/(60*60*24));
|
||||||
}
|
|
||||||
|
|
||||||
labelStakingIcon->setPixmap(QIcon(":/icons/staking_on").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE));
|
labelStakingIcon->setPixmap(QIcon(":/icons/staking_on").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE));
|
||||||
labelStakingIcon->setToolTip(tr("Staking.<br>Your weight is %1<br>Network weight is %2<br>Expected time to earn reward is %3").arg(nWeight).arg(nNetworkWeight).arg(text));
|
labelStakingIcon->setToolTip(tr("Staking.<br>Your weight is %1<br>Network weight is %2<br>Expected time to earn reward is %3").arg(nWeight).arg(nNetworkWeight).arg(text));
|
||||||
@@ -1623,9 +1641,9 @@ void TrianglesGUI::updateStakingIcon()
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
labelStakingIcon->setPixmap(QIcon(":/icons/staking_off").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE));
|
labelStakingIcon->setPixmap(QIcon(":/icons/staking_off").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE));
|
||||||
if (pwalletMain && pwalletMain->IsLocked())
|
if (fWalletLocked)
|
||||||
labelStakingIcon->setToolTip(tr("Not staking because wallet is locked"));
|
labelStakingIcon->setToolTip(tr("Not staking because wallet is locked"));
|
||||||
else if (vNodes.empty())
|
else if (!fHasPeers)
|
||||||
labelStakingIcon->setToolTip(tr("Not staking because wallet is offline"));
|
labelStakingIcon->setToolTip(tr("Not staking because wallet is offline"));
|
||||||
else if (IsInitialBlockDownload())
|
else if (IsInitialBlockDownload())
|
||||||
labelStakingIcon->setToolTip(tr("Not staking because wallet is syncing"));
|
labelStakingIcon->setToolTip(tr("Not staking because wallet is syncing"));
|
||||||
|
|||||||
@@ -92,10 +92,10 @@ void WalletModel::pollBalanceChanged()
|
|||||||
|
|
||||||
void WalletModel::checkBalanceChanged()
|
void WalletModel::checkBalanceChanged()
|
||||||
{
|
{
|
||||||
qint64 newBalance = getBalance();
|
// Get all balances in a single lock acquisition + single pass
|
||||||
qint64 newStake = getStake();
|
// instead of 4 separate lock+iterate cycles
|
||||||
qint64 newUnconfirmedBalance = getUnconfirmedBalance();
|
qint64 newBalance = 0, newStake = 0, newUnconfirmedBalance = 0, newImmatureBalance = 0;
|
||||||
qint64 newImmatureBalance = getImmatureBalance();
|
wallet->GetAllBalances(newBalance, newStake, newUnconfirmedBalance, newImmatureBalance);
|
||||||
|
|
||||||
if(cachedBalance != newBalance || cachedStake != newStake || cachedUnconfirmedBalance != newUnconfirmedBalance || cachedImmatureBalance != newImmatureBalance)
|
if(cachedBalance != newBalance || cachedStake != newStake || cachedUnconfirmedBalance != newUnconfirmedBalance || cachedImmatureBalance != newImmatureBalance)
|
||||||
{
|
{
|
||||||
|
|||||||
+1
-1
@@ -53,7 +53,7 @@ static const int MEMPOOL_GD_VERSION = 60002;
|
|||||||
|
|
||||||
#define DISPLAY_VERSION_MAJOR 5
|
#define DISPLAY_VERSION_MAJOR 5
|
||||||
#define DISPLAY_VERSION_MINOR 3
|
#define DISPLAY_VERSION_MINOR 3
|
||||||
#define DISPLAY_VERSION_REVISION 3
|
#define DISPLAY_VERSION_REVISION 4
|
||||||
#define DISPLAY_VERSION_BUILD 0
|
#define DISPLAY_VERSION_BUILD 0
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1476,6 +1476,31 @@ int64_t CWallet::GetNewMint() const
|
|||||||
return nTotal;
|
return nTotal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CWallet::GetAllBalances(int64_t& nBalance, int64_t& nStake, int64_t& nUnconfirmed, int64_t& nImmature) const
|
||||||
|
{
|
||||||
|
nBalance = 0;
|
||||||
|
nStake = 0;
|
||||||
|
nUnconfirmed = 0;
|
||||||
|
nImmature = 0;
|
||||||
|
LOCK(cs_wallet);
|
||||||
|
for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
|
||||||
|
{
|
||||||
|
const CWalletTx& pcoin = (*it).second;
|
||||||
|
|
||||||
|
if (pcoin.IsCoinStake() && pcoin.GetBlocksToMaturity() > 0 && pcoin.GetDepthInMainChain() > 0)
|
||||||
|
nStake += CWallet::GetCredit(pcoin);
|
||||||
|
|
||||||
|
if (pcoin.IsCoinBase() && pcoin.GetBlocksToMaturity() > 0 && pcoin.IsInMainChain())
|
||||||
|
nImmature += GetCredit(pcoin);
|
||||||
|
|
||||||
|
if (pcoin.IsTrusted())
|
||||||
|
nBalance += pcoin.GetAvailableCredit();
|
||||||
|
|
||||||
|
if (!pcoin.IsFinal() || !pcoin.IsTrusted())
|
||||||
|
nUnconfirmed += pcoin.GetAvailableCredit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool CWallet::SelectCoinsMinConf(int64_t nTargetValue, unsigned int nSpendTime, int nConfMine, int nConfTheirs, vector<COutput> vCoins, set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64_t& nValueRet) const
|
bool CWallet::SelectCoinsMinConf(int64_t nTargetValue, unsigned int nSpendTime, int nConfMine, int nConfTheirs, vector<COutput> vCoins, set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64_t& nValueRet) const
|
||||||
{
|
{
|
||||||
setCoinsRet.clear();
|
setCoinsRet.clear();
|
||||||
|
|||||||
@@ -107,6 +107,8 @@ public:
|
|||||||
nMasterKeyMaxID = 0;
|
nMasterKeyMaxID = 0;
|
||||||
pwalletdbEncryption = NULL;
|
pwalletdbEncryption = NULL;
|
||||||
nOrderPosNext = 0;
|
nOrderPosNext = 0;
|
||||||
|
nCachedStakeWeight = 0;
|
||||||
|
nCachedStakeWeightTime = 0;
|
||||||
}
|
}
|
||||||
CWallet(std::string strWalletFileIn)
|
CWallet(std::string strWalletFileIn)
|
||||||
{
|
{
|
||||||
@@ -117,6 +119,8 @@ public:
|
|||||||
nMasterKeyMaxID = 0;
|
nMasterKeyMaxID = 0;
|
||||||
pwalletdbEncryption = NULL;
|
pwalletdbEncryption = NULL;
|
||||||
nOrderPosNext = 0;
|
nOrderPosNext = 0;
|
||||||
|
nCachedStakeWeight = 0;
|
||||||
|
nCachedStakeWeightTime = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<uint256, CWalletTx> mapWallet;
|
std::map<uint256, CWalletTx> mapWallet;
|
||||||
@@ -190,6 +194,8 @@ public:
|
|||||||
int64_t GetImmatureBalance() const;
|
int64_t GetImmatureBalance() const;
|
||||||
int64_t GetStake() const;
|
int64_t GetStake() const;
|
||||||
int64_t GetNewMint() const;
|
int64_t GetNewMint() const;
|
||||||
|
// Get all balances in a single lock acquisition + single pass (avoids 4x lock + 4x iteration)
|
||||||
|
void GetAllBalances(int64_t& nBalance, int64_t& nStake, int64_t& nUnconfirmed, int64_t& nImmature) const;
|
||||||
bool CreateTransaction(const std::vector<std::pair<CScript, int64_t> >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, const CCoinControl *coinControl=NULL);
|
bool CreateTransaction(const std::vector<std::pair<CScript, int64_t> >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, const CCoinControl *coinControl=NULL);
|
||||||
bool CreateTransaction(CScript scriptPubKey, int64_t nValue, std::string& sNarr, CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, const CCoinControl *coinControl=NULL);
|
bool CreateTransaction(CScript scriptPubKey, int64_t nValue, std::string& sNarr, CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, const CCoinControl *coinControl=NULL);
|
||||||
bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey);
|
bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey);
|
||||||
@@ -197,6 +203,11 @@ public:
|
|||||||
bool GetStakeWeight(const CKeyStore& keystore, uint64_t& nMinWeight, uint64_t& nMaxWeight, uint64_t& nWeight);
|
bool GetStakeWeight(const CKeyStore& keystore, uint64_t& nMinWeight, uint64_t& nMaxWeight, uint64_t& nWeight);
|
||||||
bool CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int64_t nSearchInterval, int64_t nFees, CTransaction& txNew, CKey& key);
|
bool CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int64_t nSearchInterval, int64_t nFees, CTransaction& txNew, CKey& key);
|
||||||
|
|
||||||
|
// Cached staking info - updated by the staking thread, read by the UI thread.
|
||||||
|
// Access is safe without locks: written atomically by the miner, read by UI for display only.
|
||||||
|
volatile uint64_t nCachedStakeWeight;
|
||||||
|
volatile int64_t nCachedStakeWeightTime; // GetTime() when last updated
|
||||||
|
|
||||||
std::string SendMoney(CScript scriptPubKey, int64_t nValue, std::string& sNarr, CWalletTx& wtxNew, bool fAskFee=false);
|
std::string SendMoney(CScript scriptPubKey, int64_t nValue, std::string& sNarr, CWalletTx& wtxNew, bool fAskFee=false);
|
||||||
std::string SendMoneyToDestination(const CTxDestination& address, int64_t nValue, std::string& sNarr, CWalletTx& wtxNew, bool fAskFee=false);
|
std::string SendMoneyToDestination(const CTxDestination& address, int64_t nValue, std::string& sNarr, CWalletTx& wtxNew, bool fAskFee=false);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user