Merge hd-wallet into master: I2P process integration + HD wallet + reconcile with origin/master v5.9.15
This commit is contained in:
@@ -49,6 +49,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
|
||||
- name: Install dependencies + clang-tidy
|
||||
run: |
|
||||
|
||||
+4
-2
@@ -33,9 +33,10 @@ namespace Checkpoints
|
||||
{ 10881, uint256("0x4b6554c45e1e6764a6f3c309c47baf53c9edd81f624e52b072518cd15da237e6")},
|
||||
{ 17650, uint256("0x224940e1f986a202209b8e762728d1452ab45870c308abf84905674acf326a47")},
|
||||
// Recent finality pin (PoS era). Closes the long unchecked span from
|
||||
// 17650 to the live tip so stale-bootstrap / low-trust forks below this
|
||||
// height are rejected outright. Hash taken from the canonical chain.
|
||||
// 17650 to the live tip so stale-bootstrap / low-trust forks below
|
||||
// this height are rejected outright. Hash from the canonical chain.
|
||||
{ 2205000, uint256("0x6bdd3c5e5a32e1dd9a70e705f1a28d1dd84929f89579bd2696d41bc87f39446f")},
|
||||
{ 2206004, uint256("0xb34e8e6a7bb7f52167d81aaad4d26f87a876898fdd0fce860916fc1aaf9a2a46")},
|
||||
};
|
||||
|
||||
// Published UTXO snapshot file SHA256, keyed by snapshot height.
|
||||
@@ -47,6 +48,7 @@ namespace Checkpoints
|
||||
// here. The corresponding (height, blockhash) must already exist in
|
||||
// mapCheckpoints / mapCheckpointsTestnet.
|
||||
static std::map<int, uint256> mapSnapshotHashes = {
|
||||
{ 2206004, uint256("0x1419282dae817315ee1b955543f6248233fe5800f5e8488734a0ece5bd6781ea")},
|
||||
};
|
||||
|
||||
static std::map<int, uint256> mapSnapshotHashesTestnet = {
|
||||
|
||||
+1
-1
@@ -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 9
|
||||
#define CLIENT_VERSION_REVISION 12
|
||||
#define CLIENT_VERSION_REVISION 15
|
||||
#define CLIENT_VERSION_BUILD 0
|
||||
|
||||
// Converts the parameter X to a string after macro replacement on X has been performed.
|
||||
|
||||
@@ -1089,6 +1089,37 @@ bool AppInit2()
|
||||
if (!LoadBlockIndex())
|
||||
return InitError(_("Error loading blkindex.dat"));
|
||||
|
||||
// triangles fix (pitfall #61): initialize pindexFinalized from the
|
||||
// hardcoded checkpoint on startup, BEFORE the daemon opens any peer
|
||||
// connections or processes any block messages.
|
||||
//
|
||||
// Without this, pindexFinalized stays NULL on a fresh restart even when
|
||||
// we have 2.2M blocks on disk, because the auto-checkpoint code in
|
||||
// ActivateBestChain() at main.cpp:2459 only sets it when
|
||||
// !IsInitialBlockDownload(). If the chain tip is more than 24h stale
|
||||
// (which happens on every restart with a synced chain), IsInitialBlockDownload()
|
||||
// returns true and pindexFinalized never gets set.
|
||||
//
|
||||
// The downstream reorg guard at main.cpp:2198 short-circuits when
|
||||
// pindexFinalized is NULL, which allowed a 3,755-block minority fork
|
||||
// to overwrite a healthy 2,206,004-block chain on 2026-06-16. Loading
|
||||
// the hardcoded checkpoint from checkpoints.cpp (block 2,205,000) on
|
||||
// startup means the reorg guard is always active whenever the
|
||||
// checkpointed block is in our local mapBlockIndex.
|
||||
{
|
||||
CBlockIndex* pCheckpoint = Checkpoints::GetLastCheckpoint(mapBlockIndex);
|
||||
if (pCheckpoint && pCheckpoint != pindexFinalized)
|
||||
{
|
||||
pindexFinalized = pCheckpoint;
|
||||
printf("STARTUP-CHECKPOINT: pindexFinalized set to block %d (%s) from hardcoded checkpoint\n",
|
||||
pindexFinalized->nHeight, pindexFinalized->GetBlockHash().ToString().substr(0,20).c_str());
|
||||
}
|
||||
else if (!pCheckpoint)
|
||||
{
|
||||
printf("STARTUP-CHECKPOINT: WARNING — hardcoded checkpoint not in local block index, pindexFinalized remains NULL\n");
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
||||
+53
-1
@@ -4500,8 +4500,60 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
|
||||
{
|
||||
// Find the last block the caller has in the main chain
|
||||
pindex = locator.GetBlockIndex();
|
||||
|
||||
// triangles fix: handle broken pnext chain.
|
||||
// GetBlockIndex() returns pindexGenesisBlock when no locator
|
||||
// hash matches our main chain (peer is on a different fork or
|
||||
// a stale local state). pindexGenesisBlock->pnext is always
|
||||
// null, which would cause the for-loop below to send ZERO
|
||||
// headers, leaving the peer stuck (logged as "getheaders -1").
|
||||
//
|
||||
// Mirror the getblocks handler: if the locator matches nothing
|
||||
// on our main chain, serve our headers from genesis so the peer
|
||||
// can discover the canonical chain. Then fall back to a tip-
|
||||
// backwards walk if pnext is null for any other reason (this
|
||||
// happens when LoadBlockIndex() didn't fully heal pnext links,
|
||||
// or the chain was bootstrapped from a snapshot).
|
||||
//
|
||||
// pitfall #61 guard: if pindexFinalized is set (from the startup
|
||||
// hardcoded-checkpoint init in init.cpp), serve from there instead
|
||||
// of genesis. This prevents a fork peer from feeding us their
|
||||
// short chain back via getheaders — the peer only learns our
|
||||
// canonical chain from the finalized point forward, and their
|
||||
// conflicting fork gets rejected at the reorg check in
|
||||
// Reorganize() because the fork point is below pindexFinalized.
|
||||
if (!locator.IsNull() && pindex == pindexGenesisBlock &&
|
||||
pindexGenesisBlock && locator.GetTipHash() != pindexGenesisBlock->GetBlockHash())
|
||||
{
|
||||
if (pindexFinalized && pindexFinalized->pnext)
|
||||
{
|
||||
printf("getheaders: fork detected from peer %s, serving headers from finalized block %d (not genesis) — pitfall #61 guard\n",
|
||||
pfrom->addr.ToString().c_str(), pindexFinalized->nHeight);
|
||||
pindex = pindexFinalized;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("WARNING: peer getheaders locator has no common blocks — serving headers from genesis (peer may be on a fork)\n");
|
||||
pindex = pindexGenesisBlock;
|
||||
}
|
||||
}
|
||||
|
||||
if (pindex)
|
||||
pindex = pindex->pnext;
|
||||
{
|
||||
if (pindex->pnext)
|
||||
{
|
||||
pindex = pindex->pnext;
|
||||
}
|
||||
else
|
||||
{
|
||||
// pnext is null — fall back to walking from pindexBest
|
||||
// backwards to find the block immediately after pindex
|
||||
CBlockIndex* pWalk = pindexBest;
|
||||
while (pWalk && pWalk->pprev != pindex)
|
||||
pWalk = pWalk->pprev;
|
||||
pindex = pWalk; // null if pindex is already the tip
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vector<CBlock> vHeaders;
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
#include <thread>
|
||||
#include <fstream>
|
||||
#include <cstring>
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
@@ -122,11 +124,59 @@ bool CTorEmbedded::Start(int socks, int hsPort, bool enableHiddenService)
|
||||
// Prepare Tor data directory under the wallet's data dir
|
||||
torDataDir = (::GetDataDir() / "tor_data").string();
|
||||
fs::create_directories(torDataDir);
|
||||
// CRITICAL: Tor refuses to use a DataDirectory readable by other users.
|
||||
// Without 0700, tor_run_main() returns -1 and the embedded Tor never starts.
|
||||
fs::permissions(torDataDir, fs::perms::owner_all, fs::perm_options::replace);
|
||||
|
||||
// triangles fix: auto-repair `state`-as-file corruption (pitfall #19).
|
||||
// Tor's atomic state-write pattern is: write `state.tmp` → rename to `state`.
|
||||
// If the daemon is killed or the process crashes mid-write, the rename can
|
||||
// fail and `state` may be left as a regular file (or a partial file). On
|
||||
// next start, Tor sees "State file ... is not a file? Failing." and dies
|
||||
// with code -1 ("Reading config failed"). This was hit on DNS3 on
|
||||
// 2026-05-24 and on the TRI-LAPTOP GUI wallet on 2026-06-15. The user-facing
|
||||
// symptom is "Tor failed to start. Triangles requires Tor to operate." and
|
||||
// the only fix was manually renaming the corrupt file. Detect this state
|
||||
// here and auto-rename so the daemon is self-healing.
|
||||
{
|
||||
fs::path statePath = fs::path(torDataDir) / "state";
|
||||
std::error_code ec;
|
||||
if (fs::exists(statePath, ec) && !fs::is_directory(statePath, ec)) {
|
||||
// state is a file (or symlink to one) — quarantine it
|
||||
auto now = std::chrono::system_clock::now();
|
||||
auto t = std::chrono::system_clock::to_time_t(now);
|
||||
char ts[32];
|
||||
std::strftime(ts, sizeof(ts), "%Y%m%d-%H%M%S", std::gmtime(&t));
|
||||
fs::path quarantine = fs::path(torDataDir) /
|
||||
(std::string("state.corrupt-") + ts);
|
||||
try {
|
||||
fs::rename(statePath, quarantine, ec);
|
||||
if (ec) {
|
||||
// rename can fail on Windows if dest exists; remove then rename
|
||||
fs::remove(quarantine, ec);
|
||||
fs::rename(statePath, quarantine, ec);
|
||||
}
|
||||
printf("Tor state was a file (corrupt) — quarantined to %s for inspection. Tor will recreate state/ as a directory.\n",
|
||||
quarantine.filename().string().c_str());
|
||||
} catch (const std::exception& e) {
|
||||
printf("WARNING: could not quarantine corrupt Tor state file %s: %s\n",
|
||||
statePath.string().c_str(), e.what());
|
||||
// Last resort: try to remove it so Tor can proceed
|
||||
fs::remove(statePath, ec);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string hsDir;
|
||||
if (hiddenServiceEnabled) {
|
||||
hsDir = (fs::path(torDataDir) / "hidden_service").string();
|
||||
fs::create_directories(hsDir);
|
||||
// CRITICAL: Tor rejects hidden service directories that are not 0700
|
||||
// ("Permissions on directory ... are too permissive") and aborts config
|
||||
// validation with code -1. This was the root cause of "Embedded Tor
|
||||
// exited with code -1" — fs::create_directories honors umask (0022 on
|
||||
// most Linux systems), leaving the dir at 0755. Force 0700 after creation.
|
||||
fs::permissions(hsDir, fs::perms::owner_all, fs::perm_options::replace);
|
||||
}
|
||||
|
||||
// Build the argv for tor_run_main
|
||||
|
||||
Reference in New Issue
Block a user