From 47cf8abbda0ca6a640bf9dd22b0e67fc449b874f Mon Sep 17 00:00:00 2001 From: Sami Ahmed Date: Thu, 19 Mar 2026 21:35:12 -0700 Subject: [PATCH] Add daemon bootstrap, repeating bootstrap prompt, faster IBD pipeline - Daemon: add -bootstrap flag to download chain files from server on startup - Qt: bootstrap prompt shows every launch with "Don't show this again" checkbox - IBD: reduce pipeline refill interval from 1000 to 100 blocks for faster sync - Add bootstrap.o to daemon makefiles (mingw + unix) Co-Authored-By: Claude Opus 4.6 --- src/init.cpp | 49 ++++++++++++++++++++++++++++++++++++++++++ src/main.cpp | 2 +- src/makefile.mingw | 8 +++++++ src/makefile.unix | 8 +++++++ src/qt/introdialog.cpp | 24 +++++++++++++++------ 5 files changed, 83 insertions(+), 8 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index cde43bd..5440e0a 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -12,6 +12,7 @@ #include "checkpoints.h" #include "smessage.h" #include "openssl_compat.h" +#include "bootstrap.h" #include "tor/tor_embedded.h" #include "tor/onion_v3.h" #include "tor/tor_process.h" @@ -797,6 +798,54 @@ bool AppInit2() for (string strDest : mapMultiArgs["-seednode"]) 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; + std::vector files; + + uiInterface.InitMessage(_("Fetching bootstrap file list...")); + printf("Bootstrap: contacting %s...\n", host.c_str()); + + bool fGotList = Bootstrap::FetchFileList(host, files, strError); + if (!fGotList) { + host = Bootstrap::FALLBACK_HOST; + printf("Bootstrap: primary host failed, trying fallback %s...\n", host.c_str()); + fGotList = Bootstrap::FetchFileList(host, files, strError); + } + + if (!fGotList) { + printf("Bootstrap: could not reach server: %s\n", strError.c_str()); + printf("Bootstrap: skipping, will sync from network.\n"); + } else { + printf("Bootstrap: downloading %d files...\n", (int)files.size()); + for (int i = 0; i < (int)files.size(); i++) { + if (fRequestShutdown) break; + + printf("Bootstrap: downloading %s (%d of %d)...\n", + files[i].c_str(), i + 1, (int)files.size()); + uiInterface.InitMessage(strprintf(_("Downloading %s (%d of %d)..."), + files[i].c_str(), i + 1, (int)files.size())); + + fs::path destPath = dataPath / files[i]; + fs::create_directories(destPath.parent_path()); + + std::string urlPath = std::string(Bootstrap::BASE_PATH) + files[i]; + if (!Bootstrap::DownloadFile(host, urlPath, destPath, nullptr, strError)) { + printf("Bootstrap: download failed for %s: %s\n", + files[i].c_str(), strError.c_str()); + printf("Bootstrap: will sync remaining data from network.\n"); + break; + } + } + printf("Bootstrap: done.\n"); + } + } +#endif + // ********************************************************* Step 7: load blockchain if (!bitdb.Open(GetDataDir())) diff --git a/src/main.cpp b/src/main.cpp index 2e3b127..86921e1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3775,7 +3775,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) if (IsInitialBlockDownload()) { static int nBlocksSinceRequest = 0; - if (++nBlocksSinceRequest >= 1000) + if (++nBlocksSinceRequest >= 100) { nBlocksSinceRequest = 0; pfrom->pindexLastGetBlocksBegin = NULL; diff --git a/src/makefile.mingw b/src/makefile.mingw index 52b7cb4..d0f7a2e 100644 --- a/src/makefile.mingw +++ b/src/makefile.mingw @@ -111,6 +111,7 @@ OBJS= \ obj/miner.o \ obj/main.o \ obj/net.o \ + obj/bootstrap.o \ obj/net_bootstrap.o \ obj/protocol.o \ obj/trianglesrpc.o \ @@ -232,6 +233,13 @@ obj/tor_embedded.o: tor/tor_embedded.cpp -e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \ 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 $(CXX) -c $(xCXXFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $< @cp $(@:%.o=%.d) $(@:%.o=%.P); \ diff --git a/src/makefile.unix b/src/makefile.unix index e443c44..60617d6 100644 --- a/src/makefile.unix +++ b/src/makefile.unix @@ -146,6 +146,7 @@ OBJS= \ obj/miner.o \ obj/main.o \ obj/net.o \ + obj/bootstrap.o \ obj/net_bootstrap.o \ obj/protocol.o \ obj/trianglesrpc.o \ @@ -279,6 +280,13 @@ obj/tor_embedded.o: tor/tor_embedded.cpp -e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \ 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 $(CXX) -c $(xCXXFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $< @cp $(@:%.o=%.d) $(@:%.o=%.P); \ diff --git a/src/qt/introdialog.cpp b/src/qt/introdialog.cpp index 30001c5..5b75278 100644 --- a/src/qt/introdialog.cpp +++ b/src/qt/introdialog.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -204,18 +205,27 @@ bool IntroDialog::pickDataDirectory() 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()); - if (!settings.value("bootstrapOffered", false).toBool()) + if (!settings.value("bootstrapDontAsk", false).toBool()) { - settings.setValue("bootstrapOffered", true); - - int ret = QMessageBox::question(0, "Triangles", + QMessageBox msgBox; + msgBox.setWindowTitle("Triangles"); + msgBox.setText( "Would you like to download the latest blockchain snapshot?\n\n" "This will download the blockchain data from the Triangles network " "and replace any existing chain data in your data directory.\n\n" - "Click Yes to download, or No to sync from the network.", - QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + "Click Yes to download, or No to sync from the network."); + 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) {