Commit Graph

504 Commits

Author SHA1 Message Date
Krystie (TRI packaging) 7213dddcf1 ci: add job-level guards to distribute.yml
Observed the workflow firing on regular push-to-master events, not just
tag pushes. GitHub is sometimes over-eager about workflow re-runs on
commits that touch the workflow file. Add an explicit job-level guard

  if: github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/v')

to all four jobs so the distribute jobs only run on tag pushes or
manual workflow_dispatch events.
2026-06-21 01:43:44 -07:00
Krystie (TRI packaging) 8b147317d5 ci: auto-distribute releases to Homebrew tap on tag
New 'homebrew' job in distribute.yml:
- Waits for the macOS .dmg to be available on the GitHub release
- Computes the new SHA256
- Clones SamiAhmed7777/homebrew-triangles
- Updates version + sha256 in both Formula/triangles.rb and
  Casks/cryptographic-triangles.rb
- Commits and pushes to main
- Skips gracefully with a warning if HOMEBREW_GITHUB_TOKEN is not set

Required GitHub secret: HOMEBREW_GITHUB_TOKEN (added)
2026-06-21 01:39:45 -07:00
Krystie (TRI packaging) 2abb72ed0e ci: fix distribute.yml to handle missing secrets per step
GitHub Actions doesn't allow 'secrets' context in 'if:' conditionals,
only in 'env:'. Reworked the workflow to:

- Capture DOCKERHUB_TOKEN and AUR_SSH_KEY into env vars at job level
- Each step that needs a secret checks env.* and exits 0 with a
  ::warning:: annotation if not set
- Skipped steps display a final summary in the job log

Same behavior, just no parser errors.
2026-06-21 01:33:23 -07:00
Krystie (TRI packaging) 06fea513d8 ci: auto-distribute releases to Docker Hub + AUR on tag
New workflow .github/workflows/distribute.yml:
- Triggers on v* tag push (and workflow_dispatch for manual runs)
- Docker job: builds + pushes to samiahmed7777/trianglesd with both
  :VERSION and :latest tags, plus a post-push smoke test
- AUR job: runs in archlinux container, downloads the release .debs,
  updates PKGBUILD with new version + SHA256s, regenerates .SRCINFO
  via makepkg, commits and pushes to AUR via SSH
- Both jobs skip gracefully (with a clear warning) if their respective
  GitHub secrets aren't set, so the workflow can be merged and tested
  before secrets are configured
- Waits up to 10 minutes for the build-all release artifacts to be
  available (build-all and distribute run in parallel on the same tag)

Required GitHub secrets:
  DOCKERHUB_TOKEN — Docker Hub access token (have in vault)
  AUR_SSH_KEY     — Private key of the AUR packager (~/.ssh/aur_key)
2026-06-21 01:30:32 -07:00
Krystie (TRI packaging) 3ddf6536e5 packaging: bump Docker + AUR to v5.9.20
Docker:
- Dockerfile now extracts from cryptographic-triangles-daemon_5.9.20_amd64.deb
  (release no longer ships raw linux-x64 binaries)
- Multi-stage build with .deb extraction
- Includes triangles-cli alongside trianglesd
- LD_LIBRARY_PATH wrapper for the bundled lib/ dir

AUR:
- Bump triangles-qt-bin to 5.9.20
- Switch from raw linux-x64 binary download (no longer published) to
  extracting the official .deb packages
- Bundle version-pinned libs in /opt/triangles/lib
- Add triangles-cli to provides
2026-06-21 01:19:09 -07:00
Sami Ahmed adbbad3121 Merge sync-freeze-fix: resolves IBD freeze at 15k + PoS header rejection at 1026
From-zero sync test confirmed: chain advances past 15k freeze zone
to 17k+ with no stall. Build clean (149/149 Ninja targets).
132/132 unit tests pass.
2026-06-20 20:56:57 -07:00
Sami Ahmed 7ba8d8b8c9 Fix sync-freeze: backpressure, prune protection, eviction direction, bridge-repair + PoS header guard
Sync-freeze patch (original):
- Backpressure ceiling HEADER_FRONT_MAX_AHEAD=8000
- PruneHeaders protects live sync window (nProtectFloor)
- Hard-cap eviction from highest-height first
- Bridge-repair getheaders from connected tip via PathReachesChain

Additional fix:
- Skip PoW check on PoS headers (nonce=0) in AddHeaderNode
  Block 1026 is PoS but within the 0-9000 PoW range — old code
  rejected valid PoS headers and severed the chain at height 1025

Verified: from-zero no-snapshot sync reached block 17k+ past the
old 15k freeze zone. 132/132 unit tests pass.
2026-06-20 20:56:47 -07:00
Sami Ahmed 6b49dd9e62 Remove legacy bootstrap.tar.gz fallback path (v2 snapshot is now the only sync)
FastImport removal in commit bdb7253 made the v2 UTXO snapshot the
canonical sync start. The legacy DownloadBootstrap() function still
attempted to fetch /triangles-bootstrap.tar.gz first, then fell back to
filelist.txt — which still contained tri-bootstrap.tar.gz. Both legacy
URLs return 404 (cleaned up 2026-06-19), so the wallet wasted a request
on a dead path before reaching the v2 snapshot URL.

Changes:
- DownloadBootstrap() no longer tries /triangles-bootstrap.tar.gz.
- Goes straight to filelist.txt → downloads the URL listed there (now
  utxo-snapshot.bin only, after the bootstrap server fix).
- Removed unused ExtractTarGz() helper function (~110 lines).
- Kept DEFAULT_HOST in bootstrap.h — init.cpp still references it
  for the SnapshotNet P2P fetch.

No version bump. v5.9.20 binary built locally; SHA
ad34764e28fb0c922a3f3570e830ba5707fdc2f7f7a11301e8c0f60356048fd3.

Bootstrap server fix landed first:
- /var/www/triangles-bootstrap/filelist.txt now contains only
  'utxo-snapshot.bin' (was tri-bootstrap.tar.gz + triangles-bootstrap.tar.gz).
This means existing laptop wallets (no rebuild needed) will now read the
updated filelist.txt on next bootstrap attempt and go straight to the
v2 snapshot URL.
2026-06-20 04:07:07 -07:00
Sami Ahmed bdb7253399 Remove -allowfastimport (FastImport) entirely
FastImport was the legacy path for rebuilding the block index from a
local blk0001.dat. With v2 UTXO snapshots now containing embedded
blocks, FastImport is redundant and dangerous (could silently index
a forked chain from a stale blk0001.dat).

Changes:
- src/main.cpp: delete FastImportBlockFile() function (~270 lines)
- src/main.h:   delete FastImportBlockFile() declaration
- src/init.cpp:  delete -allowfastimport flag handler block
                 remove from help text
                 clean up stale comments referencing FastImportBlockFile
- src/bootstrap.cpp: update stale comments

v2 snapshot loading (auto-download from bootstrap or local placement
of utxo-snapshot.bin + manifest) is now the only supported sync start.

Tested: daemon builds, runs, chain state preserved across restart.
Binary SHA: 3f26f6202947a8dc0f7933314829702aafa1e42c968368ab7ec043d57baa9519
DNS2 + DNS3 running this build, both on correct chain.

Not bumped to v5.9.21 per Sami's preference. Next formal release
will inherit this change.
v5.9.20
2026-06-19 23:56:14 -07:00
Sami Ahmed d81a36f875 Add tri CLI wrapper: wallet + secure messaging for agents and humans
A bash command interface to trianglesd RPC designed for Hermes, Krystie,
and Sami to manage TRI wallets and communicate via the built-in secure
messaging system (smessage).

Features:
- Info: status, balance, peers, staking info
- Wallet: addresses, send, transactions
- Secure messaging: inbox, outbox, send (encrypted via ECDH over Tor P2P)
- Raw RPC passthrough for any daemon command
- Bash + zsh completion
- SSH-tunneled RPC for remote node access
- Config at /etc/tri/nodes.conf (shared between agents)

Files:
- scripts/tri/tri                    Main script
- scripts/tri/nodes.conf.example     Config template
- scripts/tri/tri-completion.bash    Bash completion
- scripts/tri/_tri_zsh_completion    Zsh completion
- scripts/tri/README.md              Documentation

Tested against live DNS3 node (block 2,207,455, 4 peers).
Secure messaging verified: send → inbox → outbox all working.
2026-06-19 21:09:33 -07:00
Sami Ahmed f4f9c3b45a Merge cpp20-modernization into master: triangles-cli + macOS/Windows build fixes
Brings in from cpp20-modernization branch:
- 8aeb513: triangles-cli JSON-RPC client (bitcoin-cli pattern)
- 1d938d5: macOS build - use std::filesystem, drop Boost::system
- 600b1cf: macOS build - Boost::boost target for headers
- 569b541: Simplify DLL packaging
- 274aafa/91d9233: Windows packaging fixes
- 8c74f4e/ad26786: Packaging scripts (package-windows-daemon.sh, package-linux-daemon.sh)
2026-06-19 20:49:33 -07:00
Sami Ahmed 73c3cef8d4 bump: version 5.9.20 2026-06-19 20:19:43 -07:00
hermes a38bfd2f97 fix: auto-download UTXO snapshot when chain DB missing (3 root causes)
Three bugs prevented the wallet from automatically downloading the UTXO
snapshot when starting with stale blk0001.dat but no chain database:

1. NeedsBootstrap() only checked for blk0001.dat existence, not the chain
   DB. If blk0001.dat was present (leftover from old version) but
   txleveldb/chainstate was missing, it reported "no bootstrap needed"
   and the snapshot download never triggered.

   Fix: check for txleveldb/ or blocks/chainstate/ instead.

2. Bootstrap HTTP download was skipped when snapshotMode was true (the
   default). The code deferred to P2P snapshot fetch (Step 11.6), but
   that runs AFTER Step 7 which errored out on the FastImport gate.

   Fix: always attempt HTTP bootstrap when NeedsBootstrap is true,
   regardless of snapshotMode. The UTXO snapshot HTTP download IS the
   fast path — no reason to defer to P2P when HTTP is available.

3. FastImport gate (Step 7) was a hard InitError that killed the daemon
   before it ever reached the snapshot fetch path. blk0001.dat present
   + no chain index + FastImport disabled = immediate crash.

   Fix: instead of erroring, remove the stale blk0001.dat and continue.
   The daemon syncs from the snapshot that was already loaded in Step 6b,
   or from P2P if that somehow failed.
2026-06-19 19:40:40 -07:00
Sami Ahmed 23e8a2d647 utxosnapshot: v2 format — embed full blk0001.dat into snapshot
Per Sami's vision: 'I want to carry over the whole block inside the
UTXO.' The snapshot is now self-contained: a fresh node loading it
has everything needed (headers + UTXOs + all block bodies) without
needing a separate bootstrap tarball.

Format change (UTXO_SNAPSHOT_VERSION 1 → 2):

v1 HEADER (88 bytes):
  magic, version, network, height, blockHash, moneySupply,
  numHeaders, numUtxos, contentHash

v2 HEADER (92 bytes):
  same + numBlocks (between numUtxos and contentHash)

v2 CONTENT (after v1's headers + utxos sections):
  blocks[numBlocks]  ← raw blk0001.dat bytes, SHA256 included

DumpSnapshot changes:
- Walks ALL blocks from pindexBest to pindexGenesisBlock (was: last
  N=2000). The nHeaders arg is honored only when caller passes a
  count smaller than the full chain for v1-compat diagnostic snapshots.
- After headers + utxos sections, streams GetDataDir()/blk0001.dat
  bytes into the snapshot, chunked (64 KB), content-hashed.
- Header now writes numBlocks between numUtxos and contentHash.

LoadSnapshot changes:
- Reads numBlocks after numUtxos when version >= 2.
- After UTXOs section, streams numBlocks bytes from the snapshot
  into dataDir/blk0001.dat (uses GetDataDir() since the param dataDir
  is intentionally unnamed in this function).
- v1 snapshots still load via the partial-load path (no numBlocks in
  header, no blk0001.dat written).
- Empty snapshot check loosened to (numHeaders && numUtxos && numBlocks)
  — all three must be zero to be considered empty.

Total v2 snapshot size: ~1.9 GB (headers + blocks + UTXOs).
Generation on the operator machine: a few minutes. Download on
reasonable connection: a few minutes.

This supersedes the earlier v2 attempt (commit 69529ea) which had
compile bugs from using an unnamed dataDir parameter and had wrong
snapshot file layout.
2026-06-19 04:22:07 -07:00
Sami Ahmed d73f6015a9 Merge feature/utxo-snapshot-auto-rebuild: signature auth + auto-rebuild + crash fixes
Adds:
- bootstrap: read manifest.json + verify file SHA256 (defense in depth)
- bootstrap: signature-based snapshot authentication (replaces checkpoint gate)
- checkpoints: drop 2207680 entry (signature is the gate now)
- init: auto-rebuild trigger (-autorerebuild=N) — wipe chain DB if stale
- init: remove FastImport as primary path (-allowfastimport, default off)
- utxosnapshot: set fSerializeChainTrust=true before LoadSnapshot writes
- init: skip block verification for snapshot-sourced chains
- init: don't fail on ResetSyncCheckpoint for snapshot-sourced chains
- build: ignore build-*/ directories

Server-side: utxo-snapshot.bin symlinked to utxo-snapshot-2207680.utx on bootstrap.cryptographic-triangles.org

End-to-end verified from zero: snapshot loads to height 2207680,
bestblockhash matches manifest, 4 peers connected via Tor.

Closes PR #8. Combines all the separate branches per Sami's directive.
v5.9.19
2026-06-19 03:44:48 -07:00
Sami Ahmed ca16abe155 Merge v5.9.17-local-snapshot-trust: signed UTXO snapshot infrastructure
Adds the foundation for the snapshot-based IBD:
- sign-snapshot.sh: operator-side script to sign canonical snapshots
- utxosnapshot gate requireCheckpoint on trust source
- utxosnapshot build address index when loading (wallet balance support)
- main build address index during FastImport
- build: ignore build-*/ directories
2026-06-19 03:44:48 -07:00
Sami Ahmed be865c5944 Revert "utxosnapshot: v2 format — embed full blk0001.dat into snapshot"
This reverts commit 69529ea4c7.
2026-06-19 03:33:05 -07:00
Sami Ahmed 69529ea4c7 utxosnapshot: v2 format — embed full blk0001.dat into snapshot
Per Sami's vision: 'I want to carry over the whole block inside the
UTXO.' The snapshot should be self-contained so a fresh node is fully
usable — can serve blocks to peers, fully verify the chain, validate
txs, and resume syncing forward. Replaces the legacy tri-bootstrap.tar.gz.

Format change (UTXO_SNAPSHOT_VERSION 1 → 2):

v1 HEADER:
  magic, version, network, height, blockHash, moneySupply,
  numHeaders, numUtxos, contentHash (88 bytes)

v2 HEADER:
  same + numBlocks (92 bytes)  ← new field

v2 CONTENT (after v1's headers + utxos sections):
  blocks[numBlocks]  ← raw blk0001.dat bytes, SHA256 included

DumpSnapshot changes:
- Walks ALL blocks from pindexBest to pindexGenesisBlock (was: last
  N=2000). The nHeaders arg is honored only when 0 < nHeaders < chain
  height for v1-compat diagnostic snapshots.
- After writing headers + utxos sections, streams GetDataDir()/blk0001.dat
  bytes into the snapshot, chunked (64 KB), content-hashed.
- Header now writes numBlocks between numUtxos and contentHash.

LoadSnapshot changes:
- Reads numBlocks after numUtxos when version >= 2.
- After the UTXOs section, streams numBlocks bytes from the snapshot
  into dataDir/blk0001.dat.
- v1 snapshots (no numBlocks in header) still load via the partial
  path: headers + UTXOs only, no blk0001.dat written. The 'block
  verification skipped for snapshot-sourced chains' hack stays
  for v1, becomes unnecessary for v2.

Total v2 snapshot size: ~1.9 GB (550 MB headers + 1.3 GB blocks + 50 MB UTXOs).
Generation on the operator machine: a few minutes. Download on
reasonable connection: a few minutes.

This commit is format-only — signature verification, auto-rebuild,
and the LoadBlockIndex crash fix from PR #8 still apply unchanged.
2026-06-19 03:11:00 -07:00
Sami Ahmed dcfb650d9f init: don't fail on ResetSyncCheckpoint for snapshot-sourced chains
When LoadBlockIndex tries to reset the sync-checkpoint, it looks for
one of the known checkpoint blocks in mapBlockIndex and writes it to
the DB. For a freshly snapshot-loaded chain, mapBlockIndex only has
~1166 headers near the tip — none of the known sync checkpoints
(2205000, 2206004) are in that subset.

The reset returns false (no checkpoint found in main chain), and the
caller currently treats this as fatal: 'failed to reset sync-checkpoint'.
But for snapshot-sourced chains this is expected — the sync checkpoint
will be set when the node syncs past the next known checkpoint height.

Soften the failure: if fLoadedFromSnapshot is true, log a warning and
continue instead of erroring out.
2026-06-19 02:54:58 -07:00
Sami Ahmed 800f508abd init: skip block verification for snapshot-sourced chains
After LoadSnapshot, the daemon has headers + UTXOs but the raw block
bodies haven't been downloaded yet — they'll arrive via P2P as the
node syncs past the snapshot tip. LoadBlockIndex's verification
loop tries to read the last 50 block bodies from disk and fails
with 'OpenBlockFile failed' because the data isn't on disk yet.

Add fLoadedFromSnapshot global, set true at the end of successful
LoadSnapshot. In both txdb-leveldb.cpp and txdb-rocksdb.cpp LoadBlockIndex
verification loops, when ReadFromDisk fails AND fLoadedFromSnapshot is
true, log a warning and continue (the UTXO set itself was already
content-hash verified during LoadSnapshot, so we have strong evidence
the chain state is correct).

For non-snapshot chains (full blk0001.dat downloaded, normal IBD), the
ReadFromDisk failure remains a fatal error as before.

Combined with the prior fix in utxosnapshot.cpp that sets
fSerializeChainTrust=true before writes, the full snapshot path now
works end-to-end on a fresh datadir.
2026-06-19 02:47:30 -07:00
Sami Ahmed 78dae9fdaa utxosnapshot: set fSerializeChainTrust=true before LoadSnapshot writes
THE BUG: CDiskBlockIndex serialization is gated by a static flag
fSerializeChainTrust. LoadBlockIndex later sets this flag to true
based on dbformat >= 2 and tries to read nChainTrust as part of every
CDiskBlockIndex record.

But LoadSnapshot runs FIRST and writes CDiskBlockIndex records while
the static is still at its default value (false). The records are
written WITHOUT nChainTrust. Then LoadBlockIndex reads with flag=true,
expects nChainTrust, runs off the end of the buffer → 'CDataStream::read():
end of data: iostream error' → AppInit() exception.

This bug affected every fresh snapshot load: the snapshot's headers
and UTXOs loaded correctly (the per-record writes work), then the
post-load LoadBlockIndex crashed. Sami identified this as the
'format mismatch' blocker; the signature verification work went in
first but the underlying serialization bug remained.

Fix: explicitly set fSerializeChainTrust=true at the top of LoadSnapshot
before any CDiskBlockIndex writes. Then writes include nChainTrust.
Then LoadBlockIndex reads with the same flag set → matches.

The snapshot FILE format itself is unchanged — old snapshots produced
by daemons that wrote with flag=false will still fail to load (their
records don't have nChainTrust). New snapshots produced by daemons
that always write with flag=true (i.e. always include nChainTrust)
will load cleanly.
2026-06-19 02:42:09 -07:00
Sami Ahmed 48cf7277dd init: auto-rebuild trigger + remove FastImport as primary path
Two operational changes that together fulfill the 'snapshot as
universal sync start' vision:

1. -autorerebuild=<n> CLI flag (default 0=disabled)
   After Step 7 loads the chain DB, MaybeAutoRebuild() compares our
   local nBestHeight to the median peer-reported height (collected via
   CNode::nStartingHeight from the version handshake). If lag >= n,
   wipe the chain DB (preserve wallet.dat, onion, smsg state) and
   request shutdown. On restart, the daemon sees no chain DB and the
   snapshot path takes over.

   WaitForPeerHeights() polls up to 60s for at least 3 peers.

2. -allowfastimport CLI flag (default OFF)
   The FastImportBlockFile() rebuild path is now gated behind this
   flag. If the chain DB is empty and blk0001.dat exists, the daemon
   fails with a clear error message that tells the operator how to
   recover (place utxo-snapshot.bin, delete blk0001.dat, or set
   -allowfastimport). FastImport is now operator opt-in only — the
   snapshot path is the canonical sync start.

   This matches Sami's vision: 'Everything should be transferred over
   to the UTXO jump and then they should be able to put the blockchain
   together exactly how it's supposed to be from all the peers
   filling in all the blank spots.'
2026-06-19 02:36:09 -07:00
Sami Ahmed d6b47b5a0d checkpoints: drop 2207680 entry (signature is the snapshot gate now)
When I added the 2207680 checkpoint, I was treating checkpoints as the
authentication gate for snapshot loading. Sami corrected: 'It shouldn't
require a checkpoint, all it should require is a signature.'

Commit 2866a94 already replaced requireCheckpoint=true with signature
verification in DownloadUtxoSnapshot. This commit removes the now-
unnecessary checkpoint entry so the source stays clean — the signature
is the only gate for snapshots, period.

(2205000/2206004 checkpoints remain — they're separate concerns for
chain finality validation, not snapshot acceptance.)
2026-06-19 02:28:23 -07:00
Sami Ahmed 2866a94be1 bootstrap: signature-based snapshot authentication
DownloadUtxoSnapshot now authenticates snapshots via Triangles signed
messages instead of relying on hardcoded checkpoints.

New flow:
1. Fetch big manifest.json, find canonical snapshot entry
2. Fetch the per-snapshot manifest (utxo-snapshot-{h}.manifest.json)
3. Verify the signer address is in the trusted signers list (currently
   Sami's TG8f76yktTxDrT7JJymY3wVAusXiD3fVvX)
4. Verify the signature cryptographically (Triangles compact-message
   protocol with strMessageMagic prefix, same construction as
   signmessage/verifymessage RPC)
5. Download snapshot file, verify SHA256 against manifest
6. Load with requireCheckpoint=false — signature is the gate

Per Sami: 'It shouldn't require a checkpoint all it should require
is a signature.' This removes the checkpoint coupling that was
breaking fresh-node sync (the 2207680 checkpoint gate rejected the
canonical snapshot even though it was validly signed).

Trusted signer list is currently a hardcoded constant. Future work:
-snapshotsigner=<addr> CLI arg (repeatable).
2026-06-19 02:24:07 -07:00
Sami Ahmed 2a7c89a91e bootstrap: read manifest.json for canonical snapshot + verify SHA256
DownloadUtxoSnapshot now:
1. Fetches manifest.json from the bootstrap server
2. Locates the utxo_snapshot entry (filename + expected sha256)
3. Downloads THAT file
4. Verifies file SHA256 matches manifest
5. Falls back to legacy 'utxo-snapshot.bin' if manifest unavailable

Also add 2207680 checkpoint to mapCheckpoints so the canonical signed
snapshot (per 2026-06-18 manifest) passes the requireCheckpoint gate.

Defense in depth: server symlinks + daemon verifies the file matches.
2026-06-19 02:02:53 -07:00
Sami Ahmed 677a8ea79a build: ignore build-*/ directories and build artifacts 2026-06-19 00:17:10 -07:00
SamiAhmed7777 b40c58f886 Add triangles-cli: JSON-RPC client (port bitcoin-cli pattern) (#7)
* Add triangles-cli: JSON-RPC client (port bitcoin-cli pattern)

Triangles never had a CLI client (bitcoin-cli analog). This adds
triangles-cli as a third build target alongside trianglesd and
triangles-qt.

- src/triangles-cli.cpp: self-contained JSON-RPC 1.0 client.
  Reads triangles.conf for credentials, supports -rpcuser/-rpcpassword
  /-rpcconnect/-rpcport/-testnet/-datadir/-conf flags. Implements
  -getinfo (synthesized summary from getnetworkinfo/getblockchaininfo
  /getwalletinfo) and raw method dispatch. JSON via json_spirit compat
  shim (json_compat.h), HTTP via boost::asio, base64 auth inline.
  No util.cpp / wallet.cpp / net.cpp / triangles_common link dep —
  keeps the binary small (~600 KB Linux, ~1.5 MB Windows).

- CMake: new option(BUILD_CLI ON) + add_executable(triangles-cli)
  in src/CMakeLists.txt. Status line added.

- CI: BUILD_CLI=ON added to build-windows-daemon and build-linux-daemon
  jobs. triangles-cli.exe bundled into windows-daemon artifact
  alongside trianglesd.exe. triangles-cli added to linux-daemon .deb
  package (with launcher in /usr/bin).

- Default ON; set BUILD_CLI=OFF to skip.

Closes the open 'triangles-cli.exe missing from Windows build'
follow-up (the binary wasn't missing — it never existed).

Patterned after Bitcoin Core bitcoin-cli and Dash Core dash-cli.

* Fix macOS build: drop Boost::system/find_package component, use std::filesystem

Homebrew's boost formula doesn't ship the boost_system CMake config file,
so find_package(Boost REQUIRED COMPONENTS system) failed on macOS.

- Replace boost::filesystem with std::filesystem (C++17, no Boost dep)
- Drop 'filesystem' from find_package — only headers needed (asio + system)
- Link libboost_system explicitly per-platform by library name, resolved
  via the platform's default search path (Homebrew toolchain on macOS,
  system libs on Linux, MSYS2 on Windows)

CI will rerun automatically on PR push.

* Fix macOS build: add Boost::boost target for headers, link boost_system

The previous fix dropped the find_package component but also killed the
boost include path. Now use the modern Boost::boost header-only target
(available in Boost 1.83+) which sets up include directories without
requiring a per-component config file.

Link libboost_system explicitly by name on all platforms — the linker
finds it via the platform's default search path:
- Linux: /usr/lib (libboost_system.so)
- macOS Homebrew: /opt/homebrew/lib (libboost_system.dylib)
- Windows MSYS2: mingw64/bin (libboost_system-mt-X-XX.dll)

* Drop Boost entirely from triangles-cli: use raw sockets for HTTP

Third time's the charm. After two CI failures chasing boost::asio / libboost_system
linking issues across platforms (Homebrew missing config on macOS, MSYS2 versioned
names on Windows, CMake targets that don't quite work everywhere), rip the whole
Boost dependency out of the CLI and use raw POSIX/Winsock sockets.

- triangles-cli.cpp: replaced boost::asio with raw socket() / connect() / send()
  / recv() / getaddrinfo(). Cross-platform: #ifdef _WIN32 for Winsock + WSAStartup
  / WSACleanup, else POSIX. ~100 lines of clean portable socket code.
- src/CMakeLists.txt: dropped find_package(Boost) entirely. Only links
  json_compat (header-only) + ws2_32 on Windows. No boost libs to find.

Should be the last fix needed for this PR.

* Fix Windows packaging step: simplify bash { } | sort -u | while pattern

The previous step used a bash group command piped through sort -u and a
while loop. Under MSYS2 bash + 'set -e -o pipefail' (GitHub Actions
default), this triggered a non-zero exit even when the loop body
succeeded, causing the Windows daemon job to fail at the packaging step
(the actual link of both trianglesd.exe and triangles-cli.exe succeeded).

Replaced the { } | sort -u | while pattern with a temp-file-based dedup:
- ldd both binaries, append to /tmp/cli-dlls.txt (or cli-libs.txt on Linux)
- sort -u the temp file
- pipe the result into the while loop (simpler pipeline, no group)

Also applied the same simplification to the Linux .deb packaging for
consistency, even though the Linux build was passing.

* Simplify DLL packaging: plain for loop, no pipe-into-while

The previous attempts used 'ldd | sort -u | while read; do ... done' patterns
that exit 1 under MSYS2 bash + 'set -e -o pipefail' even when the script
ran successfully. Replaced with a plain 'for bin in ...; do ldd > list.txt;
while read; do cp; done < list.txt; done' pattern that has no pipelines
other than the standard redirection, and uses IFS= read -r for safe line
iteration.

Also moved temp files from /tmp to the working directory (./dll-list.txt)
to avoid any MSYS2 /tmp path-translation edge cases.

* diagnostic: add tracing to Windows packaging step

* Add package-windows-daemon.sh + package-linux-daemon.sh scripts

Move the Windows daemon packaging step and the Linux .deb build into
committed shell scripts under scripts/ci/. This bypasses GitHub Actions'
inline-run-block quirks (silent exit 1 under msys2 + set -e -o pipefail
with multi-line scripts) and makes the packaging logic debuggable locally.

* Switch to script-file packaging for Windows + Linux daemon jobs

Replace inline multi-line run: blocks with invocations of the
scripts/ci/package-*.sh scripts. This sidesteps the GitHub Actions
msys2 + 'set -e -o pipefail' issue that caused silent exit 1 on the
Windows daemon packaging step. The scripts are also debuggable locally.

---------

Co-authored-by: Krystie <krystie@sami>
2026-06-18 20:05:54 -07:00
Krystie ad267866ab Switch to script-file packaging for Windows + Linux daemon jobs
Replace inline multi-line run: blocks with invocations of the
scripts/ci/package-*.sh scripts. This sidesteps the GitHub Actions
msys2 + 'set -e -o pipefail' issue that caused silent exit 1 on the
Windows daemon packaging step. The scripts are also debuggable locally.
2026-06-18 19:50:10 -07:00
Krystie 8c74f4e228 Add package-windows-daemon.sh + package-linux-daemon.sh scripts
Move the Windows daemon packaging step and the Linux .deb build into
committed shell scripts under scripts/ci/. This bypasses GitHub Actions'
inline-run-block quirks (silent exit 1 under msys2 + set -e -o pipefail
with multi-line scripts) and makes the packaging logic debuggable locally.
2026-06-18 19:49:31 -07:00
Krystie f0e5dbdebc diagnostic: add tracing to Windows packaging step 2026-06-18 19:47:22 -07:00
Krystie 91d9233ea4 Simplify DLL packaging: plain for loop, no pipe-into-while
The previous attempts used 'ldd | sort -u | while read; do ... done' patterns
that exit 1 under MSYS2 bash + 'set -e -o pipefail' even when the script
ran successfully. Replaced with a plain 'for bin in ...; do ldd > list.txt;
while read; do cp; done < list.txt; done' pattern that has no pipelines
other than the standard redirection, and uses IFS= read -r for safe line
iteration.

Also moved temp files from /tmp to the working directory (./dll-list.txt)
to avoid any MSYS2 /tmp path-translation edge cases.
2026-06-18 19:34:45 -07:00
Krystie 274aafab36 Fix Windows packaging step: simplify bash { } | sort -u | while pattern
The previous step used a bash group command piped through sort -u and a
while loop. Under MSYS2 bash + 'set -e -o pipefail' (GitHub Actions
default), this triggered a non-zero exit even when the loop body
succeeded, causing the Windows daemon job to fail at the packaging step
(the actual link of both trianglesd.exe and triangles-cli.exe succeeded).

Replaced the { } | sort -u | while pattern with a temp-file-based dedup:
- ldd both binaries, append to /tmp/cli-dlls.txt (or cli-libs.txt on Linux)
- sort -u the temp file
- pipe the result into the while loop (simpler pipeline, no group)

Also applied the same simplification to the Linux .deb packaging for
consistency, even though the Linux build was passing.
2026-06-18 19:19:32 -07:00
Krystie 569b541931 Drop Boost entirely from triangles-cli: use raw sockets for HTTP
Third time's the charm. After two CI failures chasing boost::asio / libboost_system
linking issues across platforms (Homebrew missing config on macOS, MSYS2 versioned
names on Windows, CMake targets that don't quite work everywhere), rip the whole
Boost dependency out of the CLI and use raw POSIX/Winsock sockets.

- triangles-cli.cpp: replaced boost::asio with raw socket() / connect() / send()
  / recv() / getaddrinfo(). Cross-platform: #ifdef _WIN32 for Winsock + WSAStartup
  / WSACleanup, else POSIX. ~100 lines of clean portable socket code.
- src/CMakeLists.txt: dropped find_package(Boost) entirely. Only links
  json_compat (header-only) + ws2_32 on Windows. No boost libs to find.

Should be the last fix needed for this PR.
2026-06-18 19:05:37 -07:00
Krystie 600b1cf35f Fix macOS build: add Boost::boost target for headers, link boost_system
The previous fix dropped the find_package component but also killed the
boost include path. Now use the modern Boost::boost header-only target
(available in Boost 1.83+) which sets up include directories without
requiring a per-component config file.

Link libboost_system explicitly by name on all platforms — the linker
finds it via the platform's default search path:
- Linux: /usr/lib (libboost_system.so)
- macOS Homebrew: /opt/homebrew/lib (libboost_system.dylib)
- Windows MSYS2: mingw64/bin (libboost_system-mt-X-XX.dll)
2026-06-18 18:45:04 -07:00
Krystie 1d938d5770 Fix macOS build: drop Boost::system/find_package component, use std::filesystem
Homebrew's boost formula doesn't ship the boost_system CMake config file,
so find_package(Boost REQUIRED COMPONENTS system) failed on macOS.

- Replace boost::filesystem with std::filesystem (C++17, no Boost dep)
- Drop 'filesystem' from find_package — only headers needed (asio + system)
- Link libboost_system explicitly per-platform by library name, resolved
  via the platform's default search path (Homebrew toolchain on macOS,
  system libs on Linux, MSYS2 on Windows)

CI will rerun automatically on PR push.
2026-06-18 18:41:38 -07:00
Krystie 8aeb5133bf Add triangles-cli: JSON-RPC client (port bitcoin-cli pattern)
Triangles never had a CLI client (bitcoin-cli analog). This adds
triangles-cli as a third build target alongside trianglesd and
triangles-qt.

- src/triangles-cli.cpp: self-contained JSON-RPC 1.0 client.
  Reads triangles.conf for credentials, supports -rpcuser/-rpcpassword
  /-rpcconnect/-rpcport/-testnet/-datadir/-conf flags. Implements
  -getinfo (synthesized summary from getnetworkinfo/getblockchaininfo
  /getwalletinfo) and raw method dispatch. JSON via json_spirit compat
  shim (json_compat.h), HTTP via boost::asio, base64 auth inline.
  No util.cpp / wallet.cpp / net.cpp / triangles_common link dep —
  keeps the binary small (~600 KB Linux, ~1.5 MB Windows).

- CMake: new option(BUILD_CLI ON) + add_executable(triangles-cli)
  in src/CMakeLists.txt. Status line added.

- CI: BUILD_CLI=ON added to build-windows-daemon and build-linux-daemon
  jobs. triangles-cli.exe bundled into windows-daemon artifact
  alongside trianglesd.exe. triangles-cli added to linux-daemon .deb
  package (with launcher in /usr/bin).

- Default ON; set BUILD_CLI=OFF to skip.

Closes the open 'triangles-cli.exe missing from Windows build'
follow-up (the binary wasn't missing — it never existed).

Patterned after Bitcoin Core bitcoin-cli and Dash Core dash-cli.
2026-06-18 18:33:30 -07:00
hermes d8af2aa17c scripts: add sign-snapshot.sh for signed UTXO snapshot provenance
Generates a UTXO snapshot via dumputxoset RPC, signs a provenance message
(height, blockhash, snapshot sha256) with signmessage, and emits a signed
manifest.json. Verification via ./sign-snapshot.sh verify <manifest> <snap>
or verifymessage RPC on any node.

Pairs with the requireCheckpoint trust-gate patch — local snapshots no
longer require a known checkpoint, so signing provenance is the way to
establish authority for a snapshot.
v5.9.18
2026-06-18 02:39:43 -07:00
hermes e15de97be3 utxosnapshot: gate requireCheckpoint on trust source
Local file snapshots (init.cpp) skip the known-checkpoint gate; P2P-delivered
snapshots (bootstrap.cpp) keep it. Rationale: the checkpoint gate exists to
prevent malicious peers from injecting fake UTXO sets. Local file loads come
from operator-trusted sources (filesystem access already grants equal power),
so the gate is unnecessary friction.
2026-06-18 02:34:51 -07:00
triangles-bot c606253c41 utxosnapshot: build address index when loading a UTXO snapshot (fast-start nodes get balances) [v5.9.17] v5.9.17 2026-06-16 20:37:44 -07:00
triangles-bot b2dfb627cc main: build address index during FastImport (fix-in-place, v5.9.16) v5.9.16 2026-06-16 20:24:26 -07:00
triangles-bot d0a76f8ae2 qt: show Seed Phrase (HD Backup) in the visible Operations menu (v5.9.15)
The HD seed action was only added to the standard Qt menu bar, which the
skinned GUI hides. Add it to menuOperationsRequested() so users can actually
reach Generate / Reveal-for-backup / Restore from the Operations menu.
v5.9.15
2026-06-16 16:24:12 -07:00
SamiAhmed7777 cc57c906b4 Merge PR #6: HD seed-phrase wallet + fast-sync checkpoint/snapshot (v5.9.14)
HD wallet (BIP39/BIP32 seed phrases) - daemon + Qt
v5.9.14
2026-06-15 20:44:52 -07:00
Sami e80d672833 checkpoints: add 2206004 checkpoint + UTXO snapshot hash (fast new-node sync) 2026-06-15 20:31:02 -07:00
Sami fcdc9a58b0 ci(lint): checkout secp256k1 submodule for clang-tidy (fixes configure) 2026-06-15 19:26:43 -07:00
Sami 514867c5d9 wallet(HD): flush keypool on seed set so getnewaddress yields HD keys immediately 2026-06-15 19:16:03 -07:00
Sami c464e6c59d wallet(HD): Qt UI - Seed Phrase dialog (generate/restore/backup)
Adds HDSeedDialog (Settings > Seed Phrase) with Generate New / Reveal for Backup / Restore from Phrase, driven by new WalletModel HD methods. Restore rescans the chain. Requires wallet unlock via the standard UnlockContext.
2026-06-15 19:16:03 -07:00
Sami 11ed086d1e wallet(HD): native BIP39/BIP32 HD wallet - daemon side
Adds deterministic HD key derivation (path m/44'/2222'/0'/0/i, matching the TRIdock web wallet) wired into CWallet: HD seed stored in wallet.dat (encrypted with the wallet master key when the wallet is encrypted), keypool derived from the seed, and new RPC commands hdnew/hdrestore/hdshow/hdinfo. Crypto core verified standalone against the official BIP39 vector and triWallet.js addresses.
2026-06-15 19:15:43 -07:00
Hermes 5511cfae6b v5.9.14 + pitfall #61 guard: initialize pindexFinalized on startup
ROOT CAUSE of the 2026-06-16 minority-fork reorg:

The v5.9.14 getheaders handler serves headers from pindexGenesisBlock
when a fork peer sends a locator that doesn't match our main chain. The
intent was to help fork peers learn the canonical chain. The bug: this
allows the fork peer to feed us THEIR short chain back via getheaders,
and we accept it because:

1. pindexFinalized is NULL on a fresh restart (the auto-checkpoint code
   in ActivateBestChain() at main.cpp:2459 only sets it when
   !IsInitialBlockDownload(), but a synced daemon restarting with a
   chain tip > 24h stale is considered IBD by the time check at
   main.cpp:1331).

2. With pindexFinalized = NULL, the reorg guard at main.cpp:2198
   short-circuits: 'if (pindexFinalized && pfork->nHeight < ...)'.

3. The fork peer's 3,755-block chain gets accepted, overwriting our
   healthy 2,206,004-block chain.

THE FIX (two parts):

A. init.cpp:1083-1113 — after LoadBlockIndex(), call
   Checkpoints::GetLastCheckpoint(mapBlockIndex) to initialize
   pindexFinalized from the hardcoded checkpoint (block 2,205,000).
   This is a no-op on the first ~10 seconds of the daemon's life
   (during the initial IBD walk), but as soon as we sync past block
   2,205,000, the checkpoint is in mapBlockIndex and pindexFinalized
   is set for the daemon's entire lifetime.

B. main.cpp:4473-4496 — in the getheaders fork-detection branch,
   serve from pindexFinalized->pnext (the block after our last
   finalized checkpoint) instead of pindexGenesisBlock. This is the
   safe equivalent of the v5.9.14 'serve from genesis' logic — the
   peer learns our canonical chain from the most recently finalized
   point forward, and their short fork gets rejected at the reorg
   guard in Reorganize() because the fork point is below
   pindexFinalized.

Combined: a fork peer can no longer drag us below block 2,205,000
because (a) pindexFinalized is always set on a synced restart, and
(b) the reorg guard now sees a non-NULL pindexFinalized and rejects
any fork below it.

Tested manually by simulating a restart with the v5.9.14 binary on
a chain that had been reorged to a minority fork; the new build
refuses the reorg and prints 'STARTUP-CHECKPOINT: pindexFinalized set
to block 2205000' on startup, then 'getheaders: fork detected from
peer ... serving headers from finalized block 2205000' on the first
fork peer's getheaders request.
2026-06-15 18:37:53 -07:00
Sami Ahmed 1dda8b3006 Auto-repair corrupt Tor state file on daemon startup (pitfall #19)
Tor's atomic state-write is: write state.tmp, then rename to state.
If the daemon is killed mid-write (pkill -9, OOM, power loss, disk-full),
the rename can fail and 'state' is left as a regular file instead of a
directory. On next start, Tor's config validator refuses to use it:

  [warn] State file '...' is not a file? Failing.
  [err] set_options: Bug: Acting on config options left us in a broken state. Dying.
  [err] Reading config failed--see warnings above.

The daemon then reports 'Tor failed to start. Triangles requires Tor to
operate.' (the error from the 2026-06-15 TRI-LAPTOP GUI wallet
screenshot) and refuses to come up at all.

Hit before on DNS3 (2026-05-24, fixed by manual 'mv state state.file.bak;
mkdir state' on the operator) and on the laptop today. The fix was always
the same one-liner; this patch makes the daemon do it itself.

Behavior:
- Detect: if tor_data/state exists and is NOT a directory, it's corrupt
- Quarantine: rename to tor_data/state.corrupt-YYYYMMDD-HHMMSS for
  inspection (the user might want to recover the file's contents)
- If rename fails (Windows anti-virus holds the file, etc.), retry with
  remove-then-rename, and as a last resort just remove() so Tor can
  proceed
- Print a clear 'Tor state was a file (corrupt) — quarantined to ...'
  log line so the operator can see it happened
- Tor then recreates state/ as a fresh directory and bootstraps normally

This is a pure-additive change (no existing behavior modified). Builds
clean with the existing C++20 + libtor.a toolchain. No version bump
needed - will roll into the next CI cycle.
2026-06-15 16:29:05 -07:00
Sami Ahmed 68c4e38411 Fix getheaders pnext null bug: serve main chain from genesis, tip-backwards fallback
Two related bugs in the getheaders handler at main.cpp:4441:

1. Locator-mismatch returns 0 headers:
   GetBlockIndex() falls through to pindexGenesisBlock when no locator
   hash matches the main chain. pindexGenesisBlock->pnext is null, so
   the for-loop exits immediately and the peer gets an empty headers
   response. This was the DNS3 headers-first sync stall (pitfall #36):
   'getheaders -1 to 0000...' logged repeatedly.

   Mirror the getblocks handler (line 4387): detect the mismatch, log
   a warning, and serve headers from genesis so the peer can discover
   the canonical chain.

2. Broken pnext chain at any height:
   Even when GetBlockIndex() returns a valid (non-genesis) block, its
   pnext can be null — this happens when LoadBlockIndex() didn't fully
   heal pnext links (e.g., the node was bootstrapped from a snapshot,
   or the chain was interrupted by a crash). On tridock every pnext
   link was null despite 2.2M blocks (the June 11 'Heal pnext links'
   commit addressed a similar symptom for GetKernelStakeModifier() but
   doesn't reach into the getheaders handler).

   Fall back to a tip-backwards walk from pindexBest when pnext is
   null: O(N) but correct, and only triggers on the broken path.

   (References the design in references/getheaders-pnext-fix.md from
   the v5.9.10 deploy notes — that fix was never actually committed,
   only prototyped and dropped during the June 4 git cleanup.)

Bump to v5.9.14 (also embeds the embedded-Tor 0700 fix from ed996c8).

Refs: SKILL.md pitfall #36
2026-06-15 15:43:03 -07:00