diff options
| author | bitflash-maintainer <noreply@bitflash.invalid> | 2026-08-26 22:41:53 +0000 |
|---|---|---|
| committer | bitflash-maintainer <noreply@bitflash.invalid> | 2026-08-26 22:41:53 +0000 |
| commit | bbe04dc8c96b4c4766d2771d3438233bf82687c0 (patch) | |
| tree | 62e5a1febfb8e9bc38d133ac7981e9ef93918484 /scripts | |
| parent | 991853fa55a749d28d6f9e507ce72b289df92785 (diff) | |
| parent | 9e39a50d1f110020b0841d31e4b931aa877cc448 (diff) | |
Merge upstream v1.2.19 into the fork
Merges upstream tag v1.2.19 (9e39a50) into merge/upstream-1.2.18 (991853f).
Unlike the 1.2.15->1.2.18 replay, bc3de30 is a true ancestor of our branch via
a9b4f89, so this is an ordinary three-way merge with merge-base bc3de30.
Delta: 36 files, +3265/-163, the Tor onion transport (managed Tor, signed
direct onion endpoints, onion-only dial mode) and the nHDNext restore fix.
src/main.cpp, main.h, script.cpp/h and db.cpp/h are byte-identical between the
two tags, and the poll() socket loop carries no diff hunks, so the merge is
consensus-neutral and the soak-tested socket paths are unchanged. relay/ is
untouched; the payout ledger (rpc.cpp) is untouched.
7 conflict blocks in 6 files, resolved as follows.
Makefile -- VERSION. Ours: stays 1.2.19+sec with the naming comment updated,
so artifacts from this tree cannot be mistaken for the upstream release.
src/Makefile, src/makefile.mingw -- HEADERS list. Ours (no market.h) plus
their tor.h. NODE_OBJS auto-merged correctly: no market.o, tor.o present.
src/selftest.cpp -- known-tests list. Union: payout-ledger (ours) and
managed-tor (theirs).
src/main_gui.cpp -- one block. Our /dumpaddresses kept; their
BtfStopManagedTor() on the StartNode failure path taken. The /rvrelay pin,
/noannounce and the marketplace removal auto-merged and were verified intact.
src/nostr.cpp -- two blocks in ResolveDescriptor. Signature takes both
out-params (our fExchangeOk, their rawOut). The body keeps our
drain-to-EOSE read -- returning early poisons the pooled socket for the next
caller -- and adds their rawOut capture. Our TLS 1.2 floor
(SSL_CTX_set_min_proto_version) is untouched by this merge; upstream 1.2.19
still has no floor of its own.
Upstream's release commit also added four build logs and a compiled Python
cache (build-1219.log, build-17.log, build-appimage.log, build-node.log,
scripts/__pycache__/bitflash_chain.cpython-313.pyc). Removed here; they are
build output, not source.
Diffstat (limited to 'scripts')
| -rwxr-xr-x | scripts/build-explorer.py | 128 | ||||
| -rwxr-xr-x | scripts/make-release-checksums.sh | 2 | ||||
| -rwxr-xr-x | scripts/package-windows-tor.sh | 187 | ||||
| -rw-r--r-- | scripts/smoke-managed-tor-onion.ps1 | 174 |
4 files changed, 447 insertions, 44 deletions
diff --git a/scripts/build-explorer.py b/scripts/build-explorer.py index 97a7f4e..dbb8e17 100755 --- a/scripts/build-explorer.py +++ b/scripts/build-explorer.py | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | import argparse | 4 | import argparse |
| 5 | import hashlib | 5 | import hashlib |
| 6 | import json | 6 | import json |
| 7 | import shutil | ||
| 7 | import sys | 8 | import sys |
| 8 | from pathlib import Path | 9 | from pathlib import Path |
| 9 | 10 | ||
| @@ -148,6 +149,11 @@ def build_explorer(raw_blocks, out_dir): | |||
| 148 | encoding="ascii", | 149 | encoding="ascii", |
| 149 | ) | 150 | ) |
| 150 | (out / "index.html").write_text(INDEX_HTML, encoding="utf-8") | 151 | (out / "index.html").write_text(INDEX_HTML, encoding="utf-8") |
| 152 | (out / "style.css").write_text(STYLE_CSS, encoding="utf-8") | ||
| 153 | (out / "explorer.js").write_text(EXPLORER_JS, encoding="utf-8") | ||
| 154 | logo = Path(__file__).resolve().parents[1] / "docs" / "logo.png" | ||
| 155 | if logo.exists(): | ||
| 156 | shutil.copyfile(logo, out / "logo.png") | ||
| 151 | return len(blocks), len(blocks) - 1 | 157 | return len(blocks), len(blocks) - 1 |
| 152 | 158 | ||
| 153 | 159 | ||
| @@ -167,43 +173,26 @@ INDEX_HTML = r"""<!doctype html> | |||
| 167 | <meta charset="utf-8"> | 173 | <meta charset="utf-8"> |
| 168 | <meta name="viewport" content="width=device-width, initial-scale=1"> | 174 | <meta name="viewport" content="width=device-width, initial-scale=1"> |
| 169 | <title>Bitflash Block Explorer</title> | 175 | <title>Bitflash Block Explorer</title> |
| 170 | <style> | 176 | <meta name="description" content="Read-only Bitflash block explorer generated from local blk*.dat files."> |
| 171 | :root { color-scheme: light dark; --bg:#fff; --fg:#111; --mut:#666; --line:#e2e2e2; --card:#fafafa; --acc:#2b6cb0; } | 177 | <link rel="icon" href="logo.png" type="image/png"> |
| 172 | @media (prefers-color-scheme: dark){ :root{ --bg:#0e0f11; --fg:#e6e6e6; --mut:#9aa0a6; --line:#26282c; --card:#16181b; --acc:#63b3ed; } } | 178 | <link rel="stylesheet" href="style.css"> |
| 173 | * { box-sizing: border-box; } | 179 | <script src="explorer.js" defer></script> |
| 174 | body { font: 15px/1.5 system-ui, sans-serif; margin:0; background:var(--bg); color:var(--fg); } | ||
| 175 | header { padding:20px 24px; border-bottom:1px solid var(--line); display:flex; gap:16px; align-items:baseline; flex-wrap:wrap; } | ||
| 176 | h1 { font-size:18px; margin:0; } | ||
| 177 | h2 { font-size:16px; } | ||
| 178 | .mut { color:var(--mut); } | ||
| 179 | .wrap { padding:16px 24px; max-width:1100px; margin:0 auto; } | ||
| 180 | input { width:100%; max-width:420px; padding:9px 12px; border:1px solid var(--line); border-radius:8px; background:var(--card); color:var(--fg); font-size:14px; } | ||
| 181 | table { width:100%; border-collapse:collapse; margin-top:12px; } | ||
| 182 | th,td { text-align:left; padding:9px 10px; border-bottom:1px solid var(--line); font-variant-numeric:tabular-nums; } | ||
| 183 | th { font-size:12px; text-transform:uppercase; letter-spacing:.05em; color:var(--mut); } | ||
| 184 | tr.blk { cursor:pointer; } | ||
| 185 | tr.blk:hover td { background:var(--card); } | ||
| 186 | code { font-size:12px; word-break:break-all; } | ||
| 187 | .scroll { overflow-x:auto; } | ||
| 188 | .card { border:1px solid var(--line); border-radius:10px; background:var(--card); padding:14px 16px; margin-top:12px; } | ||
| 189 | .kv { display:grid; grid-template-columns:150px 1fr; gap:4px 12px; font-size:13px; } | ||
| 190 | .kv div:nth-child(odd){ color:var(--mut); } | ||
| 191 | .tx { border-top:1px solid var(--line); padding:10px 0; } | ||
| 192 | .io { display:grid; grid-template-columns:1fr 1fr; gap:16px; } | ||
| 193 | @media (max-width:640px){ .io{ grid-template-columns:1fr; } .kv{ grid-template-columns:110px 1fr; } } | ||
| 194 | .pill { display:inline-block; font-size:11px; padding:1px 7px; border-radius:99px; border:1px solid var(--line); color:var(--mut); } | ||
| 195 | button.link { background:none; border:none; color:var(--acc); cursor:pointer; font:inherit; padding:0; } | ||
| 196 | .note { color:var(--mut); font-size:13px; max-width:820px; } | ||
| 197 | </style> | ||
| 198 | </head> | 180 | </head> |
| 199 | <body> | 181 | <body> |
| 200 | <header> | 182 | <header class="topbar"> |
| 201 | <h1>Bitflash Block Explorer</h1> | 183 | <a class="brand" href="https://bitflash.network/"><img src="logo.png" width="22" height="22" alt="">Bitflash</a> |
| 202 | <span class="mut" id="tip"></span> | 184 | <nav aria-label="Bitflash sites"> |
| 203 | <span style="flex:1"></span> | 185 | <a href="https://bitflash.network/">home</a> |
| 204 | <button class="link" id="home" style="display:none">back to blocks</button> | 186 | <a href="https://git.bitflash.network/bitflash/bitflash">code</a> |
| 187 | <a href="https://docs.bitflash.network/">docs</a> | ||
| 188 | <a href="https://releases.bitflash.network/">downloads</a> | ||
| 189 | <a href="https://explorer.bitflash.network/" aria-current="page">explorer</a> | ||
| 190 | <a href="https://status.bitflash.network/">status</a> | ||
| 191 | </nav> | ||
| 192 | <span class="meta" id="tip"></span> | ||
| 205 | </header> | 193 | </header> |
| 206 | <div class="wrap"> | 194 | <div class="wrap"> |
| 195 | <h1>Block explorer <button class="link" id="home" hidden>back to blocks</button></h1> | ||
| 207 | <p class="note">Read-only view of the main chain: blocks, their transactions, and the coinbase payout. There is no mempool and no per-address history here.</p> | 196 | <p class="note">Read-only view of the main chain: blocks, their transactions, and the coinbase payout. There is no mempool and no per-address history here.</p> |
| 208 | <div id="listview"> | 197 | <div id="listview"> |
| 209 | <input id="q" placeholder="Search by height or block hash"> | 198 | <input id="q" placeholder="Search by height or block hash"> |
| @@ -213,9 +202,63 @@ INDEX_HTML = r"""<!doctype html> | |||
| 213 | </table></div> | 202 | </table></div> |
| 214 | <p class="mut" id="more"></p> | 203 | <p class="mut" id="more"></p> |
| 215 | </div> | 204 | </div> |
| 216 | <div id="detail" style="display:none"></div> | 205 | <div id="detail" hidden></div> |
| 217 | </div> | 206 | </div> |
| 218 | <script> | 207 | </body> |
| 208 | </html> | ||
| 209 | """ | ||
| 210 | |||
| 211 | |||
| 212 | STYLE_CSS = r""":root{ | ||
| 213 | color-scheme: light dark; | ||
| 214 | --bg:#fff; --bg-2:#f6f6f6; --fg:#111; --dim:#6b6b6b; --line:#d7d7d7; --line-hard:#111; | ||
| 215 | --accent:#0b7a34; --accent-2:#12a047; --bar:3rem; | ||
| 216 | --mono: ui-monospace,"Cascadia Mono","Segoe UI Mono",Consolas,"DejaVu Sans Mono","Liberation Mono","Courier New",monospace; | ||
| 217 | } | ||
| 218 | @media (prefers-color-scheme: dark){:root{ | ||
| 219 | --bg:#000; --bg-2:#0b0b0b; --fg:#dcdcdc; --dim:#8a8a8a; --line:#242424; --line-hard:#4a4a4a; | ||
| 220 | --accent:#2ee06a; --accent-2:#6bff9f; | ||
| 221 | }} | ||
| 222 | *{box-sizing:border-box;} | ||
| 223 | html,body{margin:0;background:var(--bg);color:var(--fg);font:15px/1.55 var(--mono);-webkit-text-size-adjust:100%;} | ||
| 224 | a{color:var(--accent);} | ||
| 225 | .topbar{display:flex;align-items:center;gap:1rem;flex-wrap:wrap;min-height:var(--bar); | ||
| 226 | padding:.5rem clamp(.9rem,2.5vw,3rem);border-bottom:1px solid var(--line);position:sticky;top:0;background:var(--bg);z-index:5;} | ||
| 227 | .brand{display:flex;align-items:center;gap:.6rem;text-decoration:none;color:var(--fg);font-weight:bold;} | ||
| 228 | .brand img{width:22px;height:22px;display:block;} | ||
| 229 | .topbar nav{display:flex;flex-wrap:wrap;gap:.15rem 1.4rem;font-size:.86rem;} | ||
| 230 | .topbar nav a{text-decoration:none;color:var(--dim);} | ||
| 231 | .topbar nav a::before{content:"/";color:var(--line-hard);margin-right:.3rem;} | ||
| 232 | .topbar nav a:hover{color:var(--fg);text-decoration:underline;} | ||
| 233 | .topbar nav a[aria-current="page"]{color:var(--accent);font-weight:bold;} | ||
| 234 | .topbar nav a[aria-current="page"]::before{color:var(--accent);} | ||
| 235 | .topbar .meta{margin-left:auto;font-size:.76rem;color:var(--dim);letter-spacing:.06em;} | ||
| 236 | .wrap{padding:1.4rem clamp(.9rem,2.5vw,3rem) 4rem;} | ||
| 237 | h1{font-size:1rem;margin:0 0 .3rem;letter-spacing:.02em;} | ||
| 238 | h2{font-size:.95rem;margin:1.2rem 0 .4rem;} | ||
| 239 | .mut{color:var(--dim);} | ||
| 240 | .note{color:var(--dim);font-size:.82rem;max-width:60rem;margin:.2rem 0 1rem;} | ||
| 241 | input{width:100%;max-width:28rem;padding:.5rem .7rem;border:1px solid var(--line);background:var(--bg-2);color:var(--fg);font:inherit;font-size:.86rem;} | ||
| 242 | input:focus{outline:none;border-color:var(--accent);} | ||
| 243 | .scroll{overflow-x:auto;} | ||
| 244 | table{width:100%;border-collapse:collapse;margin-top:.8rem;font-size:.86rem;} | ||
| 245 | th,td{text-align:left;padding:.5rem .6rem;border-bottom:1px solid var(--line);font-variant-numeric:tabular-nums;} | ||
| 246 | th{font-size:.72rem;text-transform:uppercase;letter-spacing:.1em;color:var(--dim);font-weight:normal;} | ||
| 247 | tr.blk{cursor:pointer;} | ||
| 248 | tr.blk:hover td{background:var(--bg-2);} | ||
| 249 | code{font-size:.8rem;word-break:break-all;color:var(--fg);} | ||
| 250 | .card{border:1px solid var(--line);background:var(--bg-2);padding:.9rem 1rem;margin-top:.8rem;} | ||
| 251 | .kv{display:grid;grid-template-columns:11rem 1fr;gap:.25rem .8rem;font-size:.82rem;} | ||
| 252 | .kv div:nth-child(odd){color:var(--dim);} | ||
| 253 | .tx{border-top:1px solid var(--line);padding:.7rem 0;} | ||
| 254 | .io{display:grid;grid-template-columns:1fr 1fr;gap:1rem;} | ||
| 255 | @media (max-width:640px){.io{grid-template-columns:1fr;}.kv{grid-template-columns:7rem 1fr;}.topbar .meta{margin-left:0;width:100%;}} | ||
| 256 | .pill{display:inline-block;font-size:.68rem;padding:.05rem .5rem;border:1px solid var(--line);color:var(--dim);text-transform:uppercase;letter-spacing:.05em;} | ||
| 257 | button.link{background:none;border:none;color:var(--accent);cursor:pointer;font:inherit;padding:0;text-decoration:underline;} | ||
| 258 | """ | ||
| 259 | |||
| 260 | |||
| 261 | EXPLORER_JS = r""" | ||
| 219 | var SUM = [], SHOWN = 200; | 262 | var SUM = [], SHOWN = 200; |
| 220 | function btf(sat){ return (sat/1e8).toFixed(8).replace(/0+$/,'').replace(/\.$/,'')+" BTF"; } | 263 | function btf(sat){ return (sat/1e8).toFixed(8).replace(/0+$/,'').replace(/\.$/,'')+" BTF"; } |
| 221 | function ts(t){ return new Date(t*1000).toISOString().replace('T',' ').replace('.000Z',' UTC'); } | 264 | function ts(t){ return new Date(t*1000).toISOString().replace('T',' ').replace('.000Z',' UTC'); } |
| @@ -310,25 +353,22 @@ function openBlock(h){ | |||
| 310 | })); | 353 | })); |
| 311 | append(tx, head, io); append(detail, tx); | 354 | append(tx, head, io); append(detail, tx); |
| 312 | }); | 355 | }); |
| 313 | document.getElementById('listview').style.display='none'; | 356 | document.getElementById('listview').hidden = true; |
| 314 | detail.style.display='block'; | 357 | detail.hidden = false; |
| 315 | document.getElementById('home').style.display='inline'; | 358 | document.getElementById('home').hidden = false; |
| 316 | window.scrollTo(0,0); | 359 | window.scrollTo(0,0); |
| 317 | }); | 360 | }); |
| 318 | } | 361 | } |
| 319 | document.getElementById('home').onclick=function(){ | 362 | document.getElementById('home').onclick=function(){ |
| 320 | document.getElementById('detail').style.display='none'; | 363 | document.getElementById('detail').hidden = true; |
| 321 | document.getElementById('listview').style.display='block'; | 364 | document.getElementById('listview').hidden = false; |
| 322 | this.style.display='none'; | 365 | this.hidden = true; |
| 323 | }; | 366 | }; |
| 324 | document.getElementById('q').oninput=function(){ renderList(this.value); }; | 367 | document.getElementById('q').oninput=function(){ renderList(this.value); }; |
| 325 | fetch('blocks.json').then(function(r){return r.json();}).then(function(d){ | 368 | fetch('blocks.json').then(function(r){return r.json();}).then(function(d){ |
| 326 | SUM=d.blocks; document.getElementById('tip').textContent='tip height '+d.tipHeight+' - '+d.count+' blocks'; | 369 | SUM=d.blocks; document.getElementById('tip').textContent='tip height '+d.tipHeight+' - '+d.count+' blocks'; |
| 327 | renderList(''); | 370 | renderList(''); |
| 328 | }); | 371 | }); |
| 329 | </script> | ||
| 330 | </body> | ||
| 331 | </html> | ||
| 332 | """ | 372 | """ |
| 333 | 373 | ||
| 334 | 374 | ||
diff --git a/scripts/make-release-checksums.sh b/scripts/make-release-checksums.sh index 5b29e2d..9c4f45a 100755 --- a/scripts/make-release-checksums.sh +++ b/scripts/make-release-checksums.sh | |||
| @@ -10,6 +10,7 @@ Usage: scripts/make-release-checksums.sh [--sign] [--local-user KEYID] | |||
| 10 | 10 | ||
| 11 | Creates SHA256SUMS for release assets in the current directory: | 11 | Creates SHA256SUMS for release assets in the current directory: |
| 12 | Bitflash-*-windows.zip | 12 | Bitflash-*-windows.zip |
| 13 | Bitflash-*-windows-with-tor.zip | ||
| 13 | Bitflash-*-x86_64.AppImage | 14 | Bitflash-*-x86_64.AppImage |
| 14 | bitflash-node-*-x86_64 | 15 | bitflash-node-*-x86_64 |
| 15 | 16 | ||
| @@ -44,6 +45,7 @@ fi | |||
| 44 | assets=() | 45 | assets=() |
| 45 | for pattern in \ | 46 | for pattern in \ |
| 46 | "Bitflash-*-windows.zip" \ | 47 | "Bitflash-*-windows.zip" \ |
| 48 | "Bitflash-*-windows-with-tor.zip" \ | ||
| 47 | "Bitflash-*-x86_64.AppImage" \ | 49 | "Bitflash-*-x86_64.AppImage" \ |
| 48 | "bitflash-node-*-x86_64" | 50 | "bitflash-node-*-x86_64" |
| 49 | do | 51 | do |
diff --git a/scripts/package-windows-tor.sh b/scripts/package-windows-tor.sh new file mode 100755 index 0000000..c8cca3b --- /dev/null +++ b/scripts/package-windows-tor.sh | |||
| @@ -0,0 +1,187 @@ | |||
| 1 | #!/usr/bin/env bash | ||
| 2 | set -euo pipefail | ||
| 3 | |||
| 4 | ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" | ||
| 5 | VERSION="${VERSION:-$(sed -n 's/^VERSION[[:space:]]*:= *//p' "$ROOT/Makefile" | head -1)}" | ||
| 6 | |||
| 7 | TOR_VERSION="${TOR_VERSION:-15.0.19}" | ||
| 8 | TOR_ARCHIVE_NAME="tor-expert-bundle-windows-x86_64-${TOR_VERSION}.tar.gz" | ||
| 9 | TOR_URL="${TOR_URL:-https://archive.torproject.org/tor-package-archive/torbrowser/${TOR_VERSION}/${TOR_ARCHIVE_NAME}}" | ||
| 10 | TOR_SHA256="${TOR_SHA256:-6AC067402C7B4A3DC37887ED3754B3914B67FDC220C966190683E9CCF91ABF0F}" | ||
| 11 | TOR_ASC_URL="${TOR_ASC_URL:-${TOR_URL}.asc}" | ||
| 12 | TOR_SIGNING_KEY_FINGERPRINT="${TOR_SIGNING_KEY_FINGERPRINT:-EF6E286DDA85EA2A4BA7DE684E2C6E8793298290}" | ||
| 13 | TOR_VERIFY_GPG="${TOR_VERIFY_GPG:-auto}" | ||
| 14 | |||
| 15 | BUNDLE_DIR="${BUNDLE_DIR:-$ROOT/Bitflash-${VERSION}-windows}" | ||
| 16 | OUT_ZIP="${OUT_ZIP:-$ROOT/Bitflash-${VERSION}-windows-with-tor.zip}" | ||
| 17 | CACHE_DIR="${CACHE_DIR:-$ROOT/.cache}" | ||
| 18 | ARCHIVE="${TOR_ARCHIVE:-$CACHE_DIR/$TOR_ARCHIVE_NAME}" | ||
| 19 | ASC_FILE="${TOR_ASC:-$ARCHIVE.asc}" | ||
| 20 | |||
| 21 | usage() { | ||
| 22 | cat <<EOF | ||
| 23 | Usage: scripts/package-windows-tor.sh | ||
| 24 | |||
| 25 | Creates Bitflash-${VERSION}-windows-with-tor.zip from an existing | ||
| 26 | Bitflash-${VERSION}-windows/ release directory. | ||
| 27 | |||
| 28 | Environment overrides: | ||
| 29 | VERSION Bitflash release version, default read from Makefile | ||
| 30 | TOR_VERSION Tor Expert Bundle version, default ${TOR_VERSION} | ||
| 31 | TOR_URL Download URL, default official Tor archive URL | ||
| 32 | TOR_ASC_URL Signature URL, default TOR_URL.asc | ||
| 33 | TOR_SHA256 Expected archive SHA256 | ||
| 34 | TOR_ARCHIVE Use an existing archive instead of downloading | ||
| 35 | TOR_ASC Use an existing detached signature | ||
| 36 | TOR_VERIFY_GPG auto, required, or off; default auto | ||
| 37 | BUNDLE_DIR Existing unpacked Windows release directory | ||
| 38 | OUT_ZIP Output zip path | ||
| 39 | EOF | ||
| 40 | } | ||
| 41 | |||
| 42 | if [ "${1:-}" = "-h" ] || [ "${1:-}" = "--help" ]; then | ||
| 43 | usage | ||
| 44 | exit 0 | ||
| 45 | fi | ||
| 46 | |||
| 47 | need() { | ||
| 48 | command -v "$1" >/dev/null 2>&1 || { | ||
| 49 | echo "missing required command: $1" >&2 | ||
| 50 | exit 1 | ||
| 51 | } | ||
| 52 | } | ||
| 53 | |||
| 54 | need sha256sum | ||
| 55 | need tar | ||
| 56 | need zip | ||
| 57 | |||
| 58 | download() { | ||
| 59 | local url="$1" | ||
| 60 | local out="$2" | ||
| 61 | if command -v curl >/dev/null 2>&1; then | ||
| 62 | curl -L --fail --output "$out" "$url" | ||
| 63 | elif command -v wget >/dev/null 2>&1; then | ||
| 64 | wget -O "$out" "$url" | ||
| 65 | else | ||
| 66 | echo "missing required command: curl or wget" >&2 | ||
| 67 | exit 1 | ||
| 68 | fi | ||
| 69 | } | ||
| 70 | |||
| 71 | if [ ! -d "$BUNDLE_DIR" ]; then | ||
| 72 | echo "missing Windows release directory: $BUNDLE_DIR" >&2 | ||
| 73 | echo "run 'make windows' first, or set BUNDLE_DIR" >&2 | ||
| 74 | exit 1 | ||
| 75 | fi | ||
| 76 | if [ ! -f "$BUNDLE_DIR/Bitflash.exe" ]; then | ||
| 77 | echo "missing Bitflash.exe in $BUNDLE_DIR" >&2 | ||
| 78 | exit 1 | ||
| 79 | fi | ||
| 80 | BUNDLE_PARENT="$(cd "$(dirname "$BUNDLE_DIR")" && pwd)" | ||
| 81 | BUNDLE_NAME="$(basename "$BUNDLE_DIR")" | ||
| 82 | OUT_PARENT="$(mkdir -p "$(dirname "$OUT_ZIP")" && cd "$(dirname "$OUT_ZIP")" && pwd)" | ||
| 83 | OUT_NAME="$(basename "$OUT_ZIP")" | ||
| 84 | OUT_ZIP_ABS="$OUT_PARENT/$OUT_NAME" | ||
| 85 | |||
| 86 | mkdir -p "$CACHE_DIR" | ||
| 87 | if [ ! -f "$ARCHIVE" ]; then | ||
| 88 | download "$TOR_URL" "$ARCHIVE" | ||
| 89 | fi | ||
| 90 | |||
| 91 | got="$(sha256sum "$ARCHIVE" | awk '{print tolower($1)}')" | ||
| 92 | want="$(printf '%s' "$TOR_SHA256" | tr 'A-F' 'a-f')" | ||
| 93 | if [ "$got" != "$want" ]; then | ||
| 94 | echo "Tor archive SHA256 mismatch" >&2 | ||
| 95 | echo " expected: $want" >&2 | ||
| 96 | echo " got: $got" >&2 | ||
| 97 | exit 1 | ||
| 98 | fi | ||
| 99 | |||
| 100 | tmp="$(mktemp -d "${TMPDIR:-/tmp}/bitflash-tor-package.XXXXXX")" | ||
| 101 | trap 'rm -rf "$tmp"' EXIT | ||
| 102 | |||
| 103 | case "$TOR_VERIFY_GPG" in | ||
| 104 | auto|required|off) ;; | ||
| 105 | *) echo "TOR_VERIFY_GPG must be auto, required, or off" >&2; exit 2 ;; | ||
| 106 | esac | ||
| 107 | |||
| 108 | if [ "$TOR_VERIFY_GPG" != "off" ]; then | ||
| 109 | if command -v gpg >/dev/null 2>&1; then | ||
| 110 | if [ ! -f "$ASC_FILE" ]; then | ||
| 111 | download "$TOR_ASC_URL" "$ASC_FILE" | ||
| 112 | fi | ||
| 113 | gnupg_home="$tmp/gnupg" | ||
| 114 | mkdir -p "$gnupg_home" | ||
| 115 | chmod 0700 "$gnupg_home" | ||
| 116 | gpg --homedir "$gnupg_home" --batch --auto-key-locate nodefault,wkd,keyserver \ | ||
| 117 | --locate-keys torbrowser@torproject.org >/dev/null 2>&1 || { | ||
| 118 | if [ "$TOR_VERIFY_GPG" = "required" ]; then | ||
| 119 | echo "could not import Tor Browser Developers signing key" >&2 | ||
| 120 | exit 1 | ||
| 121 | fi | ||
| 122 | echo "WARNING: skipped Tor GPG signature verification; signing key import failed" >&2 | ||
| 123 | } | ||
| 124 | if gpg --homedir "$gnupg_home" --batch --with-colons --fingerprint "$TOR_SIGNING_KEY_FINGERPRINT" \ | ||
| 125 | 2>/dev/null | grep -q "^fpr:::::::::${TOR_SIGNING_KEY_FINGERPRINT}:"; then | ||
| 126 | gpg --homedir "$gnupg_home" --batch --verify "$ASC_FILE" "$ARCHIVE" >/dev/null 2>&1 || { | ||
| 127 | echo "Tor archive GPG signature verification failed" >&2 | ||
| 128 | exit 1 | ||
| 129 | } | ||
| 130 | echo "Tor archive GPG signature verified against ${TOR_SIGNING_KEY_FINGERPRINT}" | ||
| 131 | elif [ "$TOR_VERIFY_GPG" = "required" ]; then | ||
| 132 | echo "Tor signing key fingerprint mismatch" >&2 | ||
| 133 | exit 1 | ||
| 134 | else | ||
| 135 | echo "WARNING: skipped Tor GPG signature verification; signing key fingerprint was not available" >&2 | ||
| 136 | fi | ||
| 137 | elif [ "$TOR_VERIFY_GPG" = "required" ]; then | ||
| 138 | echo "missing required command for TOR_VERIFY_GPG=required: gpg" >&2 | ||
| 139 | exit 1 | ||
| 140 | else | ||
| 141 | echo "WARNING: gpg not found; relying on pinned SHA256 only" >&2 | ||
| 142 | fi | ||
| 143 | fi | ||
| 144 | |||
| 145 | mkdir -p "$tmp/extract" | ||
| 146 | tar -xzf "$ARCHIVE" -C "$tmp/extract" | ||
| 147 | |||
| 148 | if [ ! -f "$tmp/extract/tor/tor.exe" ]; then | ||
| 149 | echo "Tor archive did not contain tor/tor.exe" >&2 | ||
| 150 | exit 1 | ||
| 151 | fi | ||
| 152 | |||
| 153 | rm -rf "$BUNDLE_DIR/tor" | ||
| 154 | mkdir -p "$BUNDLE_DIR/tor" | ||
| 155 | cp -R "$tmp/extract/tor/." "$BUNDLE_DIR/tor/" | ||
| 156 | if [ -d "$tmp/extract/docs" ]; then | ||
| 157 | mkdir -p "$BUNDLE_DIR/tor/docs" | ||
| 158 | cp -R "$tmp/extract/docs/." "$BUNDLE_DIR/tor/docs/" | ||
| 159 | fi | ||
| 160 | if [ -d "$tmp/extract/data" ]; then | ||
| 161 | mkdir -p "$BUNDLE_DIR/tor/data-defaults" | ||
| 162 | cp -R "$tmp/extract/data/." "$BUNDLE_DIR/tor/data-defaults/" | ||
| 163 | fi | ||
| 164 | |||
| 165 | cat > "$BUNDLE_DIR/TOR-EXPERT-BUNDLE.txt" <<EOF | ||
| 166 | Bitflash managed Tor bundle | ||
| 167 | |||
| 168 | This Windows package includes the Tor Expert Bundle so -managedtor can start | ||
| 169 | Tor without requiring a separate Tor install. | ||
| 170 | |||
| 171 | Tor Expert Bundle: ${TOR_VERSION} | ||
| 172 | Archive URL: ${TOR_URL} | ||
| 173 | Archive SHA256: ${TOR_SHA256} | ||
| 174 | |||
| 175 | Bitflash uses tor/tor.exe from this package. Runtime state, hidden-service keys, | ||
| 176 | and control authentication cookies are written under the user's Bitflash data | ||
| 177 | directory, not inside this release directory. | ||
| 178 | EOF | ||
| 179 | |||
| 180 | rm -f "$OUT_ZIP_ABS" | ||
| 181 | ( | ||
| 182 | cd "$BUNDLE_PARENT" | ||
| 183 | zip -qr "$OUT_ZIP_ABS" "$BUNDLE_NAME" | ||
| 184 | ) | ||
| 185 | |||
| 186 | echo "Bundled Tor Expert Bundle ${TOR_VERSION}" | ||
| 187 | echo "Built: $OUT_ZIP_ABS" | ||
diff --git a/scripts/smoke-managed-tor-onion.ps1 b/scripts/smoke-managed-tor-onion.ps1 new file mode 100644 index 0000000..70658c7 --- /dev/null +++ b/scripts/smoke-managed-tor-onion.ps1 | |||
| @@ -0,0 +1,174 @@ | |||
| 1 | param( | ||
| 2 | [string]$Bitflash, | ||
| 3 | [string]$Tor, | ||
| 4 | [string]$WorkDir, | ||
| 5 | [int]$TimeoutSec = 600, | ||
| 6 | [int]$OnionWarmupSec = 120 | ||
| 7 | ) | ||
| 8 | |||
| 9 | $ErrorActionPreference = "Stop" | ||
| 10 | |||
| 11 | function Resolve-RepoRoot { | ||
| 12 | $scriptDir = Split-Path -Parent $PSCommandPath | ||
| 13 | return (Resolve-Path (Join-Path $scriptDir "..")).Path | ||
| 14 | } | ||
| 15 | |||
| 16 | function Get-FreeTcpPort { | ||
| 17 | $listener = [Net.Sockets.TcpListener]::new([Net.IPAddress]::Loopback, 0) | ||
| 18 | $listener.Start() | ||
| 19 | try { | ||
| 20 | return ([Net.IPEndPoint]$listener.LocalEndpoint).Port | ||
| 21 | } | ||
| 22 | finally { | ||
| 23 | $listener.Stop() | ||
| 24 | } | ||
| 25 | } | ||
| 26 | |||
| 27 | function Wait-RegexInFile { | ||
| 28 | param( | ||
| 29 | [string]$Path, | ||
| 30 | [string]$Pattern, | ||
| 31 | [datetime]$Deadline, | ||
| 32 | [string]$Label | ||
| 33 | ) | ||
| 34 | |||
| 35 | while ((Get-Date) -lt $Deadline) { | ||
| 36 | if (Test-Path $Path) { | ||
| 37 | $text = Get-Content -Raw -ErrorAction SilentlyContinue $Path | ||
| 38 | if ($null -ne $text) { | ||
| 39 | $m = [regex]::Match($text, $Pattern) | ||
| 40 | if ($m.Success) { | ||
| 41 | return $m | ||
| 42 | } | ||
| 43 | } | ||
| 44 | } | ||
| 45 | Start-Sleep -Milliseconds 500 | ||
| 46 | } | ||
| 47 | throw "timed out waiting for $Label" | ||
| 48 | } | ||
| 49 | |||
| 50 | function Stop-TestProcesses { | ||
| 51 | param( | ||
| 52 | [System.Diagnostics.Process[]]$Processes, | ||
| 53 | [string[]]$DataDirKeys | ||
| 54 | ) | ||
| 55 | |||
| 56 | foreach ($p in $Processes) { | ||
| 57 | try { | ||
| 58 | $p.Refresh() | ||
| 59 | if (!$p.HasExited) { | ||
| 60 | Stop-Process -Id $p.Id -Force | ||
| 61 | } | ||
| 62 | } | ||
| 63 | catch {} | ||
| 64 | } | ||
| 65 | |||
| 66 | foreach ($key in $DataDirKeys) { | ||
| 67 | if ([string]::IsNullOrWhiteSpace($key)) { | ||
| 68 | continue | ||
| 69 | } | ||
| 70 | try { | ||
| 71 | Get-CimInstance Win32_Process -Filter "name = 'tor.exe'" | | ||
| 72 | Where-Object { $_.CommandLine -like "*$key*" } | | ||
| 73 | ForEach-Object { Stop-Process -Id $_.ProcessId -Force } | ||
| 74 | } | ||
| 75 | catch {} | ||
| 76 | } | ||
| 77 | } | ||
| 78 | |||
| 79 | $repo = Resolve-RepoRoot | ||
| 80 | if ([string]::IsNullOrWhiteSpace($Bitflash)) { | ||
| 81 | $Bitflash = Join-Path $repo "src\bitflash.exe" | ||
| 82 | } | ||
| 83 | if ([string]::IsNullOrWhiteSpace($Tor)) { | ||
| 84 | $candidates = @( | ||
| 85 | (Join-Path $repo "src\tor\tor.exe"), | ||
| 86 | (Join-Path $repo "Bitflash-$(Select-String -Path (Join-Path $repo 'Makefile') -Pattern '^VERSION[ ]*:=' | ForEach-Object { ($_.Line -split ':=')[1].Trim() })-windows\tor\tor.exe") | ||
| 87 | ) | ||
| 88 | foreach ($candidate in $candidates) { | ||
| 89 | if (Test-Path $candidate) { | ||
| 90 | $Tor = $candidate | ||
| 91 | break | ||
| 92 | } | ||
| 93 | } | ||
| 94 | } | ||
| 95 | if ([string]::IsNullOrWhiteSpace($WorkDir)) { | ||
| 96 | $WorkDir = Join-Path $env:TEMP ("bitflash-managed-tor-two-node-" + (Get-Date -Format "yyyyMMddHHmmss")) | ||
| 97 | } | ||
| 98 | |||
| 99 | if (!(Test-Path $Bitflash)) { | ||
| 100 | throw "Bitflash binary not found: $Bitflash" | ||
| 101 | } | ||
| 102 | if (!(Test-Path $Tor)) { | ||
| 103 | throw "Tor binary not found. Pass -Tor or build the Windows-with-Tor package." | ||
| 104 | } | ||
| 105 | |||
| 106 | New-Item -ItemType Directory -Force -Path $WorkDir | Out-Null | ||
| 107 | $dataA = Join-Path $WorkDir "node-a" | ||
| 108 | $dataB = Join-Path $WorkDir "node-b" | ||
| 109 | New-Item -ItemType Directory -Force -Path $dataA | Out-Null | ||
| 110 | New-Item -ItemType Directory -Force -Path $dataB | Out-Null | ||
| 111 | |||
| 112 | $portA = Get-FreeTcpPort | ||
| 113 | $portB = Get-FreeTcpPort | ||
| 114 | $deadline = (Get-Date).AddSeconds($TimeoutSec) | ||
| 115 | $oldPath = $env:PATH | ||
| 116 | $env:PATH = "C:\msys64\ucrt64\bin;C:\msys64\usr\bin;" + $env:PATH | ||
| 117 | |||
| 118 | $procA = $null | ||
| 119 | $procB = $null | ||
| 120 | $dataKeys = @((Split-Path $WorkDir -Leaf)) | ||
| 121 | |||
| 122 | try { | ||
| 123 | $argsA = @( | ||
| 124 | "-datadir=$dataA", | ||
| 125 | "-port=$portA", | ||
| 126 | "-managedtor=$Tor", | ||
| 127 | "-nogui", | ||
| 128 | "-debug", | ||
| 129 | "-checkblocks=0" | ||
| 130 | ) | ||
| 131 | $procA = Start-Process -FilePath $Bitflash -ArgumentList $argsA -WorkingDirectory (Split-Path $Bitflash -Parent) -PassThru -WindowStyle Hidden | ||
| 132 | $logA = Join-Path $dataA "debug.log" | ||
| 133 | |||
| 134 | $btfMatch = Wait-RegexInFile -Path $logA -Pattern "Nostr: node \.btf address = ([a-z2-7]+\.btf)" -Deadline $deadline -Label "node A .btf address" | ||
| 135 | $btfA = $btfMatch.Groups[1].Value | ||
| 136 | |||
| 137 | $onionMatch = Wait-RegexInFile -Path $logA -Pattern "Managed Tor hidden service ready: ([a-z2-7]{56}\.onion:\d+)" -Deadline $deadline -Label "node A managed onion" | ||
| 138 | $onionA = $onionMatch.Groups[1].Value | ||
| 139 | |||
| 140 | Wait-RegexInFile -Path $logA -Pattern "Nostr: \.btf self-resolve OK via .*" -Deadline $deadline -Label "node A descriptor self-resolve" | Out-Null | ||
| 141 | if ($OnionWarmupSec -gt 0) { | ||
| 142 | Write-Host "Waiting $OnionWarmupSec second(s) for Tor hidden-service descriptor propagation" | ||
| 143 | Start-Sleep -Seconds $OnionWarmupSec | ||
| 144 | } | ||
| 145 | |||
| 146 | $argsB = @( | ||
| 147 | "-datadir=$dataB", | ||
| 148 | "-port=$portB", | ||
| 149 | "-managedtor=$Tor", | ||
| 150 | "-oniononly", | ||
| 151 | "-connectbtf=$btfA", | ||
| 152 | "-nogui", | ||
| 153 | "-debug", | ||
| 154 | "-checkblocks=0" | ||
| 155 | ) | ||
| 156 | $procB = Start-Process -FilePath $Bitflash -ArgumentList $argsB -WorkingDirectory (Split-Path $Bitflash -Parent) -PassThru -WindowStyle Hidden | ||
| 157 | $logB = Join-Path $dataB "debug.log" | ||
| 158 | |||
| 159 | Wait-RegexInFile -Path $logB -Pattern ("connected " + [regex]::Escape($btfA) + " via direct onion ") -Deadline $deadline -Label "node B direct onion connection to node A" | Out-Null | ||
| 160 | |||
| 161 | Write-Host "Managed Tor two-node onion smoke test passed" | ||
| 162 | Write-Host " node A .btf: $btfA" | ||
| 163 | Write-Host " node A onion: $onionA" | ||
| 164 | Write-Host " node A port: $portA" | ||
| 165 | Write-Host " node B port: $portB" | ||
| 166 | Write-Host " work dir: $WorkDir" | ||
| 167 | } | ||
| 168 | finally { | ||
| 169 | $env:PATH = $oldPath | ||
| 170 | $procs = @() | ||
| 171 | if ($null -ne $procA) { $procs += $procA } | ||
| 172 | if ($null -ne $procB) { $procs += $procB } | ||
| 173 | Stop-TestProcesses -Processes $procs -DataDirKeys $dataKeys | ||
| 174 | } | ||