Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9bdb1b21b1 | |||
| 309f529218 | |||
| af55bfac80 | |||
| 372b252294 | |||
| 6c56e41e82 | |||
| 79b0c4a176 | |||
| 3db537d759 | |||
| 63be053b1d | |||
| d0fb2dc105 | |||
| bea3c4447c | |||
| 89a480a85a |
@@ -13,9 +13,13 @@ jobs:
|
|||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- name: Checkout (with history for submodule)
|
||||||
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Init submodules
|
||||||
|
run: git submodule update --init --recursive
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
@@ -55,9 +59,13 @@ jobs:
|
|||||||
# and BDB until they're fixed file-by-file.
|
# and BDB until they're fixed file-by-file.
|
||||||
SAN_FLAGS: "-fsanitize=address,undefined -fno-omit-frame-pointer -fno-sanitize-recover=undefined -fno-sanitize=alignment,signed-integer-overflow,vptr"
|
SAN_FLAGS: "-fsanitize=address,undefined -fno-omit-frame-pointer -fno-sanitize-recover=undefined -fno-sanitize=alignment,signed-integer-overflow,vptr"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- name: Checkout (with history for submodule)
|
||||||
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Init submodules
|
||||||
|
run: git submodule update --init --recursive
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
@@ -91,9 +99,14 @@ jobs:
|
|||||||
run:
|
run:
|
||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- name: Checkout (with history for submodule)
|
||||||
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Init submodules
|
||||||
|
run: git submodule update --init --recursive
|
||||||
|
shell: bash
|
||||||
|
|
||||||
- uses: msys2/setup-msys2@v2
|
- uses: msys2/setup-msys2@v2
|
||||||
with:
|
with:
|
||||||
@@ -244,9 +257,14 @@ jobs:
|
|||||||
run:
|
run:
|
||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- name: Checkout (with history for submodule)
|
||||||
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Init submodules
|
||||||
|
run: git submodule update --init --recursive
|
||||||
|
shell: bash
|
||||||
|
|
||||||
- uses: msys2/setup-msys2@v2
|
- uses: msys2/setup-msys2@v2
|
||||||
with:
|
with:
|
||||||
@@ -309,9 +327,13 @@ jobs:
|
|||||||
build-linux-qt:
|
build-linux-qt:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- name: Checkout (with history for submodule)
|
||||||
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Init submodules
|
||||||
|
run: git submodule update --init --recursive
|
||||||
|
|
||||||
- name: Set VERSION
|
- name: Set VERSION
|
||||||
run: |
|
run: |
|
||||||
@@ -428,9 +450,13 @@ jobs:
|
|||||||
build-linux-daemon:
|
build-linux-daemon:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- name: Checkout (with history for submodule)
|
||||||
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Init submodules
|
||||||
|
run: git submodule update --init --recursive
|
||||||
|
|
||||||
- name: Set VERSION
|
- name: Set VERSION
|
||||||
run: |
|
run: |
|
||||||
@@ -560,9 +586,13 @@ jobs:
|
|||||||
build-macos:
|
build-macos:
|
||||||
runs-on: macos-15
|
runs-on: macos-15
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- name: Checkout (with history for submodule)
|
||||||
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Init submodules
|
||||||
|
run: git submodule update --init --recursive
|
||||||
|
|
||||||
- name: Set VERSION
|
- name: Set VERSION
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -71,3 +71,10 @@ triangles.conf
|
|||||||
*.o
|
*.o
|
||||||
src/trianglesd
|
src/trianglesd
|
||||||
src/obj/
|
src/obj/
|
||||||
|
build-bench/
|
||||||
|
build-cmake/
|
||||||
|
build-cmake-test/
|
||||||
|
build-latest/
|
||||||
|
build-rocks-probe/
|
||||||
|
build-rocksdb/
|
||||||
|
bench-results.csv
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
Sami Ahmed <tweet@sami-ahmed.net> Sami <sami@dashcaddy.net>
|
||||||
|
Sami Ahmed <tweet@sami-ahmed.net> SamiAhmed7777 <sami@users.noreply.github.com>
|
||||||
|
Sami Ahmed <tweet@sami-ahmed.net> SamiAhmed7777 <79177212+SamiAhmed7777@users.noreply.github.com>
|
||||||
|
Sami Ahmed <tweet@sami-ahmed.net> Sami Ahmed <hello@sami-ahmed.net>
|
||||||
|
Sami Ahmed <tweet@sami-ahmed.net> Sami Ahmed <samiahmed7777@gmail.com>
|
||||||
+1
-1
@@ -6,7 +6,7 @@ if(POLICY CMP0167)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
project(Triangles
|
project(Triangles
|
||||||
VERSION 6.0.0
|
VERSION 6.0.1
|
||||||
DESCRIPTION "Cryptographic Triangles Wallet"
|
DESCRIPTION "Cryptographic Triangles Wallet"
|
||||||
LANGUAGES C CXX
|
LANGUAGES C CXX
|
||||||
)
|
)
|
||||||
|
|||||||
+159
@@ -0,0 +1,159 @@
|
|||||||
|
# TRI v6 Development Task Queue
|
||||||
|
|
||||||
|
*Autonomous development pipeline — Krystie cycles through these continuously.*
|
||||||
|
|
||||||
|
## Legend
|
||||||
|
- **P0** = Critical (chain broken / users blocked)
|
||||||
|
- **P1** = Important (v6 milestone)
|
||||||
|
- **P2** = Nice-to-have (polish / optimization)
|
||||||
|
- **Status**: TODO | IN-PROGRESS | DONE | BLOCKED
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## P0 — Immediate (Unblock Chain & Users)
|
||||||
|
|
||||||
|
### T001: Fix DNS2 RPC thread crash
|
||||||
|
- **Status**: TODO
|
||||||
|
- **Depends**: none
|
||||||
|
- **Description**: ThreadRPCServer exits on bad auth attempts from external IPs. Need to not kill the RPC thread on individual auth failures.
|
||||||
|
- **Files**: `src/rpc.cpp` or `src/bitcoinrpc.cpp`
|
||||||
|
- **Acceptance**: RPC stays up even with bad auth attempts; curl JSON-RPC works reliably
|
||||||
|
- **Model**: Claude Code or MiniMax M2.7
|
||||||
|
|
||||||
|
### T002: Fix DNS2 wallet 0 confirmed balance
|
||||||
|
- **Status**: TODO
|
||||||
|
- **Depends**: T001 (need reliable RPC)
|
||||||
|
- **Description**: Wallet restored from April 20 backup. Shows 11.24 TRI unconfirmed. Need to verify rescan completes and coins mature (520 confirmations) for staking.
|
||||||
|
- **Files**: wallet.dat, `src/wallet.cpp`
|
||||||
|
- **Acceptance**: Wallet shows confirmed balance after rescan + confirmations
|
||||||
|
- **Model**: Krystie (manual investigation, not subagent)
|
||||||
|
|
||||||
|
### T003: Fix seeds.txt parsing (only returns 1 address)
|
||||||
|
- **Status**: TODO
|
||||||
|
- **Depends**: none
|
||||||
|
- **Description**: HTTPS fetch of seeds.cryptographic-triangles.org/seeds.txt only returns 1 address. Possible comment parsing bug in net.cpp seed fetch logic.
|
||||||
|
- **Files**: `src/net.cpp`, `/var/www/seeds/seeds.txt`
|
||||||
|
- **Acceptance**: All 7 onion addresses returned on fetch
|
||||||
|
- **Model**: ZAI GLM-5.1
|
||||||
|
|
||||||
|
### T004: Fix Sami's PC wallet block 570 stall
|
||||||
|
- **Status**: IN-PROGRESS
|
||||||
|
- **Depends**: Windows binary build (DONE — built on sami-pc)
|
||||||
|
- **Description**: Windows Qt wallet stuck at block 570. GUI bootstrap fix committed (d0fb2dc). New binary built at E:\repos\triangles_v5\build-mingw\bin\triangles-qt.exe. Needs testing.
|
||||||
|
- **Acceptance**: Windows wallet syncs past block 570 with bootstrap
|
||||||
|
- **Model**: Krystie (manual deployment)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## P1 — v6 Core Milestones
|
||||||
|
|
||||||
|
### T010: Complete RocksDB runtime testing
|
||||||
|
- **Status**: TODO
|
||||||
|
- **Depends**: T001
|
||||||
|
- **Description**: RocksDB backend compiles clean but never tested with actual blockchain data. Need to: start daemon with `-rocksdb`, let it index chain, verify block lookups work, compare performance vs LevelDB.
|
||||||
|
- **Files**: `src/txdb.h`, `src/txdb.cpp`, `src/utxosnapshot.cpp`
|
||||||
|
- **Acceptance**: Daemon runs with `-rocksdb` flag, processes blocks, RPC queries return correct data
|
||||||
|
- **Model**: MiniMax M2.7
|
||||||
|
|
||||||
|
### T011: Wire UTXO snapshot P2P distribution (SnapshotNet)
|
||||||
|
- **Status**: TODO
|
||||||
|
- **Depends**: T010
|
||||||
|
- **Description**: `snapshotnet.cpp` exists but is placeholder. Need to implement: peer advertisement of snapshot availability, chunk transfer protocol, hash verification, integration with bootstrap flow.
|
||||||
|
- **Files**: `src/snapshotnet.cpp`, `src/net.cpp`, `src/utxosnapshot.cpp`
|
||||||
|
- **Acceptance**: New node can get UTXO snapshot from peers via P2P (not just HTTPS)
|
||||||
|
- **Model**: Claude Code + MiniMax M2.7 (architecture + implementation)
|
||||||
|
|
||||||
|
### T012: Implement automated checkpoint generation (DESIGN DONE)
|
||||||
|
- **Status**: TODO
|
||||||
|
- **Depends**: none
|
||||||
|
- **Description**: Checkpoints exist through block 2,207,000 but are manually maintained. Need automated checkpoint generation: every N blocks, compute checkpoint hash, push to code or external manifest.
|
||||||
|
- **Files**: `src/checkpoints.cpp`, `src/checkpoints.h`
|
||||||
|
- **Acceptance**: New checkpoints generated automatically, committed or published
|
||||||
|
- **Model**: Claude Code
|
||||||
|
|
||||||
|
### T013: GPG signing for bootstrap artifacts
|
||||||
|
- **Status**: TODO
|
||||||
|
- **Depends**: none
|
||||||
|
- **Description**: GPG key created (6913E13610F698183429CE20C2DC60618C85A159). Need to: sign every bootstrap/snapshot artifact on generation, verify signature on download, publish public key.
|
||||||
|
- **Files**: `/usr/local/bin/auto-update.sh`, `src/bootstrap.cpp`
|
||||||
|
- **Acceptance**: `gpg --verify` works on downloaded artifacts
|
||||||
|
- **Model**: ZAI GLM-5.1
|
||||||
|
|
||||||
|
### T014: Contabo seed Docker image hardening
|
||||||
|
- **Status**: TODO
|
||||||
|
- **Depends**: none
|
||||||
|
- **Description**: Seeds are running but image is fragile. Need: proper Dockerfile with version pinning, health checks, auto-restart, log shipping, and persistent volumes.
|
||||||
|
- **Files**: `/tmp/Dockerfile` on Contabo, `/tri/seed-{1..4}/`
|
||||||
|
- **Acceptance**: Seeds survive host reboot, auto-restart on crash, health check endpoint
|
||||||
|
- **Model**: ZAI GLM-5.1
|
||||||
|
|
||||||
|
### T015: Network health dashboard
|
||||||
|
- **Status**: TODO
|
||||||
|
- **Depends**: T001, T003
|
||||||
|
- **Description**: Operator-facing dashboard showing: block height per node, peer count, staking weight, chain sync status, seed health. Could be a simple web page served from DNS2.
|
||||||
|
- **Files**: New — `src/rpcblockchain.cpp` (health endpoint), frontend
|
||||||
|
- **Acceptance**: Live page showing all 7 nodes' status updated every 30s
|
||||||
|
- **Model**: MiniMax M2.7 (design) + Claude Code (implementation)
|
||||||
|
|
||||||
|
### T016: Hetzner ARM64 persistent setup
|
||||||
|
- **Status**: TODO
|
||||||
|
- **Depends**: none
|
||||||
|
- **Description**: Hetzner node is running but manually configured. Need: systemd service, auto-start on boot, bootstrap automation, monitoring.
|
||||||
|
- **Files**: systemd unit file on Hetzner
|
||||||
|
- **Acceptance**: Node survives reboot, auto-syncs, reports health
|
||||||
|
- **Model**: Krystie (manual, it's infra not code)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## P2 — Polish & Optimization
|
||||||
|
|
||||||
|
### T020: Remove unused Gemini/Google references from codebase
|
||||||
|
- **Status**: TODO
|
||||||
|
- **Depends**: none
|
||||||
|
- **Description**: Clean up any dead code, unused imports, stale comments referencing old architectures.
|
||||||
|
- **Model**: ZAI GLM-5.1
|
||||||
|
|
||||||
|
### T021: Comprehensive test suite
|
||||||
|
- **Status**: TODO
|
||||||
|
- **Depends**: T010
|
||||||
|
- **Description**: Expand test coverage for: UTXO snapshot load/dump, RocksDB backend, bootstrap download, seed fetch, checkpoint verification.
|
||||||
|
- **Files**: `src/test/`
|
||||||
|
- **Acceptance**: `test_triangles` passes with < 5 pre-existing failures
|
||||||
|
- **Model**: ZAI GLM-5.1 + MiniMax M2.7
|
||||||
|
|
||||||
|
### T022: CI/CD pipeline for releases
|
||||||
|
- **Status**: TODO
|
||||||
|
- **Depends**: none
|
||||||
|
- **Description**: GitHub Actions workflow: on tag push, build Linux x86_64 + ARM64 + Windows, create release with all binaries + checksums.
|
||||||
|
- **Files**: `.github/workflows/build-all.yml`
|
||||||
|
- **Acceptance**: Tag push produces release with 3 platform binaries
|
||||||
|
- **Model**: ZAI GLM-5.1
|
||||||
|
|
||||||
|
### T023: TRIdock + tri-wallet-web consolidation
|
||||||
|
- **Status**: TODO
|
||||||
|
- **Depends**: none
|
||||||
|
- **Description**: TRIdock and tri-wallet-web appear to be near-duplicates. Evaluate and either consolidate or clearly separate concerns.
|
||||||
|
- **Model**: MiniMax M2.7 (analysis)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Completed
|
||||||
|
|
||||||
|
### ✅ Windows GUI bootstrap fix (d0fb2dc)
|
||||||
|
- Removed `#ifndef QT_GUI` guard so auto-bootstrap runs in GUI wallet
|
||||||
|
- Added `uiInterface.InitMessage()` for progress display
|
||||||
|
|
||||||
|
### ✅ Windows native build on sami-pc
|
||||||
|
- Built `triangles-qt.exe` (26MB) and `trianglesd.exe` via MSYS2/MinGW64
|
||||||
|
- All dependencies found natively
|
||||||
|
|
||||||
|
### ✅ RocksDB integration complete (ac9c6fb)
|
||||||
|
- CActiveTxDB wrapper, dual-backend support, compiles clean
|
||||||
|
|
||||||
|
### ✅ All nodes updated to v5.9.7.0
|
||||||
|
- DNS2, DNS3, Hetzner, Contabo seeds all running latest
|
||||||
|
|
||||||
|
### ✅ Bootstrap infrastructure live
|
||||||
|
- HTTPS at bootstrap.cryptographic-triangles.org
|
||||||
|
- Tor hidden service serving nginx on port 8085
|
||||||
|
- Seeds.txt with 7 onion nodes
|
||||||
Executable
+210
@@ -0,0 +1,210 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Fresh-datadir IBD smoke test for TRI.
|
||||||
|
# Goal: detect the classic "starts from zero but stalls early / loops around 570"
|
||||||
|
# failure mode, and verify that sync keeps making forward progress.
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
# bash scripts/ibd-smoke-test.sh \
|
||||||
|
# --bin ./build/src/trianglesd \
|
||||||
|
# --bootstrap-url http://100.104.4.5:8085/triangles-bootstrap.tar.gz \
|
||||||
|
# --addnode 74.208.167.19 --addnode 194.233.88.206
|
||||||
|
|
||||||
|
BIN="${BIN:-./build/src/trianglesd}"
|
||||||
|
TIMEOUT_SECONDS="${TIMEOUT_SECONDS:-1800}" # 30 minutes target window
|
||||||
|
POLL_SECONDS="${POLL_SECONDS:-15}"
|
||||||
|
STALL_WINDOW_SECONDS="${STALL_WINDOW_SECONDS:-180}"
|
||||||
|
BOOTSTRAP_URL="${BOOTSTRAP_URL:-}"
|
||||||
|
WORKDIR="${WORKDIR:-}"
|
||||||
|
RPC_PORT="${RPC_PORT:-19192}"
|
||||||
|
P2P_PORT="${P2P_PORT:-24193}"
|
||||||
|
MIN_EXPECTED_HEIGHT="${MIN_EXPECTED_HEIGHT:-5000}"
|
||||||
|
ALLOW_IBD="${ALLOW_IBD:-0}"
|
||||||
|
WHITELIST="${WHITELIST:-127.0.0.1}"
|
||||||
|
ADDNODES=()
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<EOF
|
||||||
|
Usage: $0 [options]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--bin PATH trianglesd binary (default: $BIN)
|
||||||
|
--bootstrap-url URL optional bootstrap tar.gz URL to preload
|
||||||
|
--workdir PATH use an explicit temp workdir
|
||||||
|
--rpc-port N RPC port for test node (default: $RPC_PORT)
|
||||||
|
--p2p-port N P2P port for test node (default: $P2P_PORT)
|
||||||
|
--timeout N total test timeout seconds (default: $TIMEOUT_SECONDS)
|
||||||
|
--poll N poll interval seconds (default: $POLL_SECONDS)
|
||||||
|
--stall-window N no-progress failure window seconds (default: $STALL_WINDOW_SECONDS)
|
||||||
|
--min-height N minimum expected height/progress floor (default: $MIN_EXPECTED_HEIGHT)
|
||||||
|
--allow-ibd allow test to pass while still in IBD if progress is strong
|
||||||
|
--addnode HOST trusted peer to add (repeatable)
|
||||||
|
-h, --help show this help
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--bin) BIN="$2"; shift 2 ;;
|
||||||
|
--bootstrap-url) BOOTSTRAP_URL="$2"; shift 2 ;;
|
||||||
|
--workdir) WORKDIR="$2"; shift 2 ;;
|
||||||
|
--rpc-port) RPC_PORT="$2"; shift 2 ;;
|
||||||
|
--p2p-port) P2P_PORT="$2"; shift 2 ;;
|
||||||
|
--timeout) TIMEOUT_SECONDS="$2"; shift 2 ;;
|
||||||
|
--poll) POLL_SECONDS="$2"; shift 2 ;;
|
||||||
|
--stall-window) STALL_WINDOW_SECONDS="$2"; shift 2 ;;
|
||||||
|
--min-height) MIN_EXPECTED_HEIGHT="$2"; shift 2 ;;
|
||||||
|
--allow-ibd) ALLOW_IBD=1; shift ;;
|
||||||
|
--addnode) ADDNODES+=("$2"); shift 2 ;;
|
||||||
|
-h|--help) usage; exit 0 ;;
|
||||||
|
*) echo "unknown arg: $1" >&2; usage; exit 2 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ ! -x "$BIN" ]]; then
|
||||||
|
echo "ERROR: trianglesd binary not executable: $BIN" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$WORKDIR" ]]; then
|
||||||
|
WORKDIR="$(mktemp -d /tmp/tri-ibd-smoke-XXXXXX)"
|
||||||
|
fi
|
||||||
|
DATADIR="$WORKDIR/datadir"
|
||||||
|
mkdir -p "$DATADIR"
|
||||||
|
|
||||||
|
RPCUSER="tri_test"
|
||||||
|
RPCPASSWORD="tri_test_$(date +%s)_$RANDOM"
|
||||||
|
CONF="$DATADIR/triangles.conf"
|
||||||
|
cat > "$CONF" <<EOF
|
||||||
|
server=1
|
||||||
|
daemon=1
|
||||||
|
staking=0
|
||||||
|
listen=1
|
||||||
|
discover=0
|
||||||
|
upnp=0
|
||||||
|
tor=0
|
||||||
|
irc=0
|
||||||
|
dnsseed=1
|
||||||
|
checkpoints=1
|
||||||
|
rpcuser=$RPCUSER
|
||||||
|
rpcpassword=$RPCPASSWORD
|
||||||
|
rpcport=$RPC_PORT
|
||||||
|
port=$P2P_PORT
|
||||||
|
maxconnections=32
|
||||||
|
whitelist=$WHITELIST
|
||||||
|
logtimestamps=1
|
||||||
|
EOF
|
||||||
|
|
||||||
|
for host in "${ADDNODES[@]}"; do
|
||||||
|
echo "addnode=$host" >> "$CONF"
|
||||||
|
done
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
"$BIN" -datadir="$DATADIR" -conf="$CONF" stop >/dev/null 2>&1 || true
|
||||||
|
sleep 2 || true
|
||||||
|
pkill -f "$DATADIR" >/dev/null 2>&1 || true
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
if [[ -n "$BOOTSTRAP_URL" ]]; then
|
||||||
|
echo "[ibd-test] downloading bootstrap: $BOOTSTRAP_URL"
|
||||||
|
curl -L --fail --max-time 1800 "$BOOTSTRAP_URL" -o "$WORKDIR/bootstrap.tar.gz"
|
||||||
|
tar xzf "$WORKDIR/bootstrap.tar.gz" -C "$DATADIR"
|
||||||
|
rm -f "$DATADIR/database/log."* "$DATADIR/txleveldb/LOCK" "$DATADIR/smsgDB/LOCK" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[ibd-test] starting node from datadir: $DATADIR"
|
||||||
|
"$BIN" -daemon -datadir="$DATADIR" -conf="$CONF" >/dev/null
|
||||||
|
sleep 6
|
||||||
|
|
||||||
|
rpc() {
|
||||||
|
local method="$1"
|
||||||
|
local params="${2:-[]}"
|
||||||
|
curl -sS --fail --user "$RPCUSER:$RPCPASSWORD" \
|
||||||
|
--data-binary "{\"jsonrpc\":\"1.0\",\"id\":\"ibd\",\"method\":\"$method\",\"params\":$params}" \
|
||||||
|
-H 'content-type: text/plain;' "http://127.0.0.1:$RPC_PORT/"
|
||||||
|
}
|
||||||
|
|
||||||
|
extract_json() {
|
||||||
|
python3 -c 'import json,sys; obj=json.load(sys.stdin); print(obj["result"])'
|
||||||
|
}
|
||||||
|
|
||||||
|
extract_field() {
|
||||||
|
local field="$1"
|
||||||
|
python3 -c 'import json,sys; obj=json.load(sys.stdin); val=obj["result"].get(sys.argv[1]); print(val if val is not None else "")' "$field"
|
||||||
|
}
|
||||||
|
|
||||||
|
start_ts=$(date +%s)
|
||||||
|
last_progress_ts=$start_ts
|
||||||
|
last_height=-1
|
||||||
|
samples=0
|
||||||
|
same_570_loops=0
|
||||||
|
best_height=0
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
now=$(date +%s)
|
||||||
|
elapsed=$((now - start_ts))
|
||||||
|
if (( elapsed > TIMEOUT_SECONDS )); then
|
||||||
|
echo "FAIL: timeout after ${elapsed}s"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
|
||||||
|
if info_json="$(rpc getblockchaininfo 2>/dev/null)"; then
|
||||||
|
height=$(printf '%s' "$info_json" | extract_field blocks)
|
||||||
|
ibd=$(printf '%s' "$info_json" | extract_field initialblockdownload)
|
||||||
|
headers=$(printf '%s' "$info_json" | extract_field headers)
|
||||||
|
else
|
||||||
|
height=""
|
||||||
|
ibd=""
|
||||||
|
headers=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
peers=0
|
||||||
|
if peer_json="$(rpc getconnectioncount 2>/dev/null)"; then
|
||||||
|
peers=$(printf '%s' "$peer_json" | extract_json)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$height" && "$height" != "$last_height" ]]; then
|
||||||
|
last_progress_ts=$now
|
||||||
|
last_height="$height"
|
||||||
|
if (( height > best_height )); then
|
||||||
|
best_height=$height
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_file="$DATADIR/debug.log"
|
||||||
|
if [[ -f "$log_file" ]]; then
|
||||||
|
loop_hits=$(tail -n 400 "$log_file" | grep -c 'start=571' || true)
|
||||||
|
if (( loop_hits >= 3 )); then
|
||||||
|
same_570_loops=$loop_hits
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[ibd-test] t=${elapsed}s height=${height:-?} headers=${headers:-?} ibd=${ibd:-?} peers=$peers best=$best_height"
|
||||||
|
|
||||||
|
if [[ -n "$height" ]] && (( best_height >= MIN_EXPECTED_HEIGHT )) && [[ "$ibd" == "False" || "$ibd" == "false" ]]; then
|
||||||
|
echo "PASS: left IBD and reached height $best_height"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$ALLOW_IBD" == "1" && -n "$height" ]] && (( best_height >= MIN_EXPECTED_HEIGHT )); then
|
||||||
|
echo "PASS: strong sync progress observed (height $best_height) even though IBD remains true"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (( now - last_progress_ts > STALL_WINDOW_SECONDS )); then
|
||||||
|
echo "FAIL: no block-height progress for $((now - last_progress_ts))s"
|
||||||
|
if (( same_570_loops > 0 )); then
|
||||||
|
echo "HINT: detected repeated start=571 loop pattern ($same_570_loops hits in recent log tail)"
|
||||||
|
fi
|
||||||
|
echo "--- debug tail ---"
|
||||||
|
tail -n 120 "$log_file" 2>/dev/null || true
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
((samples++)) || true
|
||||||
|
sleep "$POLL_SECONDS"
|
||||||
|
done
|
||||||
|
|
||||||
|
exit 1
|
||||||
+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 6
|
#define CLIENT_VERSION_MAJOR 6
|
||||||
#define CLIENT_VERSION_MINOR 0
|
#define CLIENT_VERSION_MINOR 0
|
||||||
#define CLIENT_VERSION_REVISION 0
|
#define CLIENT_VERSION_REVISION 1
|
||||||
#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.
|
||||||
|
|||||||
+25
-4
@@ -417,7 +417,7 @@ std::string HelpMessage()
|
|||||||
//" -proxy=<ip:port> " + _("Connect through socks proxy") + "\n" +
|
//" -proxy=<ip:port> " + _("Connect through socks proxy") + "\n" +
|
||||||
//" -socks=<n> " + _("Select the version of socks proxy to use (4-5, default: 5)") + "\n" +
|
//" -socks=<n> " + _("Select the version of socks proxy to use (4-5, default: 5)") + "\n" +
|
||||||
" -tor=<ip:port> " + _("Use proxy to reach tor hidden services (default: same as -proxy)") + "\n"
|
" -tor=<ip:port> " + _("Use proxy to reach tor hidden services (default: same as -proxy)") + "\n"
|
||||||
" -notor " + _("Disable Tor (WARNING: wallet will not start - Tor is required)") + "\n" +
|
" -notor " + _("Disable Tor - run in clearnet-only mode (no .onion connectivity)") + "\n" +
|
||||||
" -torsocks=<port> " + _("Set embedded or managed Tor SOCKS proxy port (default: 19099)") + "\n" +
|
" -torsocks=<port> " + _("Set embedded or managed Tor SOCKS proxy port (default: 19099)") + "\n" +
|
||||||
" -torhiddenservice " + _("Enable the managed Tor hidden service (default: 1)") + "\n" +
|
" -torhiddenservice " + _("Enable the managed Tor hidden service (default: 1)") + "\n" +
|
||||||
" -torhsport=<port> " + _("Set embedded or managed Tor hidden service port (default: wallet listen port)") + "\n" +
|
" -torhsport=<port> " + _("Set embedded or managed Tor hidden service port (default: wallet listen port)") + "\n" +
|
||||||
@@ -927,7 +927,8 @@ bool AppInit2()
|
|||||||
// v5.9.5: P2P UTXO snapshot fetch is the default for fresh installs (Step 11.6).
|
// v5.9.5: P2P UTXO snapshot fetch is the default for fresh installs (Step 11.6).
|
||||||
// The legacy clearnet HTTP bootstrap only runs when the user explicitly requests
|
// The legacy clearnet HTTP bootstrap only runs when the user explicitly requests
|
||||||
// it via -bootstrap, or when -snapshot=0 disables the P2P fetcher.
|
// it via -bootstrap, or when -snapshot=0 disables the P2P fetcher.
|
||||||
#ifndef QT_GUI
|
// Bootstrap auto-download works for both GUI and daemon.
|
||||||
|
// GUI users get the same automatic bootstrap on fresh installs.
|
||||||
{
|
{
|
||||||
bool wantsBootstrap = GetBoolArg("-bootstrap", false);
|
bool wantsBootstrap = GetBoolArg("-bootstrap", false);
|
||||||
bool noBootstrap = GetBoolArg("-nobootstrap", false);
|
bool noBootstrap = GetBoolArg("-nobootstrap", false);
|
||||||
@@ -938,6 +939,7 @@ bool AppInit2()
|
|||||||
if (needsBootstrap && !noBootstrap && !snapshotMode) {
|
if (needsBootstrap && !noBootstrap && !snapshotMode) {
|
||||||
printf("Bootstrap: no blockchain data found — downloading automatically.\n");
|
printf("Bootstrap: no blockchain data found — downloading automatically.\n");
|
||||||
printf("Bootstrap: (use -nobootstrap to skip)\n");
|
printf("Bootstrap: (use -nobootstrap to skip)\n");
|
||||||
|
uiInterface.InitMessage(_("Downloading blockchain data..."));
|
||||||
wantsBootstrap = true;
|
wantsBootstrap = true;
|
||||||
} else if (needsBootstrap && snapshotMode && !wantsBootstrap) {
|
} else if (needsBootstrap && snapshotMode && !wantsBootstrap) {
|
||||||
printf("Bootstrap: no blockchain data found — will fetch UTXO snapshot via P2P after network start.\n");
|
printf("Bootstrap: no blockchain data found — will fetch UTXO snapshot via P2P after network start.\n");
|
||||||
@@ -951,13 +953,24 @@ bool AppInit2()
|
|||||||
std::string host = Bootstrap::DEFAULT_HOST;
|
std::string host = Bootstrap::DEFAULT_HOST;
|
||||||
std::string strError;
|
std::string strError;
|
||||||
|
|
||||||
auto progressFn = [](int64_t bytesDownloaded, int64_t totalBytes) {
|
int64_t lastGuiUpdate = 0;
|
||||||
|
auto progressFn = [&lastGuiUpdate](int64_t bytesDownloaded, int64_t totalBytes) {
|
||||||
if (totalBytes > 0) {
|
if (totalBytes > 0) {
|
||||||
printf("\rBootstrap: %lld / %lld MB (%lld%%)",
|
printf("\rBootstrap: %lld / %lld MB (%lld%%)",
|
||||||
(long long)(bytesDownloaded / (1024*1024)),
|
(long long)(bytesDownloaded / (1024*1024)),
|
||||||
(long long)(totalBytes / (1024*1024)),
|
(long long)(totalBytes / (1024*1024)),
|
||||||
(long long)((bytesDownloaded * 100) / totalBytes));
|
(long long)((bytesDownloaded * 100) / totalBytes));
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
|
// Update GUI status bar every ~1 MB
|
||||||
|
int64_t now = GetTimeMillis();
|
||||||
|
if (now - lastGuiUpdate > 1000) {
|
||||||
|
lastGuiUpdate = now;
|
||||||
|
std::string msg = strprintf("Downloading blockchain: %lld / %lld MB (%lld%%)",
|
||||||
|
(long long)(bytesDownloaded / (1024*1024)),
|
||||||
|
(long long)(totalBytes / (1024*1024)),
|
||||||
|
(long long)((bytesDownloaded * 100) / totalBytes));
|
||||||
|
uiInterface.InitMessage(msg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -999,7 +1012,6 @@ bool AppInit2()
|
|||||||
strprintf("host=%s success=%d utxo_snapshot=%d", host.c_str(), success, triedUtxoSnapshot));
|
strprintf("host=%s success=%d utxo_snapshot=%d", host.c_str(), success, triedUtxoSnapshot));
|
||||||
}
|
}
|
||||||
} // end bootstrap scope
|
} // end bootstrap scope
|
||||||
#endif
|
|
||||||
|
|
||||||
// ********************************************************* Step 6c: manual UTXO snapshot loading
|
// ********************************************************* Step 6c: manual UTXO snapshot loading
|
||||||
// If utxo-snapshot.bin exists in data dir and the chain DB hasn't been
|
// If utxo-snapshot.bin exists in data dir and the chain DB hasn't been
|
||||||
@@ -1343,6 +1355,15 @@ bool AppInit2()
|
|||||||
#ifdef USE_UPNP
|
#ifdef USE_UPNP
|
||||||
fUseUPnP = false;
|
fUseUPnP = false;
|
||||||
#endif
|
#endif
|
||||||
|
} else if (GetBoolArg("-notor", false)) {
|
||||||
|
// -notor: user explicitly disabled Tor. Allow the daemon to start
|
||||||
|
// in clearnet-only mode (useful for diagnostics, benchmarking, and
|
||||||
|
// recovery). .onion connectivity will not be available.
|
||||||
|
printf("NOTICE: Tor disabled via -notor. Running in clearnet-only mode.\n");
|
||||||
|
printf(" .onion connections will NOT be available.\n");
|
||||||
|
SetReachable(NET_IPV4, true);
|
||||||
|
SetReachable(NET_IPV6, true);
|
||||||
|
SetReachable(NET_TOR, false);
|
||||||
} else {
|
} else {
|
||||||
std::string torError = CTorEmbedded::GetInstance()->GetStartupError();
|
std::string torError = CTorEmbedded::GetInstance()->GetStartupError();
|
||||||
if (torError.empty())
|
if (torError.empty())
|
||||||
|
|||||||
+28
-4
@@ -259,10 +259,34 @@ bool CTorProcess::WriteTorrc()
|
|||||||
// SOCKS proxy for wallet connections
|
// SOCKS proxy for wallet connections
|
||||||
torrc << "SocksPort " << socksPort << "\n";
|
torrc << "SocksPort " << socksPort << "\n";
|
||||||
|
|
||||||
// Data directory for Tor state
|
// Data directory for Tor state.
|
||||||
fs::path torStateDir = dataPath / "state";
|
// Use the tor_data directory itself as DataDirectory so that Tor creates
|
||||||
fs::create_directories(torStateDir);
|
// its internal 'state' FILE at <tor_data>/state. Older wallet builds
|
||||||
torrc << "DataDirectory " << torStateDir.string() << "\n";
|
// erroneously created a subdirectory called 'state' and pointed
|
||||||
|
// DataDirectory at it; newer Tor versions (0.4.9+) reject that because
|
||||||
|
// they expect to write a plain file called 'state' inside DataDirectory.
|
||||||
|
//
|
||||||
|
// Recovery: if 'state' exists as a directory, move its contents up and
|
||||||
|
// remove it so that Tor can create its state file in the normal location.
|
||||||
|
{
|
||||||
|
fs::path badStateDir = dataPath / "state";
|
||||||
|
if (fs::exists(badStateDir) && fs::is_directory(badStateDir)) {
|
||||||
|
// Migrate any files inside the bad 'state/' directory up to dataPath
|
||||||
|
try {
|
||||||
|
for (auto& entry : fs::directory_iterator(badStateDir)) {
|
||||||
|
fs::path dest = dataPath / entry.path().filename();
|
||||||
|
if (!fs::exists(dest)) {
|
||||||
|
fs::rename(entry.path(), dest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fs::remove(badStateDir);
|
||||||
|
printf("Auto-recovered: removed legacy 'state' directory from %s\n", dataPath.string().c_str());
|
||||||
|
} catch (const fs::filesystem_error& e) {
|
||||||
|
printf("WARNING: Could not auto-recover tor_data/state directory: %s\n", e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
torrc << "DataDirectory " << dataPath.string() << "\n";
|
||||||
|
|
||||||
// Persistent Tor log for post-mortem debugging on user machines.
|
// Persistent Tor log for post-mortem debugging on user machines.
|
||||||
fs::path torLogPath = dataPath / "tor.log";
|
fs::path torLogPath = dataPath / "tor.log";
|
||||||
|
|||||||
+66
-34
@@ -546,10 +546,17 @@ int ReadHTTP(std::basic_istream<char>& stream, map<string, string>& mapHeadersRe
|
|||||||
bool HTTPAuthorized(map<string, string>& mapHeaders)
|
bool HTTPAuthorized(map<string, string>& mapHeaders)
|
||||||
{
|
{
|
||||||
string strAuth = mapHeaders["authorization"];
|
string strAuth = mapHeaders["authorization"];
|
||||||
if (strAuth.substr(0,6) != "Basic ")
|
if (strAuth.size() < 6 || strAuth.substr(0,6) != "Basic ")
|
||||||
return false;
|
return false;
|
||||||
string strUserPass64 = strAuth.substr(6); strUserPass64 = TrimString(strUserPass64);
|
string strUserPass64 = strAuth.substr(6); strUserPass64 = TrimString(strUserPass64);
|
||||||
string strUserPass = DecodeBase64(strUserPass64);
|
if (strUserPass64.empty())
|
||||||
|
return false;
|
||||||
|
string strUserPass;
|
||||||
|
try {
|
||||||
|
strUserPass = DecodeBase64(strUserPass64);
|
||||||
|
} catch (const std::exception&) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return TimingResistantEqual(strUserPass, strRPCUserColonPass);
|
return TimingResistantEqual(strUserPass, strRPCUserColonPass);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -783,44 +790,61 @@ static void RPCAcceptHandler(boost::shared_ptr< basic_socket_acceptor<Protocol,
|
|||||||
{
|
{
|
||||||
vnThreadsRunning[THREAD_RPCLISTENER]++;
|
vnThreadsRunning[THREAD_RPCLISTENER]++;
|
||||||
|
|
||||||
// Immediately start accepting new connections, except when we're cancelled or our socket is closed.
|
try {
|
||||||
if (error != asio::error::operation_aborted
|
// Immediately start accepting new connections, except when we're cancelled or our socket is closed.
|
||||||
&& acceptor->is_open())
|
if (error != asio::error::operation_aborted
|
||||||
RPCListen(acceptor, context, fUseSSL);
|
&& acceptor->is_open())
|
||||||
|
RPCListen(acceptor, context, fUseSSL);
|
||||||
|
|
||||||
AcceptedConnectionImpl<ip::tcp>* tcp_conn = dynamic_cast< AcceptedConnectionImpl<ip::tcp>* >(conn);
|
AcceptedConnectionImpl<ip::tcp>* tcp_conn = dynamic_cast< AcceptedConnectionImpl<ip::tcp>* >(conn);
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
{
|
{
|
||||||
if (error != asio::error::operation_aborted)
|
if (error != asio::error::operation_aborted)
|
||||||
printf("RPC accept error from %s: %s (%d)\n",
|
printf("RPC accept error from %s: %s (%d)\n",
|
||||||
tcp_conn ? tcp_conn->peer.address().to_string().c_str() : "unknown peer",
|
tcp_conn ? tcp_conn->peer.address().to_string().c_str() : "unknown peer",
|
||||||
error.message().c_str(),
|
error.message().c_str(),
|
||||||
error.value());
|
error.value());
|
||||||
|
delete conn;
|
||||||
|
vnThreadsRunning[THREAD_RPCLISTENER]--;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restrict callers by IP. It is important to
|
||||||
|
// do this before starting client thread, to filter out
|
||||||
|
// certain DoS and misbehaving clients.
|
||||||
|
else if (tcp_conn
|
||||||
|
&& !ClientAllowed(tcp_conn->peer.address()))
|
||||||
|
{
|
||||||
|
// Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake.
|
||||||
|
try {
|
||||||
|
if (!fUseSSL)
|
||||||
|
conn->stream() << HTTPReply(HTTP_FORBIDDEN, "", false) << std::flush;
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
printf("RPC error sending 403 to %s: %s\n",
|
||||||
|
tcp_conn->peer.address().to_string().c_str(), e.what());
|
||||||
|
}
|
||||||
|
delete conn;
|
||||||
|
vnThreadsRunning[THREAD_RPCLISTENER]--;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// start HTTP client thread
|
||||||
|
else if (!NewThread(ThreadRPCServer3, conn)) {
|
||||||
|
printf("Failed to create RPC server client thread\n");
|
||||||
|
delete conn;
|
||||||
|
}
|
||||||
|
|
||||||
|
vnThreadsRunning[THREAD_RPCLISTENER]--;
|
||||||
|
} catch (std::exception& e) {
|
||||||
|
PrintException(&e, "RPCAcceptHandler()");
|
||||||
delete conn;
|
delete conn;
|
||||||
vnThreadsRunning[THREAD_RPCLISTENER]--;
|
vnThreadsRunning[THREAD_RPCLISTENER]--;
|
||||||
return;
|
} catch (...) {
|
||||||
}
|
PrintException(NULL, "RPCAcceptHandler()");
|
||||||
|
|
||||||
// Restrict callers by IP. It is important to
|
|
||||||
// do this before starting client thread, to filter out
|
|
||||||
// certain DoS and misbehaving clients.
|
|
||||||
else if (tcp_conn
|
|
||||||
&& !ClientAllowed(tcp_conn->peer.address()))
|
|
||||||
{
|
|
||||||
// Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake.
|
|
||||||
if (!fUseSSL)
|
|
||||||
conn->stream() << HTTPReply(HTTP_FORBIDDEN, "", false) << std::flush;
|
|
||||||
delete conn;
|
delete conn;
|
||||||
|
vnThreadsRunning[THREAD_RPCLISTENER]--;
|
||||||
}
|
}
|
||||||
|
|
||||||
// start HTTP client thread
|
|
||||||
else if (!NewThread(ThreadRPCServer3, conn)) {
|
|
||||||
printf("Failed to create RPC server client thread\n");
|
|
||||||
delete conn;
|
|
||||||
}
|
|
||||||
|
|
||||||
vnThreadsRunning[THREAD_RPCLISTENER]--;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThreadRPCServer2(void* parg)
|
void ThreadRPCServer2(void* parg)
|
||||||
@@ -1128,6 +1152,7 @@ void ThreadRPCServer3(void* parg)
|
|||||||
AcceptedConnection *conn = (AcceptedConnection *) parg;
|
AcceptedConnection *conn = (AcceptedConnection *) parg;
|
||||||
|
|
||||||
bool fRun = true;
|
bool fRun = true;
|
||||||
|
try {
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
if (fShutdown || !fRun)
|
if (fShutdown || !fRun)
|
||||||
@@ -1247,6 +1272,13 @@ void ThreadRPCServer3(void* parg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // end try
|
||||||
|
catch (std::exception& e) {
|
||||||
|
PrintException(&e, "ThreadRPCServer3()");
|
||||||
|
} catch (...) {
|
||||||
|
PrintException(NULL, "ThreadRPCServer3()");
|
||||||
|
}
|
||||||
|
|
||||||
delete conn;
|
delete conn;
|
||||||
{
|
{
|
||||||
LOCK(cs_THREAD_RPCHANDLER);
|
LOCK(cs_THREAD_RPCHANDLER);
|
||||||
|
|||||||
Reference in New Issue
Block a user