Compare commits
193 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0df054bbcb | |||
| fbd931a392 | |||
| a792f90489 | |||
| 64939a9793 | |||
| b506a48192 | |||
| dee0d9ef62 | |||
| 22e220acaa | |||
| 1c068f4782 | |||
| a671708f0b | |||
| be90d39cd4 | |||
| 4d0478add5 | |||
| 734979c93b | |||
| 00af636aca | |||
| 9377b3a52f | |||
| 1881ff867e | |||
| caddfb1789 | |||
| 6eb25d6b25 | |||
| cd7b68f7cb | |||
| a0e8e74d0d | |||
| 7d62e34868 | |||
| b0e9ca334f | |||
| 029f5a4bfc | |||
| 0d6e143398 | |||
| 310a2b7371 | |||
| 9acff4bb43 | |||
| 97dbc13b62 | |||
| edf029e403 | |||
| b41d1be128 | |||
| 94df26a0e0 | |||
| d10ca379a7 | |||
| f5c0f53377 | |||
| ada278cb9f | |||
| 3579f98033 | |||
| f5a0bf1727 | |||
| 47a5ec1e38 | |||
| 56351ffb89 | |||
| 334b525fe6 | |||
| 76ec2da20d | |||
| 1e276da344 | |||
| 412ca94a25 | |||
| 23dc7992e5 | |||
| 46f719162b | |||
| e1a3eae0a3 | |||
| 57aaa1dcc6 | |||
| 48d84d40c5 | |||
| aa1251f4fa | |||
| d9deaf509b | |||
| 104778fa61 | |||
| 1b416ae704 | |||
| 0a0129cbcc | |||
| 6a124cb411 | |||
| e0e38d50ac | |||
| 5b21f7cc1f | |||
| 5f1c84255b | |||
| ed1ae79822 | |||
| eb21b8c87b | |||
| f7eec5c138 | |||
| a8d0e291c3 | |||
| d6839164dd | |||
| 6fc31fec60 | |||
| f950eb58ad | |||
| f3d5c677a0 | |||
| 26276ef92b | |||
| 1bcaf6b615 | |||
| b339ede17a | |||
| 74d7666398 | |||
| 62f7d6457c | |||
| 730466e54e | |||
| b857257516 | |||
| e8bf45af00 | |||
| 012bc344f5 | |||
| 80a39fa1de | |||
| 0b8b693d7d | |||
| 14abcc9746 | |||
| 74b11e7404 | |||
| 096a4f9927 | |||
| 585ccd4a07 | |||
| c4949a6d4f | |||
| db77184277 | |||
| 557d5807d8 | |||
| 2413983dae | |||
| 014947580b | |||
| ca8baab501 | |||
| 64db028788 | |||
| 2c4c8370df | |||
| e1ef89a169 | |||
| bb4414804b | |||
| 6a9b710b18 | |||
| 050558435f | |||
| 22e888dd47 | |||
| 35c4a2c823 | |||
| d8fb2b7d7d | |||
| 89dad5818f | |||
| f5a5ebb204 | |||
| 999ffea314 | |||
| 42a33457bf | |||
| 098f27368f | |||
| 279d643582 | |||
| 3dfecf5cb7 | |||
| baa38340a6 | |||
| cf4851bede | |||
| fb4c0708bf | |||
| 2c3a2f983c | |||
| cfaf742053 | |||
| fbd498ad8e | |||
| 308f8a5f5c | |||
| 999726730b | |||
| 069f42d6d0 | |||
| d903ef2fc7 | |||
| 6c87931901 | |||
| 35369995fb | |||
| b31d8d08dd | |||
| 4be0101f2d | |||
| e3705a66b8 | |||
| 7c79dc5ae8 | |||
| f0a2c0e237 | |||
| 2f9841b0e3 | |||
| 7120df3989 | |||
| e7e2d8443e | |||
| a031795aea | |||
| 352dda5645 | |||
| 3eb436bcd2 | |||
| daf47d2fab | |||
| 6ca0770d72 | |||
| 64e132b34b | |||
| 7e8ae1a25b | |||
| 7ca970d998 | |||
| 552809e359 | |||
| c2e5cf4330 | |||
| 099f78efea | |||
| 6c5ce18459 | |||
| 91e026d7ec | |||
| 1011cf84fe | |||
| ad0088ef3a | |||
| 1f0b83f893 | |||
| 4605cb4b70 | |||
| 7b021a65c0 | |||
| 19ea33b706 | |||
| 37b69f45ea | |||
| 8203f97eeb | |||
| 3e19c1b232 | |||
| b34f7e5ebd | |||
| fea90d1f0a | |||
| 96bf97a3b3 | |||
| 3fdccf2b14 | |||
| 0b53c21eaf | |||
| 2c77ebb122 | |||
| 2b79d8a6f9 | |||
| a81570499b | |||
| 853efe3ad9 | |||
| 8cab86518c | |||
| 9a3b643a5a | |||
| 40d281c1ff | |||
| dd72957646 | |||
| 97577a677c | |||
| cec7ca2e2e | |||
| a82bf999d2 | |||
| 52203b6003 | |||
| 76775cc697 | |||
| 1911724373 | |||
| 790cbd1cea | |||
| 91203c4ef4 | |||
| 2e8f3f5194 | |||
| a3b479d954 | |||
| 9ca61bef47 | |||
| 2e4bca9493 | |||
| f54d456920 | |||
| 07b90a53de | |||
| 9f69ead715 | |||
| 3be4d18b81 | |||
| b7b9c13bfa | |||
| b6e0fc62a9 | |||
| 420630168b | |||
| 5f599a72da | |||
| c475f38b85 | |||
| d655d9ed70 | |||
| efb4455fa5 | |||
| e4d519711f | |||
| 5799125d5a | |||
| 3a9d845e94 | |||
| 6779662ec1 | |||
| 3fd9fe70eb | |||
| 1abe33c482 | |||
| c74d0dc0ee | |||
| 918e5bf5a2 | |||
| 5ea26a94ff | |||
| dbe22a8383 | |||
| 67a5d19ec1 | |||
| f07f7f902a | |||
| 158b2bcd2d | |||
| 1ede9babf6 | |||
| 104d496e77 | |||
| 76d128917a |
@@ -1,171 +0,0 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(git clone:*)",
|
||||
"Bash(git init:*)",
|
||||
"Bash(git remote add:*)",
|
||||
"Bash(git fetch:*)",
|
||||
"Bash(git checkout:*)",
|
||||
"Bash(git config:*)",
|
||||
"Bash(git -C \"E:\\\\repos\\\\triangles_old\" log --oneline --all)",
|
||||
"Bash(git -C \"E:\\\\repos\\\\triangles_old\" branch -a)",
|
||||
"Bash(git -C \"E:\\\\repos\\\\triangles_old\" log --oneline --all --graph)",
|
||||
"Bash(git -C \"E:\\\\repos\\\\triangles_old\" show 7676e66 --stat)",
|
||||
"Bash(python:*)",
|
||||
"Bash(where:*)",
|
||||
"Bash(powershell:*)",
|
||||
"Bash(C:/msys64/usr/bin/bash.exe -lc \"pacman -Syu --noconfirm\")",
|
||||
"Bash(C:/msys64/usr/bin/bash.exe -lc \"pacman -S --needed --noconfirm mingw-w64-x86_64-toolchain make\")",
|
||||
"Bash(C:/msys64/msys2_shell.cmd -mingw64 -defterm -no-start -here -c \"ls /mingw64/lib/libboost_system*.a 2>/dev/null; ls /mingw64/lib/cmake/boost_system* 2>/dev/null; ls /mingw64/lib/libboost*.a 2>/dev/null | head -10\")",
|
||||
"Bash(C:/msys64/msys2_shell.cmd -mingw64 -defterm -no-start -here -c \"cd /c/Qt/deps/openssl-1.0.2u && make -j4 2>&1 | tail -10\")",
|
||||
"Bash(C:/msys64/msys2_shell.cmd -mingw64 -defterm -no-start -here -c \"cd /e/repos/triangles && mingw32-make -j4 2>&1 | tail -40\")",
|
||||
"Bash(C:/msys64/msys2_shell.cmd -mingw64 -defterm -no-start -here -c \"cd /e/repos/triangles && mingw32-make -j1 2>&1 | grep ''error:'' | grep -v ''bignum'' | sort -u | head -30\")",
|
||||
"Bash(C:/msys64/msys2_shell.cmd -mingw64 -defterm -no-start -here -c \"cd /e/repos/triangles && mingw32-make -j4 2>&1 | grep ''error:'' | sort -u | head -30\")",
|
||||
"Bash(C:/msys64/msys2_shell.cmd -mingw64 -defterm -no-start -here -c \"grep ''MINIUPNPC_API_VERSION'' /mingw64/include/miniupnpc/miniupnpc.h\")",
|
||||
"Bash(C:/msys64/msys2_shell.cmd -mingw64 -defterm -no-start -here -c \"grep -A3 ''upnpDiscover\\('' /mingw64/include/miniupnpc/miniupnpc.h | head -10\")",
|
||||
"Bash(C:/msys64/msys2_shell.cmd -mingw64 -defterm -no-start -here -c \"grep -B1 -A5 ''UPNP_GetValidIGD\\('' /mingw64/include/miniupnpc/miniupnpc.h\")",
|
||||
"Bash(C:/msys64/msys2_shell.cmd -mingw64 -defterm -no-start -here -c \"cd /e/repos/triangles && qmake-qt5 triangles-qt.pro 2>&1 | tail -5 && mingw32-make -j4 2>&1 | grep ''error:'' | sort -u | head -30\")",
|
||||
"Bash(C:/msys64/msys2_shell.cmd -mingw64 -defterm -no-start -here -c \"cd /e/repos/triangles && mingw32-make clean 2>&1 | tail -5 && qmake triangles-qt.pro 2>&1 && mingw32-make -j4 2>&1 | grep ''error:'' | sort -u | head -40\")",
|
||||
"Bash(C:/msys64/msys2_shell.cmd -mingw64 -defterm -no-start -here -c \"export PATH=/mingw64/bin:$PATH && cd /e/repos/triangles && rm -f build/*.o build/*.cpp 2>/dev/null; qmake triangles-qt.pro && mingw32-make -j4 2>&1 | grep ''error:'' | sort -u | head -40\")",
|
||||
"Bash(C:/msys64/msys2_shell.cmd -mingw64 -defterm -no-start -here -c \"cd /e/repos/triangles && rm -f build/*.o build/*.cpp 2>/dev/null; /mingw64/bin/qmake triangles-qt.pro && mingw32-make -j4 2>&1 | grep ''error:'' | sort -u | head -40\")",
|
||||
"Bash(C:/msys64/msys2_shell.cmd -mingw64 -defterm -no-start -here -c \"which qmake 2>/dev/null || ls /mingw64/bin/qmake* 2>/dev/null || ls /mingw64/share/qt5/bin/qmake* 2>/dev/null || find /mingw64 -name ''qmake*'' -type f 2>/dev/null | head -5\")",
|
||||
"Bash(C:/msys64/msys2_shell.cmd -mingw64 -defterm -no-start -here -c \"cd /e/repos/triangles && rm -f build/*.o build/*.cpp 2>/dev/null; qmake-qt5 triangles-qt.pro && mingw32-make -j4 2>&1 | grep ''error:'' | sort -u | head -40\")",
|
||||
"Bash(C:/msys64/msys2_shell.cmd -mingw64 -defterm -no-start -here -c \"cd /e/repos/triangles && mingw32-make -j4 2>&1 | grep ''error:'' | sort -u | head -40\")",
|
||||
"Bash(C:/msys64/msys2_shell.cmd -mingw64 -defterm -no-start -here -c \"cd /e/repos/triangles && mingw32-make -j4 2>&1 | grep -E ''error:'' | sort -u | head -40\")",
|
||||
"Bash(C:/msys64/msys2_shell.cmd -mingw64 -defterm -no-start -here -c \"find /e/repos/triangles -name ''*.exe'' -type f 2>/dev/null; ls -la /e/repos/triangles/release/ 2>/dev/null; ls -la /e/repos/triangles/debug/ 2>/dev/null\")",
|
||||
"Bash(C:/msys64/msys2_shell.cmd -mingw64 -defterm -no-start -here -c \"cd /e/repos/triangles && mingw32-make -j4 2>&1 | tail -60\")",
|
||||
"Bash(C:/msys64/msys2_shell.cmd -mingw64 -defterm -no-start -here -c \"pacman -S --noconfirm mingw-w64-x86_64-qt5-tools 2>&1 | tail -10\")",
|
||||
"Bash(C:/msys64/msys2_shell.cmd -mingw64 -defterm -no-start -here -c \"which lrelease 2>/dev/null; which lrelease-qt5 2>/dev/null; ls /mingw64/bin/lrelease* 2>/dev/null\")",
|
||||
"Bash(C:/msys64/msys2_shell.cmd -mingw64 -defterm -no-start -here -c \"ln -sf /mingw64/bin/lrelease-qt5.exe /mingw64/bin/lrelease.exe 2>/dev/null; ls -la /mingw64/bin/lrelease.exe\")",
|
||||
"Bash(C:/msys64/msys2_shell.cmd -mingw64 -defterm -no-start -here -c \"cd /e/repos/triangles && mingw32-make -j4 2>&1 | tail -80\")",
|
||||
"Bash(C:/msys64/msys2_shell.cmd -mingw64 -defterm -no-start -here -c \"cd /e/repos/triangles && qmake-qt5 triangles-qt.pro && mingw32-make -j4 2>&1 | tail -30\")",
|
||||
"Bash(C:/msys64/msys2_shell.cmd -mingw64 -defterm -no-start -here -c \"find /mingw64/lib -name ''*boost_system*'' 2>/dev/null\")",
|
||||
"Bash(C:/msys64/msys2_shell.cmd -mingw64 -defterm -no-start -here -c \"find /mingw64/lib -name ''libboost_*'' -name ''*.a'' 2>/dev/null | head -20\")",
|
||||
"Bash(C:/msys64/msys2_shell.cmd -mingw64 -defterm -no-start -here -c \"cd /e/repos/triangles && rm -f build/net.o && mingw32-make -j4 2>&1 | tail -20\")",
|
||||
"Bash(C:/msys64/msys2_shell.cmd -mingw64 -defterm -no-start -here -c \"ls -la /e/repos/triangles/release/triangles-qt.exe\")",
|
||||
"Bash(C:/msys64/msys2_shell.cmd -mingw64 -defterm -no-start -here -c \"cd /e/repos/triangles/release && ldd triangles-qt.exe 2>/dev/null | grep mingw64\")",
|
||||
"Bash(C:/msys64/msys2_shell.cmd -mingw64 -defterm -no-start -here -c \"cd /e/repos/triangles/release && ./triangles-qt.exe &\")",
|
||||
"Bash(C:/msys64/msys2_shell.cmd -mingw64 -defterm -no-start -here -c \"identify /e/repos/triangles/src/qt/res/images/header_logo.png 2>/dev/null || file /e/repos/triangles/src/qt/res/images/header_logo.png\")",
|
||||
"Bash(C:/msys64/msys2_shell.cmd -mingw64 -defterm -no-start -here -c \"ls -la /e/repos/triangles/src/qt/res/images/ | grep -i header\")",
|
||||
"Bash(C:/msys64/msys2_shell.cmd -mingw64 -defterm -no-start -here -c \"cp ''/e/TRI/TRI logo w name new1 \\(300 x 63 px\\).png'' ''/e/repos/triangles/src/qt/res/images/header_logo.png'' && file ''/e/repos/triangles/src/qt/res/images/header_logo.png''\")",
|
||||
"Bash(C:/msys64/msys2_shell.cmd -mingw64 -defterm -no-start -here -c \"taskkill //IM triangles-qt.exe //F 2>/dev/null; echo done\")",
|
||||
"Bash(C:/msys64/msys2_shell.cmd -mingw64 -defterm -no-start -here -c \"cd /e/repos/triangles && qmake-qt5 triangles-qt.pro && mingw32-make -j4 2>&1 | tail -10\")",
|
||||
"Bash(C:/msys64/msys2_shell.cmd -mingw64 -defterm -no-start -here -c \"cd /e/repos/triangles/src/qt/locale && sed -i ''s|https://bittrex.com/Market/Index?MarketName=BTC-TRI|https://313.cash|g'' *.ts && sed -i ''s|TRI on Bittrex|TRI on Pinball|g'' *.ts && echo done\")",
|
||||
"Bash(C:/msys64/msys2_shell.cmd -mingw64 -defterm -no-start -here -c \"cp ''/e/TRI/Copy of TRI logo w name new4 \\(300x63\\).png'' ''/e/repos/triangles/src/qt/res/images/header_logo.png'' && file ''/e/repos/triangles/src/qt/res/images/header_logo.png''\")",
|
||||
"Bash(git add:*)",
|
||||
"Bash(git push)",
|
||||
"Bash(git remote set-url:*)",
|
||||
"Bash(git -c http.sslVerify=false push)",
|
||||
"Bash(git -c credential.helper= push)",
|
||||
"Bash(ls:*)",
|
||||
"Bash(cmd /c \"set PATH=C:\\\\msys64\\\\mingw64\\\\bin;C:\\\\msys64\\\\usr\\\\bin;%PATH% && where qmake && where mingw32-make && where g++\")",
|
||||
"Bash(PATH=\"/c/msys64/mingw64/bin:/c/msys64/usr/bin:$PATH\")",
|
||||
"Bash(qmake-qt5:*)",
|
||||
"Bash(mingw32-make:*)",
|
||||
"Bash(ldd:*)",
|
||||
"Bash(objdump:*)",
|
||||
"Bash(/c/msys64/mingw64/bin/objdump.exe:*)",
|
||||
"Bash(tasklist:*)",
|
||||
"Bash(cmd.exe /c \"start /b E:\\\\repos\\\\triangles\\\\release\\\\triangles-qt.exe -datadir=E:\\\\Coins\\\\TRI -reindex\")",
|
||||
"Bash(gcc:*)",
|
||||
"Bash(/c/msys64/mingw64/bin/gcc.exe:*)",
|
||||
"Bash(cmd.exe:*)",
|
||||
"Bash(PATH=\"/c/msys64/mingw64/bin:$PATH\" /e/repos/triangles/scan_chain_tip.exe:*)",
|
||||
"Bash(PATH=\"/c/msys64/mingw64/bin:$PATH\" /c/msys64/mingw64/bin/qmake.exe:*)",
|
||||
"Bash(PATH=\"/c/msys64/mingw64/bin:$PATH\" qmake-qt5:*)",
|
||||
"Bash(PATH=\"/c/msys64/mingw64/bin:$PATH\" mingw32-make:*)",
|
||||
"Bash(export PATH=\"/c/msys64/mingw64/bin:$PATH\")",
|
||||
"Bash(\"C:/msys64/mingw64/bin/qmake-qt5.exe\" triangles-qt.pro -o Makefile)",
|
||||
"Bash(gh release create:*)",
|
||||
"Bash(gh repo view:*)",
|
||||
"Bash(gh repo create:*)",
|
||||
"Bash(git commit:*)",
|
||||
"Bash(git branch:*)",
|
||||
"Bash(git push:*)",
|
||||
"Bash(gh repo fork:*)",
|
||||
"Bash(gh api:*)",
|
||||
"Bash(gh auth:*)",
|
||||
"Bash(1 <<'EOF'\n{\"visibility\":\"public\"}\nEOF)",
|
||||
"Bash(findstr:*)",
|
||||
"Bash(gh workflow run:*)",
|
||||
"Bash(gh run watch:*)",
|
||||
"Bash(gh run view:*)",
|
||||
"Bash(gh release view:*)",
|
||||
"Bash(gh run download:*)",
|
||||
"Bash(gh release upload:*)",
|
||||
"Bash(gh release delete-asset:*)",
|
||||
"Bash(C:/msys64/mingw64/bin/mingw32-make.exe:*)",
|
||||
"Bash(C:/msys64/usr/bin/env.exe MSYSTEM=MINGW64 PATH=\"/mingw64/bin:/usr/bin:/bin\" /usr/bin/bash -lc \"cd /e/repos/triangles/src && mingw32-make -f makefile.mingw -j8 all 2>&1 | tail -60\")",
|
||||
"Bash(C:/msys64/usr/bin/env.exe MSYSTEM=MINGW64 /usr/bin/bash -lc \"ls /mingw64/lib/libboost_system*\")",
|
||||
"Bash(C:/msys64/usr/bin/env.exe MSYSTEM=MINGW64 PATH=\"/mingw64/bin:/usr/bin:/bin\" /usr/bin/bash -lc \"cd /e/repos/triangles/src && mingw32-make -f makefile.mingw trianglesd.exe 2>&1 | tail -20\")",
|
||||
"Bash(C:/msys64/usr/bin/env.exe MSYSTEM=MINGW64 PATH=\"/mingw64/bin:/usr/bin:/bin\" /usr/bin/bash -lc \"cd /e/repos/triangles/src && mingw32-make -f makefile.mingw trianglesd.exe 2>&1 | tail -10\")",
|
||||
"Bash(gh run list:*)",
|
||||
"Bash(git rm:*)",
|
||||
"Bash(wc:*)",
|
||||
"Bash(C:/msys64/usr/bin/bash.exe -lc \"cd /e/repos/triangles/src && make -f makefile.mingw obj/rest.o 2>&1 | head -80\")",
|
||||
"Bash(C:/msys64/usr/bin/bash.exe -lc \"cd /e/repos/triangles/src && make -f makefile.mingw obj/trianglesrpc.o 2>&1 | tail -20\")",
|
||||
"Bash(C:/msys64/usr/bin/bash.exe -lc \"cd /e/repos/triangles/src && make -f makefile.mingw 2>&1 | tail -30\")",
|
||||
"Bash(node --version:*)",
|
||||
"Bash(npm --version:*)",
|
||||
"Bash(npm install:*)",
|
||||
"Bash(npx svelte-kit sync:*)",
|
||||
"Bash(npx vite build)",
|
||||
"Bash(nslookup:*)",
|
||||
"Bash(tailscale ping:*)",
|
||||
"Bash(del /f \"%APPDATA%\\\\triangles\\\\peers.dat\")",
|
||||
"Bash(C:msys64usrbinbash.exe -l -c \"cd ''e:/repos/triangles'' && qmake triangles-qt.pro ''USE_QRCODE=1'' ''USE_UPNP=-'' 2>&1 | tail -20\")",
|
||||
"Bash(C:msys64msys2_shell.cmd -mingw64 -defterm -no-start -c \"cd /e/repos/triangles && qmake triangles-qt.pro ''USE_QRCODE=1'' ''USE_UPNP=-'' 2>&1 | tail -20\")",
|
||||
"Bash(\"C:\\\\msys64\\\\mingw64\\\\bin\\\\bash.exe\" -c \"export PATH=/mingw64/bin:/usr/bin:$PATH && cd /e/repos/triangles && qmake triangles-qt.pro ''USE_QRCODE=1'' ''USE_UPNP=-'' 2>&1\")",
|
||||
"Bash(C:/msys64/mingw64/bin/qmake.exe:*)",
|
||||
"Bash(/c/msys64/mingw64/bin/qmake-qt5.exe:*)",
|
||||
"Bash(/c/msys64/usr/bin/env.exe MSYSTEM=MINGW64 /c/msys64/usr/bin/bash.exe -l -c \"cd /e/repos/triangles && qmake triangles-qt.pro ''USE_QRCODE=1'' ''USE_UPNP=-'' 2>&1 | tail -5\")",
|
||||
"Bash(export PATH=\"/c/msys64/mingw64/bin:/c/msys64/usr/bin:$PATH\")",
|
||||
"Bash(/c/msys64/usr/bin/env.exe MSYSTEM=MINGW64 /c/msys64/usr/bin/bash.exe:*)",
|
||||
"Bash(C:/msys64/usr/bin/bash.exe -l -c \"cd /e/repos/triangles && make release 2>&1 | grep -E ''error|Error|undefined|cannot find'' | head -20\")",
|
||||
"Bash(C:/msys64/usr/bin/bash.exe -l -c \"pacman -Qs qrencode\")",
|
||||
"Bash(C:/msys64/usr/bin/bash.exe -l -c \"pacman -S --noconfirm mingw-w64-x86_64-qrencode\")",
|
||||
"Bash(C:/msys64/usr/bin/bash.exe -l -c \"cd /e/repos/triangles && qmake-qt5 -o Makefile triangles-qt.pro USE_QRCODE=0 USE_UPNP=- 2>&1\")",
|
||||
"Bash(C:/msys64/usr/bin/bash.exe -l -c \"ls /mingw64/lib/libqrencode*\")",
|
||||
"Bash(C:/msys64/usr/bin/bash.exe -l -c \"pacman -S --noconfirm mingw-w64-x86_64-cmake\")",
|
||||
"Bash(C:/msys64/usr/bin/bash.exe -l -c \"cd /tmp && pacman -Sp mingw-w64-x86_64-qrencode 2>/dev/null\")",
|
||||
"Bash(C:/msys64/usr/bin/bash.exe -l -c \"cd /e/repos/triangles && mkdir -p dist && cp release/triangles-qt.exe dist/ && cd dist && strip triangles-qt.exe && ls -lh triangles-qt.exe\")",
|
||||
"Bash(C:/msys64/usr/bin/bash.exe -l -c \"cd /e/repos/triangles/dist && ldd triangles-qt.exe | grep mingw64 | awk ''{print $3}''\")",
|
||||
"Bash(C:/msys64/usr/bin/bash.exe -l -c 'cd /e/repos/triangles/dist && ldd triangles-qt.exe | grep mingw64 | awk \"\"{print \\\\$3}\"\"')",
|
||||
"Bash(C:/msys64/usr/bin/bash.exe -l -c \"cd /e/repos/triangles/dist && ldd triangles-qt.exe | grep mingw64\")",
|
||||
"Bash(C:/msys64/usr/bin/bash.exe -l -c 'cd /e/repos/triangles/dist && ldd triangles-qt.exe | grep mingw64 | sed \"\"s/.*=> //\"\" | sed \"\"s/ \\(.*//\"\"> dlls.txt && while read dll; do cp \"\"$dll\"\" .; done < dlls.txt && ls *.dll | wc -l && echo \"\"DLLs copied\"\"')",
|
||||
"Bash(C:/msys64/usr/bin/bash.exe -l -c 'cd /e/repos/triangles/dist && mkdir -p platforms && cp /mingw64/share/qt5/plugins/platforms/qwindows.dll platforms/ && echo \"\"Qt platform plugin copied\"\"')",
|
||||
"Bash(C:/msys64/usr/bin/bash.exe -l -c 'cd /e/repos/triangles && rm -f Triangles-v5.1.8-win-x64.zip && cd dist && 7z a ../Triangles-v5.1.8-win-x64.zip triangles-qt.exe *.dll platforms/ && echo \"\"ZIP created\"\"')",
|
||||
"Bash(C:/msys64/usr/bin/bash.exe -l -c \"cd /e/repos/triangles && make -j4 2>&1 | tail -15\")",
|
||||
"Bash(pacman:*)",
|
||||
"Bash(tar:*)",
|
||||
"WebFetch(domain:src-ref.docs.torproject.org)",
|
||||
"WebFetch(domain:gitlab.torproject.org)",
|
||||
"Bash(git status:*)",
|
||||
"Bash(git stash:*)",
|
||||
"Bash(git pull:*)",
|
||||
"Bash(git stash pop:*)",
|
||||
"Bash(find:*)",
|
||||
"Read(//e/repos/triangles/**)",
|
||||
"Bash(curl:*)",
|
||||
"Bash(qmake:*)",
|
||||
"Bash(/c/msys64/mingw64/bin/mingw32-make.exe:*)",
|
||||
"Bash(C:/msys64/msys2_shell.cmd -mingw64 -defterm -no-start -here -c \"cd /e/repos/triangles && make -j1 2>&1 | tail -30\")",
|
||||
"Bash(C:/msys64/msys2_shell.cmd -defterm -no-start -mingw64 -c \"cd /e/repos/triangles && rm -f build/main.o && mingw32-make -f Makefile.Release build/main.o 2>&1 | grep -E ''^\\(src/|.*error\\)'' | head -10\")",
|
||||
"Bash(C:/msys64/msys2_shell.cmd -defterm -no-start -mingw64 -c \"cd /e/repos/triangles && ls -la build/main.o 2>&1\")",
|
||||
"Bash(git -C \"e:\\\\repos\\\\triangles\" log --oneline -20)",
|
||||
"Bash(git -C \"e:\\\\repos\\\\triangles\" describe --tags --abbrev=0)",
|
||||
"Bash(git -C \"e:\\\\repos\\\\triangles\" rev-parse --short HEAD)",
|
||||
"Bash(PATH=\"/c/msys64/mingw64/bin:$PATH\" which make:*)",
|
||||
"Bash(export PATH=\"/mingw64/bin:$PATH\")",
|
||||
"Bash(make:*)",
|
||||
"Bash(C:/msys64/usr/bin/env.exe PATH=\"C:/msys64/mingw64/bin:C:/msys64/usr/bin\" C:/msys64/usr/bin/make.exe:*)",
|
||||
"Bash(C:/msys64/msys2_shell.cmd -mingw64 -defterm -no-start -c \"cd /e/repos/triangles && make -f Makefile.Release -j8 2>&1 | tail -40\")",
|
||||
"Bash(C:/msys64/msys2_shell.cmd -mingw64 -defterm -no-start -here -c \"make 2>&1 | tail -10\")",
|
||||
"Bash(gh pr list:*)",
|
||||
"Bash(gh pr view:*)",
|
||||
"Bash(cmd //C \"powershell -NoProfile -Command \"\"Get-Process | Where-Object { $_.Path -like ''*triangles*'' } | Format-Table Id, ProcessName, Path\"\"\")",
|
||||
"Bash(cmd //C \"tasklist /FI \"\"IMAGENAME eq triangles-qt.exe\"\"\")",
|
||||
"Bash(MSYS_NO_PATHCONV=1 tasklist:*)"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -18,21 +18,24 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y build-essential libboost-all-dev \
|
||||
libssl-dev libdb++-dev libleveldb-dev libevent-dev libminiupnpc-dev
|
||||
sudo apt-get install -y build-essential cmake ninja-build \
|
||||
libboost-all-dev libssl-dev libdb++-dev libleveldb-dev \
|
||||
libevent-dev libminiupnpc-dev zlib1g-dev
|
||||
|
||||
- name: Build LevelDB
|
||||
- name: Configure
|
||||
run: |
|
||||
cd src/leveldb
|
||||
chmod +x build_detect_platform
|
||||
make clean || true
|
||||
make OPT="-O2" libleveldb.a libmemenv.a
|
||||
cmake -B build -G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DBUILD_QT=OFF \
|
||||
-DBUILD_DAEMON=ON \
|
||||
-DBUILD_TESTS=ON \
|
||||
-DUSE_UPNP=OFF
|
||||
|
||||
- name: Build and run unit tests
|
||||
run: |
|
||||
cd src
|
||||
make -f makefile.unix test -j$(nproc)
|
||||
./test_triangles --log_level=test_suite 2>&1 || true
|
||||
- name: Build
|
||||
run: cmake --build build -j$(nproc)
|
||||
|
||||
- name: Run unit tests
|
||||
run: cd build && ctest --output-on-failure || true
|
||||
|
||||
build-windows-qt:
|
||||
runs-on: windows-latest
|
||||
@@ -48,6 +51,8 @@ jobs:
|
||||
update: true
|
||||
install: >-
|
||||
mingw-w64-x86_64-gcc
|
||||
mingw-w64-x86_64-cmake
|
||||
mingw-w64-x86_64-ninja
|
||||
mingw-w64-x86_64-qt5-base
|
||||
mingw-w64-x86_64-qt5-tools
|
||||
mingw-w64-x86_64-boost
|
||||
@@ -55,49 +60,132 @@ jobs:
|
||||
mingw-w64-x86_64-db
|
||||
mingw-w64-x86_64-libevent
|
||||
mingw-w64-x86_64-miniupnpc
|
||||
make
|
||||
mingw-w64-x86_64-zlib
|
||||
|
||||
- name: Create Qt5 tool symlinks
|
||||
- name: Set VERSION
|
||||
run: |
|
||||
ln -sf /mingw64/bin/qmake-qt5.exe /mingw64/bin/qmake.exe 2>/dev/null || true
|
||||
ln -sf /mingw64/bin/lrelease-qt5.exe /mingw64/bin/lrelease.exe 2>/dev/null || true
|
||||
ln -sf /mingw64/bin/windeployqt-qt5.exe /mingw64/bin/windeployqt.exe 2>/dev/null || true
|
||||
if [[ "${GITHUB_REF}" == refs/tags/v* ]]; then
|
||||
echo "VERSION=${GITHUB_REF_NAME#v}" >> $GITHUB_ENV
|
||||
else
|
||||
MAJOR=$(grep 'CLIENT_VERSION_MAJOR' src/clientversion.h | awk '{print $3}')
|
||||
MINOR=$(grep 'CLIENT_VERSION_MINOR' src/clientversion.h | awk '{print $3}')
|
||||
REV=$(grep 'CLIENT_VERSION_REVISION' src/clientversion.h | awk '{print $3}')
|
||||
echo "VERSION=${MAJOR}.${MINOR}.${REV}" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- name: Clean stale build artifacts
|
||||
run: rm -rf build/*.o build/*.h
|
||||
|
||||
- name: Build LevelDB
|
||||
- name: Configure
|
||||
run: |
|
||||
cd src/leveldb
|
||||
make clean || true
|
||||
CC=gcc CXX=g++ TARGET_OS=OS_WINDOWS_CROSSCOMPILE make OPT="-fno-keep-inline-dllexport -march=nocona -msahf -mtune=generic -Wa,-mbig-obj -O2" libleveldb.a libmemenv.a
|
||||
|
||||
- name: Run qmake
|
||||
run: |
|
||||
qmake triangles-qt.pro "RELEASE=1"
|
||||
cmake -B build -G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DBUILD_QT=ON \
|
||||
-DBUILD_DAEMON=OFF \
|
||||
-DBUILD_TESTS=OFF \
|
||||
-DUSE_UPNP=ON \
|
||||
-DUSE_QRCODE=OFF
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
make -j$(nproc)
|
||||
run: cmake --build build -j$(nproc)
|
||||
|
||||
- name: Package
|
||||
run: |
|
||||
mkdir -p dist
|
||||
cp release/triangles-qt.exe dist/
|
||||
cp build/bin/triangles-qt.exe dist/
|
||||
windeployqt dist/triangles-qt.exe || true
|
||||
# Copy runtime DLLs
|
||||
|
||||
# Copy ALL runtime DLLs the binary needs
|
||||
# MinGW runtime
|
||||
for dll in libgcc_s_seh-1.dll libstdc++-6.dll libwinpthread-1.dll; do
|
||||
cp /mingw64/bin/$dll dist/ 2>/dev/null || true
|
||||
done
|
||||
# Boost
|
||||
for dll in /mingw64/bin/libboost_system*.dll /mingw64/bin/libboost_filesystem*.dll \
|
||||
/mingw64/bin/libboost_thread*.dll /mingw64/bin/libboost_program_options*.dll \
|
||||
/mingw64/bin/libboost_chrono*.dll; do
|
||||
cp $dll dist/ 2>/dev/null || true
|
||||
done
|
||||
# OpenSSL
|
||||
for dll in /mingw64/bin/libssl*.dll /mingw64/bin/libcrypto*.dll; do
|
||||
cp $dll dist/ 2>/dev/null || true
|
||||
done
|
||||
# BerkeleyDB, libevent, miniupnpc, zlib
|
||||
for dll in /mingw64/bin/libdb*.dll /mingw64/bin/libevent*.dll \
|
||||
/mingw64/bin/libminiupnpc*.dll /mingw64/bin/zlib1.dll; do
|
||||
cp $dll dist/ 2>/dev/null || true
|
||||
done
|
||||
|
||||
- name: Strip binary
|
||||
run: strip --strip-all dist/triangles-qt.exe
|
||||
# Catch anything we missed: scan ldd output for /mingw64 deps
|
||||
ldd dist/triangles-qt.exe | grep '/mingw64' | awk '{print $3}' | while read dll; do
|
||||
cp "$dll" dist/ 2>/dev/null || true
|
||||
done
|
||||
|
||||
- name: Upload artifact
|
||||
# Write qt.conf so the exe finds plugins relative to itself
|
||||
printf '[Paths]\nPlugins = .\n' > dist/qt.conf
|
||||
|
||||
# Ensure Qt platform plugins are present (windeployqt sometimes misses them in MSYS2)
|
||||
if [ ! -f dist/platforms/qwindows.dll ]; then
|
||||
echo "WARNING: windeployqt did not copy platform plugins, copying manually..."
|
||||
mkdir -p dist/platforms
|
||||
cp /mingw64/share/qt5/plugins/platforms/qwindows.dll dist/platforms/ 2>/dev/null || \
|
||||
cp /mingw64/lib/qt5/plugins/platforms/qwindows.dll dist/platforms/ 2>/dev/null || \
|
||||
find /mingw64 -name 'qwindows.dll' -exec cp {} dist/platforms/ \; 2>/dev/null
|
||||
fi
|
||||
|
||||
# Also copy styles and imageformats for good measure
|
||||
for plugdir in styles imageformats; do
|
||||
if [ ! -d "dist/$plugdir" ]; then
|
||||
srcdir=$(find /mingw64 -type d -name "$plugdir" -path "*/plugins/*" 2>/dev/null | head -1)
|
||||
if [ -n "$srcdir" ]; then
|
||||
cp -r "$srcdir" dist/
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
strip --strip-all dist/triangles-qt.exe
|
||||
echo "=== dist/ contents ==="
|
||||
find dist/ -type f | head -50
|
||||
|
||||
- name: Download Tor
|
||||
shell: powershell
|
||||
run: |
|
||||
$TOR_VERSION = "15.0.9"
|
||||
$TOR_URL = "https://archive.torproject.org/tor-package-archive/torbrowser/${TOR_VERSION}/tor-expert-bundle-windows-x86_64-${TOR_VERSION}.tar.gz"
|
||||
Invoke-WebRequest -Uri $TOR_URL -OutFile tor-bundle.tar.gz
|
||||
New-Item -ItemType Directory -Path tor-extract -Force
|
||||
tar -xzf tor-bundle.tar.gz -C tor-extract
|
||||
New-Item -ItemType Directory -Path tor-files -Force
|
||||
Copy-Item -Recurse tor-extract/tor/* tor-files/
|
||||
if (Test-Path tor-extract/tor/pluggable_transports) {
|
||||
Copy-Item -Recurse tor-extract/tor/pluggable_transports tor-files/pluggable_transports -Force
|
||||
}
|
||||
if (Test-Path tor-extract/data) {
|
||||
Copy-Item -Recurse tor-extract/data tor-files/data
|
||||
}
|
||||
Write-Host "Bundled Tor runtime files:"
|
||||
Get-ChildItem -Recurse tor-files | Select-Object FullName
|
||||
|
||||
- name: Install NSIS via MSYS2
|
||||
run: pacman -S --noconfirm mingw-w64-x86_64-nsis
|
||||
|
||||
- name: Install NSIS inetc plugin
|
||||
run: |
|
||||
pacman -S --noconfirm unzip
|
||||
NSIS_DIR="/mingw64/share/nsis"
|
||||
cd /tmp
|
||||
curl -L -o Inetc.zip "https://nsis.sourceforge.io/mediawiki/images/c/c9/Inetc.zip"
|
||||
unzip -o Inetc.zip -d inetc_extract
|
||||
# MSYS2 mingw64 NSIS is 64-bit, needs amd64-unicode plugin in Plugins/unicode/
|
||||
mkdir -p "$NSIS_DIR/Plugins/unicode"
|
||||
cp inetc_extract/Plugins/amd64-unicode/INetC.dll "$NSIS_DIR/Plugins/unicode/"
|
||||
echo "Installed 64-bit INetC.dll to $NSIS_DIR/Plugins/unicode/"
|
||||
|
||||
- name: Build NSIS installer
|
||||
run: makensis //DVERSION=$VERSION contrib/nsis/setup.nsi
|
||||
|
||||
- name: Upload installer
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: windows-qt
|
||||
path: dist/
|
||||
name: windows-qt-setup
|
||||
path: contrib/nsis/Cryptographic-Triangles-*-setup.exe
|
||||
|
||||
build-windows-daemon:
|
||||
runs-on: windows-latest
|
||||
@@ -113,181 +201,402 @@ jobs:
|
||||
update: true
|
||||
install: >-
|
||||
mingw-w64-x86_64-gcc
|
||||
mingw-w64-x86_64-cmake
|
||||
mingw-w64-x86_64-ninja
|
||||
mingw-w64-x86_64-boost
|
||||
mingw-w64-x86_64-openssl
|
||||
mingw-w64-x86_64-db
|
||||
mingw-w64-x86_64-libevent
|
||||
mingw-w64-x86_64-miniupnpc
|
||||
make
|
||||
mingw-w64-x86_64-zlib
|
||||
|
||||
- name: Build LevelDB
|
||||
- name: Configure
|
||||
run: |
|
||||
cd src/leveldb
|
||||
make clean || true
|
||||
CC=gcc CXX=g++ TARGET_OS=OS_WINDOWS_CROSSCOMPILE make OPT="-fno-keep-inline-dllexport -march=nocona -msahf -mtune=generic -Wa,-mbig-obj -O2" libleveldb.a libmemenv.a
|
||||
cmake -B build -G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DBUILD_QT=OFF \
|
||||
-DBUILD_DAEMON=ON \
|
||||
-DBUILD_TESTS=OFF \
|
||||
-DUSE_UPNP=ON
|
||||
|
||||
- name: Build daemon
|
||||
- name: Build
|
||||
run: |
|
||||
set -eo pipefail
|
||||
cd src
|
||||
mkdir -p obj
|
||||
make -f makefile.mingw DEPSDIR=/mingw64 all -j$(nproc) 2>&1
|
||||
strip --strip-all trianglesd.exe
|
||||
cp trianglesd.exe ../trianglesd.exe
|
||||
cmake --build build -j$(nproc)
|
||||
strip --strip-all build/bin/trianglesd.exe
|
||||
|
||||
- name: Package daemon with DLLs
|
||||
run: |
|
||||
mkdir -p daemon-dist/tor
|
||||
cp build/bin/trianglesd.exe daemon-dist/
|
||||
|
||||
# Copy all linked DLLs from MSYS2
|
||||
ldd build/bin/trianglesd.exe | grep '/mingw64' | awk '{print $3}' | while read dll; do
|
||||
cp "$dll" daemon-dist/ 2>/dev/null || true
|
||||
done
|
||||
|
||||
- name: Bundle Tor for daemon
|
||||
shell: powershell
|
||||
run: |
|
||||
$TOR_VERSION = "15.0.9"
|
||||
Invoke-WebRequest -Uri "https://archive.torproject.org/tor-package-archive/torbrowser/${TOR_VERSION}/tor-expert-bundle-windows-x86_64-${TOR_VERSION}.tar.gz" -OutFile tor-bundle.tar.gz
|
||||
New-Item -ItemType Directory -Path tor-extract -Force
|
||||
tar -xzf tor-bundle.tar.gz -C tor-extract
|
||||
Copy-Item -Recurse tor-extract/tor/* daemon-dist/tor/
|
||||
if (Test-Path tor-extract/data) {
|
||||
Copy-Item -Recurse tor-extract/data daemon-dist/tor/data
|
||||
}
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: windows-daemon
|
||||
path: trianglesd.exe
|
||||
path: daemon-dist/
|
||||
|
||||
build-linux-qt:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set VERSION from source
|
||||
- name: Set VERSION
|
||||
run: |
|
||||
MAJOR=$(grep 'CLIENT_VERSION_MAJOR' src/clientversion.h | awk '{print $3}')
|
||||
MINOR=$(grep 'CLIENT_VERSION_MINOR' src/clientversion.h | awk '{print $3}')
|
||||
REV=$(grep 'CLIENT_VERSION_REVISION' src/clientversion.h | awk '{print $3}')
|
||||
echo "VERSION=${MAJOR}.${MINOR}.${REV}" >> $GITHUB_ENV
|
||||
if [[ "${GITHUB_REF}" == refs/tags/v* ]]; then
|
||||
echo "VERSION=${GITHUB_REF_NAME#v}" >> $GITHUB_ENV
|
||||
else
|
||||
MAJOR=$(grep 'CLIENT_VERSION_MAJOR' src/clientversion.h | awk '{print $3}')
|
||||
MINOR=$(grep 'CLIENT_VERSION_MINOR' src/clientversion.h | awk '{print $3}')
|
||||
REV=$(grep 'CLIENT_VERSION_REVISION' src/clientversion.h | awk '{print $3}')
|
||||
echo "VERSION=${MAJOR}.${MINOR}.${REV}" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y build-essential qt5-qmake qtbase5-dev \
|
||||
qttools5-dev-tools libboost-all-dev libssl-dev libdb++-dev \
|
||||
libleveldb-dev libevent-dev libminiupnpc-dev
|
||||
sudo apt-get install -y build-essential cmake ninja-build \
|
||||
qtbase5-dev qttools5-dev-tools \
|
||||
libboost-all-dev libssl-dev libdb++-dev \
|
||||
libleveldb-dev libevent-dev libminiupnpc-dev zlib1g-dev
|
||||
|
||||
- name: Build LevelDB
|
||||
- name: Configure
|
||||
run: |
|
||||
cd src/leveldb
|
||||
chmod +x build_detect_platform
|
||||
make clean || true
|
||||
make OPT="-O2" libleveldb.a libmemenv.a
|
||||
|
||||
- name: Clean stale build artifacts
|
||||
run: rm -rf build/*.o
|
||||
|
||||
- name: Run qmake
|
||||
run: qmake triangles-qt.pro "RELEASE=1"
|
||||
cmake -B build -G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DBUILD_QT=ON \
|
||||
-DBUILD_DAEMON=OFF \
|
||||
-DBUILD_TESTS=OFF \
|
||||
-DUSE_UPNP=ON
|
||||
|
||||
- name: Build
|
||||
run: make -j$(nproc)
|
||||
run: cmake --build build -j$(nproc)
|
||||
|
||||
- name: Strip binary
|
||||
run: strip --strip-all triangles-qt
|
||||
run: strip --strip-all build/bin/triangles-qt
|
||||
|
||||
- name: Rename
|
||||
run: mv triangles-qt Cryptographic-Triangles-v${VERSION}-linux-x64-qt
|
||||
- name: Build .deb package (fully self-contained)
|
||||
run: |
|
||||
TOR_VERSION="15.0.9"
|
||||
curl -sL "https://archive.torproject.org/tor-package-archive/torbrowser/${TOR_VERSION}/tor-expert-bundle-linux-x86_64-${TOR_VERSION}.tar.gz" -o tor-bundle.tar.gz
|
||||
mkdir -p tor-extract && tar -xzf tor-bundle.tar.gz -C tor-extract
|
||||
|
||||
- name: Upload artifact
|
||||
PKG="cryptographic-triangles_${VERSION}_amd64"
|
||||
mkdir -p ${PKG}/DEBIAN
|
||||
mkdir -p ${PKG}/usr/lib/cryptographic-triangles/lib
|
||||
mkdir -p ${PKG}/usr/lib/cryptographic-triangles/tor
|
||||
mkdir -p ${PKG}/usr/bin
|
||||
mkdir -p ${PKG}/usr/share/applications
|
||||
mkdir -p ${PKG}/usr/share/pixmaps
|
||||
|
||||
cp build/bin/triangles-qt ${PKG}/usr/lib/cryptographic-triangles/
|
||||
cp tor-extract/tor/tor ${PKG}/usr/lib/cryptographic-triangles/tor/
|
||||
chmod +x ${PKG}/usr/lib/cryptographic-triangles/tor/tor
|
||||
[ -d tor-extract/data ] && cp -r tor-extract/data ${PKG}/usr/lib/cryptographic-triangles/tor/data
|
||||
|
||||
# Bundle ALL shared library dependencies (except glibc/kernel)
|
||||
ldd build/bin/triangles-qt | grep '=> /' | awk '{print $3}' | while read lib; do
|
||||
case "$lib" in
|
||||
/lib/x86_64-linux-gnu/libc.so*|/lib/x86_64-linux-gnu/libm.so*|/lib/x86_64-linux-gnu/libpthread.so*|/lib/x86_64-linux-gnu/libdl.so*|/lib/x86_64-linux-gnu/librt.so*|/lib/x86_64-linux-gnu/ld-linux*|/lib64/ld-linux*)
|
||||
;; # Skip glibc core — always present
|
||||
*)
|
||||
cp -L "$lib" ${PKG}/usr/lib/cryptographic-triangles/lib/ 2>/dev/null || true
|
||||
;;
|
||||
esac
|
||||
done
|
||||
echo "=== Bundled libs ==="
|
||||
ls ${PKG}/usr/lib/cryptographic-triangles/lib/ | wc -l
|
||||
ls ${PKG}/usr/lib/cryptographic-triangles/lib/
|
||||
|
||||
# Launcher with LD_LIBRARY_PATH
|
||||
cat > ${PKG}/usr/bin/cryptographic-triangles << 'LAUNCHER'
|
||||
#!/bin/bash
|
||||
INSTALL_DIR=/usr/lib/cryptographic-triangles
|
||||
export LD_LIBRARY_PATH="${INSTALL_DIR}/lib:${LD_LIBRARY_PATH}"
|
||||
exec "${INSTALL_DIR}/triangles-qt" "$@"
|
||||
LAUNCHER
|
||||
sed -i 's/^ //' ${PKG}/usr/bin/cryptographic-triangles
|
||||
chmod +x ${PKG}/usr/bin/cryptographic-triangles
|
||||
|
||||
cat > ${PKG}/usr/share/applications/cryptographic-triangles.desktop << 'DESKTOP'
|
||||
[Desktop Entry]
|
||||
Name=Cryptographic Triangles
|
||||
Comment=Triangles Cryptocurrency Wallet
|
||||
Exec=cryptographic-triangles
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Icon=cryptographic-triangles
|
||||
Categories=Finance;Network;
|
||||
DESKTOP
|
||||
sed -i 's/^ //' ${PKG}/usr/share/applications/cryptographic-triangles.desktop
|
||||
|
||||
cp src/qt/res/icons/triangles.ico ${PKG}/usr/share/pixmaps/cryptographic-triangles.ico 2>/dev/null || true
|
||||
|
||||
cat > ${PKG}/DEBIAN/control << CTRL
|
||||
Package: cryptographic-triangles
|
||||
Version: ${VERSION}
|
||||
Architecture: amd64
|
||||
Maintainer: Cryptographic Triangles <dev@cryptographic-triangles.org>
|
||||
Description: Cryptographic Triangles wallet with integrated Tor
|
||||
Fully self-contained wallet with all libraries and Tor bundled.
|
||||
No external dependencies required — runs on any x86_64 Linux.
|
||||
Section: finance
|
||||
Priority: optional
|
||||
CTRL
|
||||
sed -i 's/^ //' ${PKG}/DEBIAN/control
|
||||
|
||||
dpkg-deb --build ${PKG}
|
||||
|
||||
- name: Upload .deb
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: linux-qt
|
||||
path: Cryptographic-Triangles-v*-linux-x64-qt
|
||||
name: linux-qt-deb
|
||||
path: cryptographic-triangles_*_amd64.deb
|
||||
|
||||
build-linux-daemon:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set VERSION from source
|
||||
- name: Set VERSION
|
||||
run: |
|
||||
MAJOR=$(grep 'CLIENT_VERSION_MAJOR' src/clientversion.h | awk '{print $3}')
|
||||
MINOR=$(grep 'CLIENT_VERSION_MINOR' src/clientversion.h | awk '{print $3}')
|
||||
REV=$(grep 'CLIENT_VERSION_REVISION' src/clientversion.h | awk '{print $3}')
|
||||
echo "VERSION=${MAJOR}.${MINOR}.${REV}" >> $GITHUB_ENV
|
||||
if [[ "${GITHUB_REF}" == refs/tags/v* ]]; then
|
||||
echo "VERSION=${GITHUB_REF_NAME#v}" >> $GITHUB_ENV
|
||||
else
|
||||
MAJOR=$(grep 'CLIENT_VERSION_MAJOR' src/clientversion.h | awk '{print $3}')
|
||||
MINOR=$(grep 'CLIENT_VERSION_MINOR' src/clientversion.h | awk '{print $3}')
|
||||
REV=$(grep 'CLIENT_VERSION_REVISION' src/clientversion.h | awk '{print $3}')
|
||||
echo "VERSION=${MAJOR}.${MINOR}.${REV}" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y build-essential libboost-all-dev \
|
||||
libssl-dev libdb++-dev libleveldb-dev libevent-dev libminiupnpc-dev
|
||||
sudo apt-get install -y build-essential cmake ninja-build \
|
||||
libboost-all-dev libssl-dev libdb++-dev libleveldb-dev \
|
||||
libevent-dev libminiupnpc-dev zlib1g-dev
|
||||
|
||||
- name: Build LevelDB
|
||||
- name: Configure
|
||||
run: |
|
||||
cd src/leveldb
|
||||
chmod +x build_detect_platform
|
||||
make clean || true
|
||||
make OPT="-O2" libleveldb.a libmemenv.a
|
||||
cmake -B build -G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DBUILD_QT=OFF \
|
||||
-DBUILD_DAEMON=ON \
|
||||
-DBUILD_TESTS=OFF \
|
||||
-DUSE_UPNP=ON
|
||||
|
||||
- name: Build daemon
|
||||
run: |
|
||||
cd src
|
||||
mkdir -p obj
|
||||
make -f makefile.unix trianglesd -j$(nproc)
|
||||
- name: Build
|
||||
run: cmake --build build -j$(nproc)
|
||||
|
||||
- name: Strip binary
|
||||
run: strip --strip-all src/trianglesd
|
||||
run: strip --strip-all build/bin/trianglesd
|
||||
|
||||
- name: Rename
|
||||
run: mv src/trianglesd Cryptographic-Triangles-v${VERSION}-linux-x64-daemon
|
||||
- name: Build .deb package (fully self-contained)
|
||||
run: |
|
||||
TOR_VERSION="15.0.9"
|
||||
curl -sL "https://archive.torproject.org/tor-package-archive/torbrowser/${TOR_VERSION}/tor-expert-bundle-linux-x86_64-${TOR_VERSION}.tar.gz" -o tor-bundle.tar.gz
|
||||
mkdir -p tor-extract && tar -xzf tor-bundle.tar.gz -C tor-extract
|
||||
|
||||
- name: Upload artifact
|
||||
PKG="cryptographic-triangles-daemon_${VERSION}_amd64"
|
||||
mkdir -p ${PKG}/DEBIAN
|
||||
mkdir -p ${PKG}/usr/lib/cryptographic-triangles/lib
|
||||
mkdir -p ${PKG}/usr/lib/cryptographic-triangles/tor
|
||||
mkdir -p ${PKG}/usr/bin
|
||||
mkdir -p ${PKG}/etc/systemd/system
|
||||
|
||||
cp build/bin/trianglesd ${PKG}/usr/lib/cryptographic-triangles/
|
||||
cp tor-extract/tor/tor ${PKG}/usr/lib/cryptographic-triangles/tor/
|
||||
chmod +x ${PKG}/usr/lib/cryptographic-triangles/tor/tor
|
||||
[ -d tor-extract/data ] && cp -r tor-extract/data ${PKG}/usr/lib/cryptographic-triangles/tor/data
|
||||
|
||||
# Bundle ALL shared library dependencies (except glibc/kernel)
|
||||
ldd build/bin/trianglesd | grep '=> /' | awk '{print $3}' | while read lib; do
|
||||
case "$lib" in
|
||||
/lib/x86_64-linux-gnu/libc.so*|/lib/x86_64-linux-gnu/libm.so*|/lib/x86_64-linux-gnu/libpthread.so*|/lib/x86_64-linux-gnu/libdl.so*|/lib/x86_64-linux-gnu/librt.so*|/lib/x86_64-linux-gnu/ld-linux*|/lib64/ld-linux*)
|
||||
;; # Skip glibc core — always present
|
||||
*)
|
||||
cp -L "$lib" ${PKG}/usr/lib/cryptographic-triangles/lib/ 2>/dev/null || true
|
||||
;;
|
||||
esac
|
||||
done
|
||||
echo "=== Bundled libs ==="
|
||||
ls ${PKG}/usr/lib/cryptographic-triangles/lib/ | wc -l
|
||||
ls ${PKG}/usr/lib/cryptographic-triangles/lib/
|
||||
|
||||
# Launcher with LD_LIBRARY_PATH
|
||||
cat > ${PKG}/usr/bin/trianglesd << 'LAUNCHER'
|
||||
#!/bin/bash
|
||||
INSTALL_DIR=/usr/lib/cryptographic-triangles
|
||||
export LD_LIBRARY_PATH="${INSTALL_DIR}/lib:${LD_LIBRARY_PATH}"
|
||||
exec "${INSTALL_DIR}/trianglesd" "$@"
|
||||
LAUNCHER
|
||||
sed -i 's/^ //' ${PKG}/usr/bin/trianglesd
|
||||
chmod +x ${PKG}/usr/bin/trianglesd
|
||||
|
||||
cat > ${PKG}/etc/systemd/system/trianglesd.service << 'SVC'
|
||||
[Unit]
|
||||
Description=Cryptographic Triangles Daemon
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
Environment=LD_LIBRARY_PATH=/usr/lib/cryptographic-triangles/lib
|
||||
ExecStart=/usr/lib/cryptographic-triangles/trianglesd
|
||||
Restart=on-failure
|
||||
RestartSec=10
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
SVC
|
||||
sed -i 's/^ //' ${PKG}/etc/systemd/system/trianglesd.service
|
||||
|
||||
cat > ${PKG}/DEBIAN/control << CTRL
|
||||
Package: cryptographic-triangles-daemon
|
||||
Version: ${VERSION}
|
||||
Architecture: amd64
|
||||
Maintainer: Cryptographic Triangles <dev@cryptographic-triangles.org>
|
||||
Description: Cryptographic Triangles daemon with integrated Tor
|
||||
Fully self-contained headless node with all libraries, Tor, and systemd service.
|
||||
No external dependencies required — runs on any x86_64 Linux.
|
||||
Section: finance
|
||||
Priority: optional
|
||||
CTRL
|
||||
sed -i 's/^ //' ${PKG}/DEBIAN/control
|
||||
|
||||
cat > ${PKG}/DEBIAN/postinst << 'POST'
|
||||
#!/bin/bash
|
||||
systemctl daemon-reload
|
||||
echo ""
|
||||
echo "Cryptographic Triangles daemon installed."
|
||||
echo " Start: sudo systemctl start trianglesd"
|
||||
echo " On boot: sudo systemctl enable trianglesd"
|
||||
echo ""
|
||||
POST
|
||||
chmod +x ${PKG}/DEBIAN/postinst
|
||||
|
||||
dpkg-deb --build ${PKG}
|
||||
|
||||
- name: Upload .deb
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: linux-daemon
|
||||
path: Cryptographic-Triangles-v*-linux-x64-daemon
|
||||
name: linux-daemon-deb
|
||||
path: cryptographic-triangles-daemon_*_amd64.deb
|
||||
|
||||
build-macos:
|
||||
runs-on: macos-15
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set VERSION from source
|
||||
- name: Set VERSION
|
||||
run: |
|
||||
MAJOR=$(grep 'CLIENT_VERSION_MAJOR' src/clientversion.h | awk '{print $3}')
|
||||
MINOR=$(grep 'CLIENT_VERSION_MINOR' src/clientversion.h | awk '{print $3}')
|
||||
REV=$(grep 'CLIENT_VERSION_REVISION' src/clientversion.h | awk '{print $3}')
|
||||
echo "VERSION=${MAJOR}.${MINOR}.${REV}" >> $GITHUB_ENV
|
||||
if [[ "${GITHUB_REF}" == refs/tags/v* ]]; then
|
||||
echo "VERSION=${GITHUB_REF_NAME#v}" >> $GITHUB_ENV
|
||||
else
|
||||
MAJOR=$(grep 'CLIENT_VERSION_MAJOR' src/clientversion.h | awk '{print $3}')
|
||||
MINOR=$(grep 'CLIENT_VERSION_MINOR' src/clientversion.h | awk '{print $3}')
|
||||
REV=$(grep 'CLIENT_VERSION_REVISION' src/clientversion.h | awk '{print $3}')
|
||||
echo "VERSION=${MAJOR}.${MINOR}.${REV}" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
brew install qt@5 openssl@3 boost berkeley-db@5 leveldb libevent miniupnpc
|
||||
brew install cmake ninja qt@5 openssl@3 boost berkeley-db@5 leveldb libevent miniupnpc
|
||||
|
||||
- name: Clean stale build artifacts
|
||||
run: rm -rf build/*.o build/*.h
|
||||
|
||||
- name: Build LevelDB
|
||||
run: |
|
||||
cd src/leveldb
|
||||
chmod +x build_detect_platform
|
||||
make clean || true
|
||||
CC=clang CXX=clang++ make OPT="-O2" libleveldb.a libmemenv.a
|
||||
|
||||
- name: Run qmake
|
||||
- name: Configure
|
||||
run: |
|
||||
export PATH="/opt/homebrew/opt/qt@5/bin:$PATH"
|
||||
qmake triangles-qt.pro -spec macx-clang \
|
||||
"BOOST_INCLUDE_PATH=/opt/homebrew/opt/boost/include" \
|
||||
"BOOST_LIB_PATH=/opt/homebrew/opt/boost/lib" \
|
||||
"BDB_INCLUDE_PATH=/opt/homebrew/opt/berkeley-db@5/include" \
|
||||
"BDB_LIB_PATH=/opt/homebrew/opt/berkeley-db@5/lib" \
|
||||
"BDB_LIB_SUFFIX=" \
|
||||
"OPENSSL_INCLUDE_PATH=/opt/homebrew/opt/openssl@3/include" \
|
||||
"OPENSSL_LIB_PATH=/opt/homebrew/opt/openssl@3/lib" \
|
||||
"MINIUPNPC_INCLUDE_PATH=/opt/homebrew/opt/miniupnpc/include" \
|
||||
"MINIUPNPC_LIB_PATH=/opt/homebrew/opt/miniupnpc/lib" \
|
||||
"EVENT_INCLUDE_PATH=/opt/homebrew/opt/libevent/include" \
|
||||
"EVENT_LIB_PATH=/opt/homebrew/opt/libevent/lib" \
|
||||
"RELEASE=1"
|
||||
cmake -B build -G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DBUILD_QT=ON \
|
||||
-DBUILD_DAEMON=OFF \
|
||||
-DBUILD_TESTS=OFF \
|
||||
-DUSE_UPNP=ON \
|
||||
-DBOOST_ROOT=/opt/homebrew/opt/boost \
|
||||
-DBDB_INCLUDE_PATH=/opt/homebrew/opt/berkeley-db@5/include \
|
||||
-DBDB_LIB_PATH=/opt/homebrew/opt/berkeley-db@5/lib \
|
||||
-DOPENSSL_ROOT_DIR=/opt/homebrew/opt/openssl@3 \
|
||||
-DEVENT_INCLUDE_PATH=/opt/homebrew/opt/libevent/include \
|
||||
-DEVENT_LIB_PATH=/opt/homebrew/opt/libevent/lib \
|
||||
-DMINIUPNPC_INCLUDE_PATH=/opt/homebrew/opt/miniupnpc/include \
|
||||
-DMINIUPNPC_LIB_PATH=/opt/homebrew/opt/miniupnpc/lib \
|
||||
-DQt5_DIR=/opt/homebrew/opt/qt@5/lib/cmake/Qt5
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
export PATH="/opt/homebrew/opt/qt@5/bin:$PATH"
|
||||
make -j$(sysctl -n hw.ncpu)
|
||||
run: cmake --build build -j$(sysctl -n hw.ncpu)
|
||||
|
||||
- name: Create .app bundle
|
||||
run: |
|
||||
export PATH="/opt/homebrew/opt/qt@5/bin:$PATH"
|
||||
macdeployqt Triangles-Qt.app -verbose=1
|
||||
macdeployqt build/bin/Triangles-Qt.app -verbose=1 || \
|
||||
macdeployqt build/bin/triangles-qt.app -verbose=1 || true
|
||||
|
||||
- name: Bundle non-Qt dylibs into app
|
||||
run: |
|
||||
# Find the .app bundle
|
||||
APP=$(find build/bin -name "*.app" -maxdepth 1 | head -1)
|
||||
if [ -z "$APP" ]; then
|
||||
echo "No .app bundle found, creating one manually..."
|
||||
APP="build/bin/Triangles-Qt.app"
|
||||
mkdir -p "$APP/Contents/MacOS" "$APP/Contents/Frameworks"
|
||||
cp build/bin/triangles-qt "$APP/Contents/MacOS/Triangles-Qt"
|
||||
fi
|
||||
FRAMEWORKS="$APP/Contents/Frameworks"
|
||||
BINARY=$(find "$APP/Contents/MacOS" -type f -perm +111 | head -1)
|
||||
|
||||
# Copy Homebrew dylibs that macdeployqt doesn't handle
|
||||
for lib in boost_system boost_filesystem boost_thread boost_program_options boost_chrono; do
|
||||
DYLIB=$(otool -L "$BINARY" | grep "$lib" | awk '{print $1}')
|
||||
if [ -n "$DYLIB" ] && [ -f "$DYLIB" ]; then
|
||||
cp "$DYLIB" "$FRAMEWORKS/"
|
||||
BASENAME=$(basename "$DYLIB")
|
||||
install_name_tool -change "$DYLIB" "@executable_path/../Frameworks/$BASENAME" "$BINARY"
|
||||
fi
|
||||
done
|
||||
for lib in libssl libcrypto libevent libdb_cxx libminiupnpc libsodium; do
|
||||
DYLIB=$(otool -L "$BINARY" | grep "$lib" | awk '{print $1}')
|
||||
if [ -n "$DYLIB" ] && [ -f "$DYLIB" ]; then
|
||||
cp "$DYLIB" "$FRAMEWORKS/"
|
||||
BASENAME=$(basename "$DYLIB")
|
||||
install_name_tool -change "$DYLIB" "@executable_path/../Frameworks/$BASENAME" "$BINARY"
|
||||
fi
|
||||
done
|
||||
|
||||
echo "=== Final dylib dependencies ==="
|
||||
otool -L "$BINARY" | head -30
|
||||
|
||||
- name: Bundle Tor into app
|
||||
run: |
|
||||
TOR_VERSION="15.0.9"
|
||||
curl -sL "https://archive.torproject.org/tor-package-archive/torbrowser/${TOR_VERSION}/tor-expert-bundle-macos-aarch64-${TOR_VERSION}.tar.gz" -o tor-bundle.tar.gz
|
||||
mkdir -p tor-extract && tar -xzf tor-bundle.tar.gz -C tor-extract
|
||||
APP=$(find build/bin -name "*.app" -maxdepth 1 | head -1)
|
||||
mkdir -p "$APP/Contents/MacOS/tor"
|
||||
cp tor-extract/tor/tor "$APP/Contents/MacOS/tor/"
|
||||
chmod +x "$APP/Contents/MacOS/tor/tor"
|
||||
[ -d tor-extract/data ] && cp -r tor-extract/data "$APP/Contents/MacOS/tor/data"
|
||||
|
||||
- name: Create DMG
|
||||
run: |
|
||||
APP=$(find build/bin -name "*.app" -maxdepth 1 | head -1)
|
||||
mkdir -p dmg_contents
|
||||
cp -R Triangles-Qt.app dmg_contents/
|
||||
cp -R "$APP" dmg_contents/
|
||||
ln -s /Applications dmg_contents/Applications
|
||||
hdiutil create -volname "Cryptographic Triangles" \
|
||||
-srcfolder dmg_contents \
|
||||
@@ -318,14 +627,15 @@ jobs:
|
||||
- name: Prepare release assets
|
||||
run: |
|
||||
mkdir -p release
|
||||
# Windows
|
||||
cd artifacts/windows-qt && zip -r ../../release/Cryptographic-Triangles-${VERSION}-win-x64.zip . && cd ../..
|
||||
cp artifacts/windows-qt/triangles-qt.exe release/Cryptographic-Triangles-${VERSION}-win-x64-qt.exe
|
||||
cp artifacts/windows-daemon/trianglesd.exe release/Cryptographic-Triangles-${VERSION}-win-x64-daemon.exe
|
||||
# Linux
|
||||
cp artifacts/linux-qt/Cryptographic-Triangles-* release/
|
||||
cp artifacts/linux-daemon/Cryptographic-Triangles-* release/
|
||||
# macOS
|
||||
# Windows Qt installer (setup.exe — includes Tor, Start Menu shortcuts, uninstaller)
|
||||
cp artifacts/windows-qt-setup/*.exe release/
|
||||
# Windows daemon (zip with DLLs + Tor)
|
||||
cd artifacts/windows-daemon && zip -r "../../release/Cryptographic-Triangles-${VERSION}-win-x64-daemon.zip" . && cd ../..
|
||||
# Linux Qt .deb (dpkg -i to install — includes Tor, desktop entry, icon)
|
||||
cp artifacts/linux-qt-deb/*.deb release/
|
||||
# Linux daemon .deb (dpkg -i to install — includes Tor, systemd service)
|
||||
cp artifacts/linux-daemon-deb/*.deb release/
|
||||
# macOS DMG (drag to Applications — Tor inside .app bundle)
|
||||
cp artifacts/macos-arm64-dmg/*.dmg release/
|
||||
ls -la release/
|
||||
|
||||
@@ -334,3 +644,19 @@ jobs:
|
||||
with:
|
||||
files: release/*
|
||||
generate_release_notes: true
|
||||
|
||||
trigger-tripi:
|
||||
name: Trigger TRI-PI ARM64 Build
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
needs: release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Dispatch tri-pi ARM64 build
|
||||
run: |
|
||||
curl -f -X POST \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "Authorization: Bearer ${{ secrets.TRIPI_BUILD_TOKEN }}" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
https://api.github.com/repos/SamiAhmed7777/tri-pi/dispatches \
|
||||
-d '{"event_type":"new-release","client_payload":{"version":"${{ github.ref_name }}","source_repo":"SamiAhmed7777/triangles_v5"}}'
|
||||
echo "Triggered tri-pi repository_dispatch for ${{ github.ref_name }}"
|
||||
|
||||
@@ -41,6 +41,7 @@ blocks/
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
.claude/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
@@ -51,6 +52,7 @@ blocks/
|
||||
.*.json
|
||||
temp/
|
||||
tmp/
|
||||
testnet-sync/
|
||||
|
||||
# Private/Local
|
||||
triangles.conf
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
[submodule "src/tor/tor-src"]
|
||||
path = src/tor/tor-src
|
||||
url = https://gitlab.torproject.org/tpo/core/tor.git
|
||||
@@ -0,0 +1,137 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
# Silence CMP0167 warning (FindBoost removed in CMake 3.30+, use BoostConfig)
|
||||
if(POLICY CMP0167)
|
||||
cmake_policy(SET CMP0167 NEW)
|
||||
endif()
|
||||
|
||||
project(Triangles
|
||||
VERSION 5.9.0
|
||||
DESCRIPTION "Cryptographic Triangles Wallet"
|
||||
LANGUAGES C CXX
|
||||
)
|
||||
|
||||
# ── C++ Standard ──
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
|
||||
# ── Build acceleration ──
|
||||
# ccache: auto-detect and use if available
|
||||
find_program(CCACHE_PROGRAM ccache)
|
||||
if(CCACHE_PROGRAM)
|
||||
set(CMAKE_C_COMPILER_LAUNCHER "${CCACHE_PROGRAM}")
|
||||
set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_PROGRAM}")
|
||||
message(STATUS "ccache found: ${CCACHE_PROGRAM}")
|
||||
else()
|
||||
message(STATUS "ccache not found — install it for faster rebuilds")
|
||||
endif()
|
||||
|
||||
# Unity (jumbo) build: batch source files to reduce header parsing overhead
|
||||
option(ENABLE_UNITY_BUILD "Enable CMake unity (jumbo) builds" OFF)
|
||||
if(ENABLE_UNITY_BUILD)
|
||||
set(CMAKE_UNITY_BUILD ON)
|
||||
set(CMAKE_UNITY_BUILD_BATCH_SIZE 8)
|
||||
endif()
|
||||
|
||||
# ── Output directories ──
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
|
||||
|
||||
# ── Custom module path ──
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
|
||||
|
||||
# ── User-facing options ──
|
||||
option(BUILD_QT "Build triangles-qt (Qt5 GUI wallet)" ON)
|
||||
option(BUILD_DAEMON "Build trianglesd (headless daemon)" ON)
|
||||
option(BUILD_TESTS "Build test_triangles (Boost.Test unit tests)" ON)
|
||||
option(USE_UPNP "Enable UPnP support via miniupnpc" ON)
|
||||
option(USE_IPV6 "Enable IPv6 support" ON)
|
||||
option(USE_QRCODE "Enable QR code generation via libqrencode" OFF)
|
||||
option(USE_DBUS "Enable D-Bus notifications (Linux only)" ON)
|
||||
option(USE_ZMQ "Enable ZMQ publisher support" OFF)
|
||||
option(USE_TOR_EMBEDDED "Enable embedded Tor library linking" OFF)
|
||||
option(USE_O3 "Use -O3 optimization instead of -O2" OFF)
|
||||
option(ENABLE_PIE "Build position-independent executables" OFF)
|
||||
option(ENABLE_STATIC "Prefer static linking (Linux release builds)" OFF)
|
||||
|
||||
# Cache variables for custom dependency paths
|
||||
set(BDB_INCLUDE_PATH "" CACHE PATH "Path to Berkeley DB headers")
|
||||
set(BDB_LIB_PATH "" CACHE PATH "Path to Berkeley DB libraries")
|
||||
set(EVENT_INCLUDE_PATH "" CACHE PATH "Path to libevent headers")
|
||||
set(EVENT_LIB_PATH "" CACHE PATH "Path to libevent libraries")
|
||||
set(MINIUPNPC_INCLUDE_PATH "" CACHE PATH "Path to miniupnpc headers")
|
||||
set(MINIUPNPC_LIB_PATH "" CACHE PATH "Path to miniupnpc libraries")
|
||||
set(TOR_SOURCE_ROOT "" CACHE PATH "Path to Tor source tree (for USE_TOR_EMBEDDED)")
|
||||
|
||||
# ── Compiler/linker flags ──
|
||||
include(AddCompilerFlags)
|
||||
|
||||
# ── Find required dependencies ──
|
||||
find_package(OpenSSL REQUIRED)
|
||||
find_package(Boost 1.71 REQUIRED COMPONENTS
|
||||
filesystem program_options thread chrono
|
||||
)
|
||||
if(BUILD_TESTS)
|
||||
find_package(Boost REQUIRED COMPONENTS unit_test_framework)
|
||||
endif()
|
||||
find_package(BerkeleyDB REQUIRED)
|
||||
find_package(Libevent REQUIRED)
|
||||
find_package(ZLIB REQUIRED)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
# ── Find optional dependencies ──
|
||||
if(USE_UPNP)
|
||||
find_package(Miniupnpc REQUIRED)
|
||||
endif()
|
||||
|
||||
if(USE_QRCODE)
|
||||
find_package(QRencode REQUIRED)
|
||||
endif()
|
||||
|
||||
if(USE_ZMQ)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(ZMQ REQUIRED IMPORTED_TARGET libzmq)
|
||||
endif()
|
||||
|
||||
if(BUILD_QT)
|
||||
find_package(Qt5 5.9 REQUIRED COMPONENTS Core Gui Widgets)
|
||||
find_package(Qt5 COMPONENTS LinguistTools QUIET)
|
||||
if(USE_DBUS AND UNIX AND NOT APPLE)
|
||||
find_package(Qt5 COMPONENTS DBus QUIET)
|
||||
if(NOT Qt5DBus_FOUND)
|
||||
message(STATUS "Qt5 DBus not found -- disabling D-Bus notifications")
|
||||
set(USE_DBUS OFF CACHE BOOL "" FORCE)
|
||||
endif()
|
||||
else()
|
||||
set(USE_DBUS OFF CACHE BOOL "" FORCE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# ── Build bundled LevelDB ──
|
||||
include(BuildLevelDB)
|
||||
|
||||
# ── Generate build.h from git describe ──
|
||||
include(GenerateBuildInfo)
|
||||
|
||||
# ── Descend into source tree ──
|
||||
add_subdirectory(src)
|
||||
|
||||
# ── Configuration summary ──
|
||||
message(STATUS "")
|
||||
message(STATUS "Triangles ${PROJECT_VERSION} build configuration:")
|
||||
message(STATUS " Build Qt GUI: ${BUILD_QT}")
|
||||
message(STATUS " Build daemon: ${BUILD_DAEMON}")
|
||||
message(STATUS " Build tests: ${BUILD_TESTS}")
|
||||
message(STATUS " UPnP: ${USE_UPNP}")
|
||||
message(STATUS " IPv6: ${USE_IPV6}")
|
||||
message(STATUS " QR code: ${USE_QRCODE}")
|
||||
message(STATUS " D-Bus: ${USE_DBUS}")
|
||||
message(STATUS " ZMQ: ${USE_ZMQ}")
|
||||
message(STATUS " Embedded Tor: ${USE_TOR_EMBEDDED}")
|
||||
message(STATUS " Static linking: ${ENABLE_STATIC}")
|
||||
message(STATUS " ccache: ${CCACHE_PROGRAM}")
|
||||
message(STATUS " Unity build: ${ENABLE_UNITY_BUILD}")
|
||||
message(STATUS " Precompiled header: ON")
|
||||
message(STATUS "")
|
||||
@@ -2,7 +2,7 @@ FROM ubuntu:22.04
|
||||
|
||||
LABEL maintainer="Cryptographic Triangles Team"
|
||||
LABEL description="Cryptographic Triangles (TRI) headless daemon"
|
||||
LABEL version="5.1.5"
|
||||
LABEL version="5.7.6"
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
ca-certificates \
|
||||
@@ -19,8 +19,9 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
tor \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ARG VERSION=5.7.6
|
||||
RUN curl -L -o /usr/local/bin/trianglesd \
|
||||
https://github.com/SamiAhmed7777/triangles_v5/releases/download/v5.1.5/trianglesd-linux \
|
||||
https://github.com/SamiAhmed7777/triangles_v5/releases/download/v${VERSION}/Cryptographic-Triangles-v${VERSION}-linux-x64-daemon \
|
||||
&& chmod +x /usr/local/bin/trianglesd
|
||||
|
||||
RUN useradd -m -s /bin/bash triangles
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
# Triangles Modernization Roadmap
|
||||
|
||||
**Goal:** Make TRI faster to sync, safer for wallets, and more useful as a currency — without breaking consensus.
|
||||
|
||||
**Invariant:** Any change that modifies block validation, stake modifier computation, transaction format, or signature verification MUST preserve exact consensus with existing v5.x nodes. When in doubt, test against a synced v5.8.1 node.
|
||||
|
||||
---
|
||||
|
||||
## Priority 1: Faster Syncing (High Impact, Low Risk)
|
||||
|
||||
### 1.1 Update Checkpoints (Easy, Immediate)
|
||||
**Problem:** Last hardcoded checkpoint is at block 2,186,940. `IsInitialBlockDownload()` returns false past this point, causing orphan limit to drop from 4000 to 750 — exactly what caused the fork deadlock.
|
||||
|
||||
**Fix:** Add checkpoints every ~50,000 blocks up to current height (~2,207,000+).
|
||||
```cpp
|
||||
// src/checkpoints.cpp - add recent checkpoints
|
||||
{2190000, uint256("...")},
|
||||
{2195000, uint256("...")},
|
||||
{2200000, uint256("...")},
|
||||
{2205000, uint256("...")},
|
||||
{2210000, uint256("...")},
|
||||
```
|
||||
**Risk:** None — checkpoints are only used for IBD detection and quick rejection of clearly wrong chains.
|
||||
|
||||
### 1.2 Increase Post-Checkpoint Orphan Limit (Easy)
|
||||
**Problem:** 750 orphans after IBD is too low for a low-peer network. During the fork incident, 750 orphans filled up and the node deadlocked.
|
||||
|
||||
**Fix:**
|
||||
```cpp
|
||||
// src/main.h
|
||||
static const unsigned int MAX_ORPHAN_BLOCKS = 2000; // was 750
|
||||
```
|
||||
**Risk:** Slightly more memory usage during forks. Worth it for resilience.
|
||||
|
||||
### 1.3 Parallel Block Download (Medium Effort)
|
||||
**Problem:** Current implementation downloads blocks sequentially from one peer at a time during IBD.
|
||||
|
||||
**Fix:** Increase batch sizes and allow concurrent block downloads from multiple peers:
|
||||
```cpp
|
||||
// src/main.cpp
|
||||
// During IBD, request blocks from multiple peers simultaneously
|
||||
unsigned int nGetDataBatchSize = IsInitialBlockDownload() ? 8000 : 1000; // was 4000
|
||||
```
|
||||
**Risk:** Low — larger batch sizes are already proven in Bitcoin forks.
|
||||
|
||||
### 1.4 Header-First Sync (Medium Effort)
|
||||
**Problem:** Node downloads full blocks before validating headers. A bad peer can waste bandwidth.
|
||||
|
||||
**Fix:** Download and validate all headers first (compact ~80 bytes each), then download full blocks only for the best chain.
|
||||
- Separate `getheaders`/`headers` message handling
|
||||
- Download blocks only for the best header chain
|
||||
- Reduces wasted bandwidth during forks by 95%+
|
||||
|
||||
### 1.5 Bootstrap Over HTTPS with Resume (Easy)
|
||||
**Problem:** Built-in bootstrap (`-bootstrap`) uses raw TCP and can't resume interrupted downloads.
|
||||
|
||||
**Fix:** The existing `bootstrap.cpp` already supports downloading. Add:
|
||||
- Resume support (Range headers)
|
||||
- SHA256 verification of downloaded archive
|
||||
- Better progress reporting
|
||||
- Fallback mirrors
|
||||
|
||||
---
|
||||
|
||||
## Priority 2: Wallet Safety (Critical)
|
||||
|
||||
### 2.1 Automatic Wallet Backup Before Dangerous Operations (Easy)
|
||||
**Problem:** Corrupt wallet = lost funds. No automatic backup before risky operations.
|
||||
|
||||
**Fix:** In `walletdb.cpp`, before any rewrite:
|
||||
```cpp
|
||||
// Before wallet.dat rewrite, copy to wallet.dat.bak
|
||||
if (boost::filesystem::exists(pathWallet)) {
|
||||
boost::filesystem::copy_file(pathWallet, pathWallet + ".bak",
|
||||
boost::filesystem::copy_option::overwrite_if_exists);
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2 Detect and Report BDB Corruption (Easy)
|
||||
**Problem:** BDB corruption silently corrupts wallet. User doesn't know until it's too late.
|
||||
|
||||
**Fix:** Add wallet integrity check on load:
|
||||
```cpp
|
||||
// In CWallet::LoadWallet()
|
||||
// After opening, verify BDB environment is healthy
|
||||
// If DB_RUNRECOVERY, auto-salvage and warn user
|
||||
```
|
||||
|
||||
### 2.3 Wallet.dat Versioning (Medium Effort)
|
||||
**Problem:** Single wallet.dat file. If it corrupts during write, funds are lost.
|
||||
|
||||
**Fix:** Implement copy-on-write wallet saves:
|
||||
- Write new wallet data to `wallet.dat.new`
|
||||
- Atomically rename `wallet.dat` → `wallet.dat.old`, `wallet.dat.new` → `wallet.dat`
|
||||
- Keep last 3 wallet revisions
|
||||
- On load, try wallet.dat first, fall back to wallet.dat.old if corrupt
|
||||
|
||||
### 2.4 Seed Phrase / HD Wallet (High Effort, High Impact)
|
||||
**Problem:** Losing wallet.dat = losing everything. No recovery mechanism.
|
||||
|
||||
**Fix:** Implement BIP39/BIP44 HD wallet as optional upgrade:
|
||||
- Generate 12/24-word seed phrase on new wallet creation
|
||||
- Derive all keys from seed deterministically
|
||||
- Import seed on any device to recover wallet
|
||||
- Keep backward compatibility with existing non-HD wallets
|
||||
|
||||
---
|
||||
|
||||
## Priority 3: Network Resilience (Medium Impact)
|
||||
|
||||
### 3.1 Better Peer Management (Medium Effort)
|
||||
**Problem:** Low peer counts (2-6) lead to fork divergence. No prioritization of reliable peers.
|
||||
|
||||
**Fix:**
|
||||
- Peer reliability scoring (track which peers provide valid blocks)
|
||||
- Prefer peers that are ahead and on the same chain
|
||||
- Automatic disconnection of stale/forked peers
|
||||
- Increase default `maxconnections` from 64 to 128
|
||||
|
||||
### 3.2 Compact Block Relay (High Effort)
|
||||
**Problem:** Full blocks are sent even when the receiver likely already has most transactions.
|
||||
|
||||
**Fix:** Implement BIP 152 compact blocks:
|
||||
- Send block header + short transaction IDs
|
||||
- Receiver fills in from mempool, only requests missing transactions
|
||||
- Reduces bandwidth by ~90% during normal operation
|
||||
|
||||
### 3.3 DNS Seed Infrastructure (Easy)
|
||||
**Problem:** `dnsseed=0` when Tor-only means no automatic peer discovery.
|
||||
|
||||
**Fix:** Run a DNS seed server that resolves to known reliable onion addresses:
|
||||
```
|
||||
seed.cryptographic-triangles.org → returns onion addresses of healthy nodes
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Priority 4: User Experience (Medium Impact)
|
||||
|
||||
### 4.1 Progress Reporting for IBD (Easy)
|
||||
**Problem:** Users see "downloading blocks..." with no useful progress indicator.
|
||||
|
||||
**Fix:**
|
||||
- Report `headers` vs `blocks` progress separately
|
||||
- Show estimated time remaining based on download speed
|
||||
- Log progress every 1000 blocks (currently every 5000)
|
||||
- Qt wallet: update progress bar more frequently
|
||||
|
||||
### 4.2 Staking Dashboard Improvements (Easy)
|
||||
**Problem:** Qt wallet shows staking info but not clearly.
|
||||
|
||||
**Fix:**
|
||||
- Show expected time to stake more prominently
|
||||
- Display staking weight as percentage of network
|
||||
- Notify when stake is found (system notification)
|
||||
- Show "staking" indicator in system tray
|
||||
|
||||
### 4.3 Transaction Fee Estimation (Medium Effort)
|
||||
**Problem:** No fee estimation. Users guess.
|
||||
|
||||
**Fix:** Track recent block inclusion rates by fee level, provide fee recommendations.
|
||||
|
||||
---
|
||||
|
||||
## Priority 5: Code Modernization (Low Urgency, Good Hygiene)
|
||||
|
||||
### 5.1 C++17/20 Features
|
||||
- Replace raw pointers with smart pointers where safe
|
||||
- Use `std::optional`, `std::string_view`, `std::filesystem`
|
||||
- Replace boost::filesystem with std::filesystem (C++17)
|
||||
|
||||
### 5.2 Build System
|
||||
- CMake is already in place (good)
|
||||
- Add sanitizers (ASAN, UBSAN) to CI
|
||||
- Static analysis with clang-tidy
|
||||
|
||||
### 5.3 Testing
|
||||
- Current test coverage is thin
|
||||
- Add unit tests for:
|
||||
- Checkpoint validation
|
||||
- Stake modifier computation
|
||||
- Bootstrap download/resume
|
||||
- Wallet BDB recovery
|
||||
- Orphan block handling
|
||||
|
||||
---
|
||||
|
||||
## What NOT to Change
|
||||
|
||||
These are consensus-critical and must remain identical:
|
||||
- Block validation rules
|
||||
- Stake modifier computation (`ComputeNextStakeModifier`)
|
||||
- Transaction signature verification
|
||||
- Block reward schedule
|
||||
- PoW/PoS target computation
|
||||
- Chain trust / difficulty adjustment
|
||||
- Message serialization format
|
||||
- Protocol version handshaking
|
||||
|
||||
Any change to these requires a coordinated network upgrade (hard fork).
|
||||
|
||||
---
|
||||
|
||||
## Implementation Order
|
||||
|
||||
1. **This week:** Update checkpoints (1.1), increase orphan limit (1.2), wallet backup before save (2.1)
|
||||
2. **Next week:** Better progress reporting (4.1), increase batch size (1.3)
|
||||
3. **Month 1:** Wallet versioning (2.3), bootstrap resume (1.5)
|
||||
4. **Month 2:** Header-first sync (1.4), peer reliability (3.1)
|
||||
5. **Month 3+:** HD wallet (2.4), compact blocks (3.2)
|
||||
@@ -1,284 +0,0 @@
|
||||
#############################################################################
|
||||
# Makefile for building: triangles-qt
|
||||
# Generated by qmake (3.1) (Qt 5.15.18)
|
||||
# Project: triangles-qt.pro
|
||||
# Template: app
|
||||
# Command: C:/msys64/mingw64/bin/qmake-qt5.exe -o Makefile triangles-qt.pro
|
||||
#############################################################################
|
||||
|
||||
MAKEFILE = Makefile
|
||||
|
||||
EQ = =
|
||||
|
||||
first: release
|
||||
install: release-install
|
||||
uninstall: release-uninstall
|
||||
QMAKE = C:/msys64/mingw64/bin/qmake-qt5.exe
|
||||
DEL_FILE = rm -f
|
||||
CHK_DIR_EXISTS= test -d
|
||||
MKDIR = mkdir -p
|
||||
COPY = cp -f
|
||||
COPY_FILE = cp -f
|
||||
COPY_DIR = cp -f -R
|
||||
INSTALL_FILE = cp -f
|
||||
INSTALL_PROGRAM = cp -f
|
||||
INSTALL_DIR = cp -f -R
|
||||
QINSTALL = C:/msys64/mingw64/bin/qmake-qt5.exe -install qinstall
|
||||
QINSTALL_PROGRAM = C:/msys64/mingw64/bin/qmake-qt5.exe -install qinstall -exe
|
||||
DEL_FILE = rm -f
|
||||
SYMLINK = $(QMAKE) -install ln -f -s
|
||||
DEL_DIR = rmdir
|
||||
MOVE = mv -f
|
||||
IDC = idc
|
||||
IDL = widl
|
||||
ZIP =
|
||||
DEF_FILE =
|
||||
RES_FILE = build/triangles-qt_res.o
|
||||
SED = sed
|
||||
MOVE = mv -f
|
||||
SUBTARGETS = \
|
||||
release \
|
||||
debug
|
||||
|
||||
|
||||
release: FORCE
|
||||
$(MAKE) -f $(MAKEFILE).Release
|
||||
release-make_first: FORCE
|
||||
$(MAKE) -f $(MAKEFILE).Release
|
||||
release-all: FORCE
|
||||
$(MAKE) -f $(MAKEFILE).Release all
|
||||
release-clean: FORCE
|
||||
$(MAKE) -f $(MAKEFILE).Release clean
|
||||
release-distclean: FORCE
|
||||
$(MAKE) -f $(MAKEFILE).Release distclean
|
||||
release-install: FORCE
|
||||
$(MAKE) -f $(MAKEFILE).Release install
|
||||
release-uninstall: FORCE
|
||||
$(MAKE) -f $(MAKEFILE).Release uninstall
|
||||
debug: FORCE
|
||||
$(MAKE) -f $(MAKEFILE).Debug
|
||||
debug-make_first: FORCE
|
||||
$(MAKE) -f $(MAKEFILE).Debug
|
||||
debug-all: FORCE
|
||||
$(MAKE) -f $(MAKEFILE).Debug all
|
||||
debug-clean: FORCE
|
||||
$(MAKE) -f $(MAKEFILE).Debug clean
|
||||
debug-distclean: FORCE
|
||||
$(MAKE) -f $(MAKEFILE).Debug distclean
|
||||
debug-install: FORCE
|
||||
$(MAKE) -f $(MAKEFILE).Debug install
|
||||
debug-uninstall: FORCE
|
||||
$(MAKE) -f $(MAKEFILE).Debug uninstall
|
||||
|
||||
Makefile: triangles-qt.pro C:/msys64/mingw64/share/qt5/mkspecs/win32-g++/qmake.conf C:/msys64/mingw64/share/qt5/mkspecs/features/spec_pre.prf \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/qdevice.pri \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/device_config.prf \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/common/sanitize.conf \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/common/gcc-base.conf \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/common/g++-base.conf \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/common/angle.conf \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/win32/windows_vulkan_sdk.prf \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/common/windows-vulkan.conf \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/common/g++-win32.conf \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/common/windows-desktop.conf \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/qconfig.pri \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_accessibility_support_private.pri \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_bootstrap_private.pri \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_concurrent.pri \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_concurrent_private.pri \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_core.pri \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_core_private.pri \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_dbus.pri \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_dbus_private.pri \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_designer.pri \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_designer_private.pri \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_designercomponents_private.pri \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_devicediscovery_support_private.pri \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_edid_support_private.pri \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_eventdispatcher_support_private.pri \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_fb_support_private.pri \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_fontdatabase_support_private.pri \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_gui.pri \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_gui_private.pri \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_help.pri \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_help_private.pri \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_network.pri \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_network_private.pri \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_opengl.pri \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_opengl_private.pri \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_openglextensions.pri \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_openglextensions_private.pri \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_platformcompositor_support_private.pri \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_printsupport.pri \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_printsupport_private.pri \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_sql.pri \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_sql_private.pri \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_testlib.pri \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_testlib_private.pri \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_theme_support_private.pri \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_uiplugin.pri \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_uitools.pri \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_uitools_private.pri \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_vulkan_support_private.pri \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_widgets.pri \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_widgets_private.pri \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_windowsuiautomation_support_private.pri \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_xml.pri \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_xml_private.pri \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/qt_functions.prf \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/qt_config.prf \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/win32-g++/qmake.conf \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/spec_post.prf \
|
||||
.qmake.stash \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/exclusive_builds.prf \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/toolchain.prf \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/default_pre.prf \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/win32/default_pre.prf \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/resolve_config.prf \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/exclusive_builds_post.prf \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/default_post.prf \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/precompile_header.prf \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/warn_on.prf \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/qt.prf \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/resources_functions.prf \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/resources.prf \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/moc.prf \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/win32/opengl.prf \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/uic.prf \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/qmake_use.prf \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/file_copies.prf \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/win32/windows.prf \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/testcase_targets.prf \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/exceptions.prf \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/yacc.prf \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/lex.prf \
|
||||
triangles-qt.pro \
|
||||
C:/msys64/mingw64/lib/qtmain.prl \
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/build_pass.prf \
|
||||
src/qt/triangles.qrc
|
||||
$(QMAKE) -o Makefile triangles-qt.pro
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/spec_pre.prf:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/qdevice.pri:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/device_config.prf:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/common/sanitize.conf:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/common/gcc-base.conf:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/common/g++-base.conf:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/common/angle.conf:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/win32/windows_vulkan_sdk.prf:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/common/windows-vulkan.conf:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/common/g++-win32.conf:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/common/windows-desktop.conf:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/qconfig.pri:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_accessibility_support_private.pri:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_bootstrap_private.pri:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_concurrent.pri:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_concurrent_private.pri:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_core.pri:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_core_private.pri:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_dbus.pri:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_dbus_private.pri:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_designer.pri:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_designer_private.pri:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_designercomponents_private.pri:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_devicediscovery_support_private.pri:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_edid_support_private.pri:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_eventdispatcher_support_private.pri:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_fb_support_private.pri:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_fontdatabase_support_private.pri:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_gui.pri:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_gui_private.pri:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_help.pri:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_help_private.pri:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_network.pri:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_network_private.pri:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_opengl.pri:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_opengl_private.pri:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_openglextensions.pri:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_openglextensions_private.pri:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_platformcompositor_support_private.pri:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_printsupport.pri:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_printsupport_private.pri:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_sql.pri:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_sql_private.pri:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_testlib.pri:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_testlib_private.pri:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_theme_support_private.pri:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_uiplugin.pri:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_uitools.pri:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_uitools_private.pri:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_vulkan_support_private.pri:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_widgets.pri:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_widgets_private.pri:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_windowsuiautomation_support_private.pri:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_xml.pri:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/modules/qt_lib_xml_private.pri:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/qt_functions.prf:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/qt_config.prf:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/win32-g++/qmake.conf:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/spec_post.prf:
|
||||
.qmake.stash:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/exclusive_builds.prf:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/toolchain.prf:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/default_pre.prf:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/win32/default_pre.prf:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/resolve_config.prf:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/exclusive_builds_post.prf:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/default_post.prf:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/precompile_header.prf:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/warn_on.prf:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/qt.prf:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/resources_functions.prf:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/resources.prf:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/moc.prf:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/win32/opengl.prf:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/uic.prf:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/qmake_use.prf:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/file_copies.prf:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/win32/windows.prf:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/testcase_targets.prf:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/exceptions.prf:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/yacc.prf:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/lex.prf:
|
||||
triangles-qt.pro:
|
||||
C:/msys64/mingw64/lib/qtmain.prl:
|
||||
C:/msys64/mingw64/share/qt5/mkspecs/features/build_pass.prf:
|
||||
src/qt/triangles.qrc:
|
||||
qmake: FORCE
|
||||
@$(QMAKE) -o Makefile triangles-qt.pro
|
||||
|
||||
qmake_all: FORCE
|
||||
|
||||
make_first: release-make_first debug-make_first FORCE
|
||||
all: release-all debug-all FORCE
|
||||
clean: release-clean debug-clean FORCE
|
||||
-$(DEL_FILE) E:/repos/triangles/src/leveldb/libleveldb.a;
|
||||
-$(DEL_FILE) cd
|
||||
-$(DEL_FILE) E:/repos/triangles/src/leveldb
|
||||
-$(DEL_FILE) ;
|
||||
-$(DEL_FILE) clean
|
||||
distclean: release-distclean debug-distclean FORCE
|
||||
-$(DEL_FILE) Makefile
|
||||
-$(DEL_FILE) .qmake.stash
|
||||
|
||||
E:/repos/triangles/src/leveldb/libleveldb.a: FORCE
|
||||
cd E:/repos/triangles/src/leveldb && CC=gcc CXX=g++ TARGET_OS=OS_WINDOWS_CROSSCOMPILE $(MAKE) OPT="-fno-keep-inline-dllexport -march=nocona -msahf -mtune=generic -Wa,-mbig-obj -O2" libleveldb.a libmemenv.a && ranlib E:/repos/triangles/src/leveldb/libleveldb.a && ranlib E:/repos/triangles/src/leveldb/libmemenv.a
|
||||
|
||||
release-mocclean:
|
||||
$(MAKE) -f $(MAKEFILE).Release mocclean
|
||||
debug-mocclean:
|
||||
$(MAKE) -f $(MAKEFILE).Debug mocclean
|
||||
mocclean: release-mocclean debug-mocclean
|
||||
|
||||
release-mocables:
|
||||
$(MAKE) -f $(MAKEFILE).Release mocables
|
||||
debug-mocables:
|
||||
$(MAKE) -f $(MAKEFILE).Debug mocables
|
||||
mocables: release-mocables debug-mocables
|
||||
|
||||
check: first
|
||||
|
||||
benchmark: first
|
||||
FORCE:
|
||||
|
||||
$(MAKEFILE).Release: Makefile
|
||||
$(MAKEFILE).Debug: Makefile
|
||||
@@ -0,0 +1,300 @@
|
||||
# OpenClaw Bootstrap Snapshot Guide
|
||||
|
||||
## Purpose
|
||||
|
||||
This document tells OpenClaw exactly how to update the existing Triangles bootstrap server so new wallets download a ready-to-use snapshot instead of downloading `blk0001.dat` and rebuilding the index locally.
|
||||
|
||||
This guide matches the current wallet code in:
|
||||
|
||||
- `src/bootstrap.cpp`
|
||||
- `src/bootstrap.h`
|
||||
- `src/checkpoints.cpp`
|
||||
- `src/version.h`
|
||||
|
||||
## What The Wallet Actually Does
|
||||
|
||||
When a fresh wallet bootstraps, it:
|
||||
|
||||
1. Downloads `http://bootstrap.cryptographic-triangles.org/bootstrap.tar.gz`
|
||||
2. Extracts it into the data directory
|
||||
3. Requires `blk0001.dat` to exist after extraction
|
||||
4. Looks for `txleveldb/` and `snapshot.manifest`
|
||||
5. Keeps `txleveldb/` only if `snapshot.manifest` passes verification
|
||||
6. Deletes `txleveldb/` if verification fails, then rebuilds from `blk0001.dat`
|
||||
7. Always deletes `database/` from the extracted snapshot
|
||||
|
||||
The verification rules are strict:
|
||||
|
||||
- `format` must be `1`
|
||||
- `network` must be `main` on mainnet
|
||||
- `dbversion` must be `70509`
|
||||
- `height` and `hash` must exactly match a hardcoded checkpoint
|
||||
|
||||
If any of those checks fail, the wallet throws away the shipped `txleveldb/`.
|
||||
|
||||
## Current Hardcoded Mainnet Checkpoint
|
||||
|
||||
As of the current codebase, the latest hardcoded mainnet checkpoint is:
|
||||
|
||||
- Height: `2186940`
|
||||
- Hash: `bd952e8d4a612e336d840ad924a7e09395e36bcd9d929b302e47e60b5c3098c0`
|
||||
|
||||
OpenClaw must not generate a manifest with an arbitrary tip hash. The manifest only survives if it matches a hardcoded checkpoint from `src/checkpoints.cpp`.
|
||||
|
||||
## Important Limitation
|
||||
|
||||
If the live chain tip is past the latest hardcoded checkpoint, OpenClaw has two valid options:
|
||||
|
||||
1. Publish a snapshot taken exactly at the latest hardcoded checkpoint
|
||||
2. Publish `blk0001.dat` only, without `txleveldb/`, and let clients rebuild locally
|
||||
|
||||
OpenClaw must not publish a `snapshot.manifest` for a height/hash that is not compiled into the wallet.
|
||||
|
||||
## Files OpenClaw Should Publish
|
||||
|
||||
The preferred `bootstrap.tar.gz` should contain:
|
||||
|
||||
- `blk0001.dat`
|
||||
- `txleveldb/`
|
||||
- `snapshot.manifest`
|
||||
- optionally `peers.dat`
|
||||
|
||||
It must not contain:
|
||||
|
||||
- `wallet.dat`
|
||||
- `database/`
|
||||
- `.lock`
|
||||
- pid files
|
||||
- logs
|
||||
- Tor state
|
||||
|
||||
Legacy fallback files should still exist on the web root:
|
||||
|
||||
- `blk0001.dat`
|
||||
- `filelist.txt`
|
||||
|
||||
## Requirements For The Source Node
|
||||
|
||||
Before building a snapshot, the source node should be:
|
||||
|
||||
- fully synced
|
||||
- cleanly shut down before copying files
|
||||
- built from the same code/version expected by clients
|
||||
- using the same LevelDB schema as the client (`DATABASE_VERSION=70509`)
|
||||
|
||||
Recommended node config for the source snapshot node:
|
||||
|
||||
```ini
|
||||
txindex=1
|
||||
addressindex=1
|
||||
daemon=1
|
||||
server=1
|
||||
```
|
||||
|
||||
`addressindex=1` is recommended so clients that enable address index can benefit from faster indexed wallet rescans and address RPCs immediately.
|
||||
|
||||
## OpenClaw Workflow
|
||||
|
||||
### Step 1: Decide Whether A Prebuilt Index Is Allowed
|
||||
|
||||
OpenClaw must first decide whether it can ship `txleveldb/`.
|
||||
|
||||
Rules:
|
||||
|
||||
- If the snapshot node is exactly at checkpoint `2186940`, shipping `txleveldb/` is allowed
|
||||
- If the snapshot node is above `2186940` and the code has not been updated with a newer checkpoint, do not ship `txleveldb/`
|
||||
- In that case, publish a blocks-only bootstrap instead
|
||||
|
||||
### Step 2: Stop The Source Node Cleanly
|
||||
|
||||
Never copy a live LevelDB directory.
|
||||
|
||||
```bash
|
||||
trianglesd stop
|
||||
sleep 10
|
||||
pgrep -af trianglesd || true
|
||||
```
|
||||
|
||||
OpenClaw should confirm the daemon is fully stopped before copying `txleveldb/`.
|
||||
|
||||
### Step 3: Create A Staging Directory
|
||||
|
||||
```bash
|
||||
rm -rf /tmp/triangles-bootstrap-stage
|
||||
mkdir -p /tmp/triangles-bootstrap-stage
|
||||
```
|
||||
|
||||
### Step 4: Copy Snapshot Files
|
||||
|
||||
For a verified snapshot:
|
||||
|
||||
```bash
|
||||
cp ~/.triangles/blk0001.dat /tmp/triangles-bootstrap-stage/
|
||||
cp -a ~/.triangles/txleveldb /tmp/triangles-bootstrap-stage/
|
||||
test -f ~/.triangles/peers.dat && cp ~/.triangles/peers.dat /tmp/triangles-bootstrap-stage/
|
||||
```
|
||||
|
||||
Do not copy:
|
||||
|
||||
```bash
|
||||
rm -rf /tmp/triangles-bootstrap-stage/database
|
||||
rm -f /tmp/triangles-bootstrap-stage/wallet.dat
|
||||
rm -f /tmp/triangles-bootstrap-stage/.lock
|
||||
rm -f /tmp/triangles-bootstrap-stage/*.pid
|
||||
rm -f /tmp/triangles-bootstrap-stage/debug.log
|
||||
```
|
||||
|
||||
### Step 5: Write `snapshot.manifest`
|
||||
|
||||
If OpenClaw is publishing a verified prebuilt index, write:
|
||||
|
||||
```bash
|
||||
cat > /tmp/triangles-bootstrap-stage/snapshot.manifest << 'EOF'
|
||||
format=1
|
||||
network=main
|
||||
height=2186940
|
||||
hash=bd952e8d4a612e336d840ad924a7e09395e36bcd9d929b302e47e60b5c3098c0
|
||||
dbversion=70509
|
||||
EOF
|
||||
```
|
||||
|
||||
Rules:
|
||||
|
||||
- `hash` must not include `0x`
|
||||
- `network` must be `main`
|
||||
- `dbversion` must be `70509`
|
||||
- If OpenClaw is publishing blocks-only bootstrap, it should omit `snapshot.manifest` entirely
|
||||
|
||||
### Step 6: Build The Tarball
|
||||
|
||||
```bash
|
||||
cd /tmp/triangles-bootstrap-stage
|
||||
tar czf /tmp/bootstrap.tar.gz .
|
||||
```
|
||||
|
||||
### Step 7: Publish To The Existing Bootstrap Server
|
||||
|
||||
This guide assumes the existing nginx root is:
|
||||
|
||||
- `/var/www/triangles-bootstrap`
|
||||
|
||||
Publish the preferred tarball and the legacy fallback files:
|
||||
|
||||
```bash
|
||||
sudo mkdir -p /var/www/triangles-bootstrap
|
||||
sudo mv /tmp/bootstrap.tar.gz /var/www/triangles-bootstrap/bootstrap.tar.gz
|
||||
sudo cp ~/.triangles/blk0001.dat /var/www/triangles-bootstrap/blk0001.dat
|
||||
printf "blk0001.dat\n" | sudo tee /var/www/triangles-bootstrap/filelist.txt > /dev/null
|
||||
sudo chown -R www-data:www-data /var/www/triangles-bootstrap
|
||||
```
|
||||
|
||||
If OpenClaw is publishing a blocks-only bootstrap, the commands are the same except the tarball should contain only `blk0001.dat` and optional `peers.dat`.
|
||||
|
||||
## Validation Checklist
|
||||
|
||||
Before marking the update complete, OpenClaw should verify:
|
||||
|
||||
### Tarball contents
|
||||
|
||||
```bash
|
||||
tar tzf /var/www/triangles-bootstrap/bootstrap.tar.gz | sort
|
||||
```
|
||||
|
||||
Expected for verified snapshot:
|
||||
|
||||
- `./blk0001.dat`
|
||||
- `./txleveldb/...`
|
||||
- `./snapshot.manifest`
|
||||
|
||||
Expected not to exist:
|
||||
|
||||
- `wallet.dat`
|
||||
- `database/`
|
||||
|
||||
### HTTP responses
|
||||
|
||||
```bash
|
||||
curl -I http://localhost/bootstrap.tar.gz
|
||||
curl -I http://localhost/blk0001.dat
|
||||
curl http://localhost/filelist.txt
|
||||
```
|
||||
|
||||
Expected:
|
||||
|
||||
- HTTP `200`
|
||||
- `filelist.txt` contains `blk0001.dat`
|
||||
|
||||
### Manifest sanity
|
||||
|
||||
```bash
|
||||
tar xOf /var/www/triangles-bootstrap/bootstrap.tar.gz ./snapshot.manifest
|
||||
```
|
||||
|
||||
Expected:
|
||||
|
||||
- `format=1`
|
||||
- `network=main`
|
||||
- `height=2186940`
|
||||
- `hash=bd952e8d4a612e336d840ad924a7e09395e36bcd9d929b302e47e60b5c3098c0`
|
||||
- `dbversion=70509`
|
||||
|
||||
## Fresh-Client Test
|
||||
|
||||
OpenClaw should test the artifact on a clean machine or clean data directory:
|
||||
|
||||
```bash
|
||||
mv ~/.triangles ~/.triangles.backup.$(date +%s)
|
||||
mkdir -p ~/.triangles
|
||||
trianglesd -bootstrap
|
||||
```
|
||||
|
||||
Then inspect startup logs.
|
||||
|
||||
Successful verified snapshot behavior should include:
|
||||
|
||||
- snapshot downloaded
|
||||
- `snapshot.manifest found`
|
||||
- `manifest verified - keeping pre-built index`
|
||||
- no message about removing extracted `txleveldb/`
|
||||
|
||||
Failure behavior will include:
|
||||
|
||||
- manifest parse or verification failure
|
||||
- `removing extracted txleveldb/`
|
||||
- slow rebuild from `blk0001.dat`
|
||||
|
||||
## Safe Publish Procedure
|
||||
|
||||
OpenClaw should use this order:
|
||||
|
||||
1. Build snapshot in `/tmp`
|
||||
2. Validate tarball contents
|
||||
3. Replace `/var/www/triangles-bootstrap/bootstrap.tar.gz`
|
||||
4. Replace `/var/www/triangles-bootstrap/blk0001.dat`
|
||||
5. Replace `/var/www/triangles-bootstrap/filelist.txt`
|
||||
6. Confirm HTTP `200`
|
||||
|
||||
This avoids serving a half-written tarball.
|
||||
|
||||
## Example Bot Prompt
|
||||
|
||||
Use this exact tasking for OpenClaw:
|
||||
|
||||
```text
|
||||
Update the existing Triangles bootstrap server on bootstrap.cryptographic-triangles.org.
|
||||
|
||||
Rules:
|
||||
- Build the snapshot from a cleanly stopped source node
|
||||
- If the source node is exactly at hardcoded checkpoint 2186940 / bd952e8d4a612e336d840ad924a7e09395e36bcd9d929b302e47e60b5c3098c0, publish a verified snapshot containing blk0001.dat, txleveldb/, and snapshot.manifest
|
||||
- If the source node is above the latest hardcoded checkpoint, publish a blocks-only bootstrap and do not ship txleveldb/
|
||||
- Do not ship wallet.dat, database/, .lock, pid files, logs, or Tor state
|
||||
- Publish bootstrap.tar.gz, blk0001.dat, and filelist.txt to /var/www/triangles-bootstrap
|
||||
- Verify curl HTTP 200 for bootstrap.tar.gz and blk0001.dat
|
||||
- Report the tarball contents and whether the snapshot is verified or blocks-only
|
||||
```
|
||||
|
||||
## Recommended Next Improvement
|
||||
|
||||
This workflow will stay constrained until the next checkpoint is updated in `src/checkpoints.cpp`.
|
||||
|
||||
If you want OpenClaw to keep shipping prebuilt `txleveldb/` snapshots as the chain advances, the software needs periodic checkpoint updates. Without that, the verified snapshot path will stop at the latest compiled checkpoint and clients will fall back to rebuilds.
|
||||
@@ -1,4 +1,4 @@
|
||||
# Cryptographic Triangles (TRI) - v5.1.5
|
||||
# Cryptographic Triangles (TRI)
|
||||
|
||||
Triangles is a privacy-focused cryptocurrency featuring Proof-of-Stake consensus, Tor v3 onion routing, and built-in encrypted messaging. Originally launched in July 2014, the chain was revived in March 2026 after being frozen since December 2022.
|
||||
|
||||
@@ -16,7 +16,7 @@ Triangles is a privacy-focused cryptocurrency featuring Proof-of-Stake consensus
|
||||
|----------|-------|
|
||||
| Algorithm | Hash9 (PoW blocks 0-9000), PoS from block 9001 |
|
||||
| Block Time | ~120 seconds |
|
||||
| Max Supply | 222,222 TRI |
|
||||
| Max Supply | 2,222,222 TRI |
|
||||
| PoS Reward | 33% annual, coin-age based |
|
||||
| P2P Port | 24112 |
|
||||
| RPC Port | 19112 |
|
||||
@@ -24,48 +24,60 @@ Triangles is a privacy-focused cryptocurrency featuring Proof-of-Stake consensus
|
||||
|
||||
## Network Status
|
||||
|
||||
The Triangles network is live with seed nodes operating on both clearnet and Tor:
|
||||
|
||||
**Clearnet Seeds:**
|
||||
- `194.233.88.206:24112`
|
||||
- `74.208.167.19:24112`
|
||||
The Triangles network operates exclusively over Tor for privacy:
|
||||
|
||||
**Tor v3 Seeds:**
|
||||
- `gxvrhv3qitnc6kobrhsrse46bmcfitnybapor3or3oczzuxn6hfzxyid.onion:24112`
|
||||
- `futmtrvh6j34t7s6yjdxfia6iwuyfzwh4k5eqfof5kfhoqk3xmi3qoqd.onion:24112`
|
||||
- `jbpfhe7zw3qm67wy3j2ayysp3mnrjobopthnko3b3sgahqtecblwqmid.onion:24112`
|
||||
- `uddaxjbo3lh2zskg7w6gwln4ty5cel7q4c5jbx7fdtv6zf2j47gdlyad.onion:24112`
|
||||
- `el5sirhhleecuctpeeprelzubpqmoqivvra3rzlwbjttinxa4fq3wnid.onion:24112`
|
||||
- `sj5dhybnlp3v4y5niyc5unrnd6s43lyx5ibup7rolyosjbi2u2hsbvyd.onion:24112`
|
||||
- `i3kr5meha7se4ns3wss3h7v46m6uksfzv4wrohdqxpj6n35wyo2bvlid.onion:24112`
|
||||
|
||||
**DNS Seeds:**
|
||||
- `seed1.cryptographic-triangles.org`
|
||||
- `seed2.cryptographic-triangles.org`
|
||||
**HTTP Seed List:**
|
||||
- `seeds.cryptographic-triangles.org/seeds.txt` - Dynamically updated list of active onion peers
|
||||
|
||||
## Building from Source
|
||||
|
||||
Triangles uses CMake. All platforms follow the same build pattern.
|
||||
|
||||
### Dependencies
|
||||
|
||||
| Dependency | Minimum Version |
|
||||
|------------|----------------|
|
||||
| CMake | 3.16+ |
|
||||
| C++ compiler | C++17 support |
|
||||
| OpenSSL | 3.x |
|
||||
| Boost | 1.90+ |
|
||||
| Berkeley DB | 5.3 (with C++ bindings) |
|
||||
| libevent | 2.x |
|
||||
| LevelDB | bundled |
|
||||
|
||||
### Linux (Ubuntu 24.04 / Debian 12+)
|
||||
|
||||
Install dependencies:
|
||||
```bash
|
||||
sudo apt-get install -y build-essential libboost-all-dev libssl-dev \
|
||||
libdb5.3++-dev libevent-dev zlib1g-dev libminiupnpc-dev
|
||||
sudo apt-get install -y build-essential cmake ninja-build \
|
||||
libboost-all-dev libssl-dev libdb5.3++-dev libevent-dev \
|
||||
zlib1g-dev libminiupnpc-dev
|
||||
```
|
||||
|
||||
Build the daemon:
|
||||
For the Qt wallet, also install:
|
||||
```bash
|
||||
cd src/leveldb && make libleveldb.a libmemenv.a && cd ..
|
||||
make -j$(nproc) -f makefile.unix USE_UPNP=0
|
||||
strip trianglesd
|
||||
sudo apt-get install -y qtbase5-dev qt5-qmake libqrencode-dev
|
||||
```
|
||||
|
||||
Run the unit test suite:
|
||||
Build:
|
||||
```bash
|
||||
make -C src -f makefile.unix test
|
||||
cmake -B build -G Ninja -DBUILD_QT=ON
|
||||
cmake --build build
|
||||
```
|
||||
|
||||
### Linux (AlmaLinux 9 / RHEL 9)
|
||||
|
||||
Install dependencies:
|
||||
```bash
|
||||
sudo dnf install -y gcc-c++ make boost-devel openssl-devel libevent-devel \
|
||||
zlib-devel miniupnpc-devel
|
||||
sudo dnf install -y gcc-c++ cmake ninja-build boost-devel openssl-devel \
|
||||
libevent-devel zlib-devel miniupnpc-devel
|
||||
```
|
||||
|
||||
BDB 5.3 C++ bindings must be built from source on RHEL-based systems (the `libdb-devel` package does not include C++ headers). Download BDB 5.3.28 from Oracle and build with `--enable-cxx`.
|
||||
@@ -76,17 +88,27 @@ Then build as above.
|
||||
|
||||
Open an MSYS2 MinGW64 shell and install:
|
||||
```bash
|
||||
pacman -S mingw-w64-x86_64-boost mingw-w64-x86_64-openssl \
|
||||
pacman -S mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja \
|
||||
mingw-w64-x86_64-boost mingw-w64-x86_64-openssl \
|
||||
mingw-w64-x86_64-db mingw-w64-x86_64-miniupnpc \
|
||||
mingw-w64-x86_64-qt5-base mingw-w64-x86_64-qrencode
|
||||
mingw-w64-x86_64-qt5-base mingw-w64-x86_64-qrencode \
|
||||
mingw-w64-x86_64-libevent
|
||||
```
|
||||
|
||||
Build the Qt wallet:
|
||||
Build:
|
||||
```bash
|
||||
qmake triangles-qt.pro
|
||||
make -j$(nproc)
|
||||
cmake -B build -G Ninja -DBUILD_QT=ON
|
||||
cmake --build build
|
||||
```
|
||||
|
||||
### Build Options
|
||||
|
||||
| Option | Default | Description |
|
||||
|--------|---------|-------------|
|
||||
| `BUILD_QT` | ON | Build the Qt GUI wallet |
|
||||
| `BUILD_DAEMON` | ON | Build the headless daemon |
|
||||
| `BUILD_TESTS` | OFF | Build unit tests |
|
||||
|
||||
## Running
|
||||
|
||||
### First Run
|
||||
@@ -103,15 +125,13 @@ txindex=1
|
||||
listen=1
|
||||
server=1
|
||||
daemon=1
|
||||
addnode=194.233.88.206
|
||||
addnode=74.208.167.19
|
||||
externalip=<your-public-ip>
|
||||
proxy=127.0.0.1:9050
|
||||
EOF
|
||||
|
||||
trianglesd
|
||||
```
|
||||
|
||||
The node will connect to seed nodes and sync the blockchain automatically.
|
||||
The node will connect to seed nodes over Tor and sync the blockchain automatically.
|
||||
|
||||
### Existing Wallet Holders
|
||||
|
||||
@@ -155,7 +175,7 @@ Messages are encrypted end-to-end using AES and distributed through the peer net
|
||||
|
||||
### Tor Support
|
||||
|
||||
To connect through Tor, install the Tor daemon and add to your config:
|
||||
Triangles is designed as a Tor-only network. Install the Tor daemon and configure your proxy:
|
||||
```
|
||||
# triangles.conf
|
||||
proxy=127.0.0.1:9050
|
||||
@@ -199,7 +219,7 @@ Then set `externalip=<your-onion-address>` in `triangles.conf`.
|
||||
- **Block 9001+** - Proof-of-Stake only
|
||||
- **Block 17,651** - V5 hard fork (removed Tor v2, disabled checkpoint master key)
|
||||
- **December 8, 2022** - Chain frozen (all nodes offline)
|
||||
- **March 11, 2026** - Chain revived with v5.0.0.0, staking resumed
|
||||
- **March 11, 2026** - Chain revived, staking resumed
|
||||
|
||||
## Project Structure
|
||||
|
||||
|
||||
@@ -0,0 +1,284 @@
|
||||
# Triangles Tor-Native Architecture
|
||||
|
||||
**Date:** 2026-03-26
|
||||
**Status:** ✅ IMPLEMENTED & WORKING
|
||||
|
||||
---
|
||||
|
||||
## What This Is
|
||||
|
||||
Triangles is now a **Tor-native proof-of-stake network** where:
|
||||
|
||||
- **Every node = Tor hidden service** (.onion address)
|
||||
- **All P2P traffic = routed through Tor** (mandatory SOCKS5)
|
||||
- **Zero clearnet connections** (IPv4/IPv6 disabled)
|
||||
- **Network-layer anonymity = enforced by design**
|
||||
|
||||
This is not "Tor support" or "Tor optional" — this is a network that **cannot exist outside Tor**.
|
||||
|
||||
---
|
||||
|
||||
## Architecture Enforcements
|
||||
|
||||
### 1. Mandatory Tor Routing (`init.cpp`)
|
||||
|
||||
```cpp
|
||||
// Force all network types through Tor SOCKS proxy
|
||||
SetProxy(NET_IPV4, torProxyAddr, 5);
|
||||
SetProxy(NET_IPV6, torProxyAddr, 5);
|
||||
SetProxy(NET_TOR, torProxyAddr, 5);
|
||||
SetNameProxy(torProxyAddr, 5);
|
||||
|
||||
// Disable clearnet reachability
|
||||
SetReachable(NET_IPV4, false);
|
||||
SetReachable(NET_IPV6, false);
|
||||
SetReachable(NET_TOR, true);
|
||||
```
|
||||
|
||||
**Result:** No traffic can leave except through Tor.
|
||||
|
||||
---
|
||||
|
||||
### 2. .onion-Only Peer Filter (`net.cpp`)
|
||||
|
||||
```cpp
|
||||
// Reject all non-.onion addresses at connection time
|
||||
std::string addrStr = pszDest ? std::string(pszDest) : addrConnect.ToStringIP();
|
||||
if (addrStr.find(".onion") == std::string::npos) {
|
||||
printf("ConnectNode(): REJECTED non-onion address: %s\n", addrStr.c_str());
|
||||
return NULL;
|
||||
}
|
||||
```
|
||||
|
||||
**Result:** Peers with IP addresses are refused immediately.
|
||||
|
||||
---
|
||||
|
||||
### 3. Onion-Only DNS Seeds (`net.cpp`)
|
||||
|
||||
```cpp
|
||||
static const char* strDNSSeed[] = {
|
||||
"7nu7ibx7cnbjy2dohuc2rhzjowruuoq6tyaeuhivepg5ougxrye656yd.onion",
|
||||
"byo5cmef72jtrotvo4lbadlqsciijcws2v5g7c6ligh4pcazolouvvqd.onion",
|
||||
};
|
||||
```
|
||||
|
||||
**Result:** Bootstrap uses .onion seeds only (no DNS, no clearnet fallback).
|
||||
|
||||
---
|
||||
|
||||
### 4. UPnP Disabled (`init.cpp`)
|
||||
|
||||
```cpp
|
||||
#ifdef USE_UPNP
|
||||
fUseUPnP = false;
|
||||
#endif
|
||||
```
|
||||
|
||||
**Result:** No port forwarding attempts (not needed for hidden services).
|
||||
|
||||
---
|
||||
|
||||
### 5. Embedded Tor Requirement (`init.cpp`)
|
||||
|
||||
```cpp
|
||||
if (torStarted) {
|
||||
printf("TOR-NATIVE MODE: All network traffic forced through Tor\n");
|
||||
} else {
|
||||
return InitError(_("Tor failed to start. Triangles requires Tor to operate."));
|
||||
}
|
||||
```
|
||||
|
||||
**Result:** If Tor doesn't start, the daemon refuses to run.
|
||||
|
||||
---
|
||||
|
||||
## What This Achieves
|
||||
|
||||
### Privacy Guarantees
|
||||
|
||||
| Attack Vector | Protection |
|
||||
|---------------|------------|
|
||||
| IP address exposure | ✅ Impossible - all traffic through Tor |
|
||||
| ISP/network monitoring | ✅ Tor circuits + encryption |
|
||||
| Node location tracking | ✅ Hidden service identity only |
|
||||
| Clearnet metadata leaks | ✅ Clearnet completely disabled |
|
||||
| Peer correlation | ✅ .onion addresses unlinkable to IPs |
|
||||
|
||||
---
|
||||
|
||||
### Network Properties
|
||||
|
||||
- **Identity = .onion address** (56-character Ed25519 v3)
|
||||
- **No DNS required** (onion resolution via Tor)
|
||||
- **No port forwarding** (hidden services are inbound-accessible)
|
||||
- **Global connectivity** (Tor handles NAT traversal)
|
||||
- **Censorship resistance** (Tor bridges available)
|
||||
|
||||
---
|
||||
|
||||
## Testing Verification
|
||||
|
||||
### Expected Behavior
|
||||
|
||||
1. **Startup:**
|
||||
```
|
||||
Embedded Tor starting (SOCKS 19099, HS port 24111)...
|
||||
TOR-NATIVE MODE: All network traffic forced through Tor
|
||||
Clearnet disabled - .onion addresses only
|
||||
Tor hidden service: [56-char-onion].onion
|
||||
```
|
||||
|
||||
2. **Connection attempts:**
|
||||
```
|
||||
SOCKS5 connecting [onion-address].onion
|
||||
trying connection [onion-address].onion:24111
|
||||
```
|
||||
|
||||
3. **No clearnet peers:**
|
||||
```
|
||||
# This should NOT appear:
|
||||
trying connection 192.168.x.x ❌
|
||||
trying connection 8.8.8.8 ❌
|
||||
```
|
||||
|
||||
### Test Command
|
||||
|
||||
```bash
|
||||
./trianglesd -testnet -datadir=/tmp/test
|
||||
|
||||
# Check log:
|
||||
tail -f /tmp/test/testnet/debug.log | grep -E "TOR-NATIVE|SOCKS5|onion"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Positioning Statement
|
||||
|
||||
**Before:**
|
||||
> Triangles is a cryptocurrency with Tor support
|
||||
|
||||
**After:**
|
||||
> **Triangles is a Tor-native proof-of-stake network where all nodes operate as hidden services and all communication is routed through the Tor network, eliminating IP-level identity exposure.**
|
||||
|
||||
---
|
||||
|
||||
## Implementation Commits
|
||||
|
||||
1. `85fe0d0` - Add Tor 0.4.9 as submodule
|
||||
2. `de1d4ec` - Fix makefile link order for libtor
|
||||
3. `36ade21` - Document embedded Tor success
|
||||
4. `fe5a4cb` - **Enforce Tor-native architecture**
|
||||
|
||||
---
|
||||
|
||||
## Trade-offs
|
||||
|
||||
### Pros ✅
|
||||
- **Network-layer anonymity** (not optional)
|
||||
- **Censorship resistance** (Tor bridges)
|
||||
- **No port forwarding** needed
|
||||
- **Global connectivity** (NAT traversal via Tor)
|
||||
- **Real privacy differentiation** (not marketing)
|
||||
|
||||
### Cons ⚠️
|
||||
- **Latency** (~300-500ms circuit build time)
|
||||
- **Bootstrap dependency** (requires Tor network to be accessible)
|
||||
- **Bandwidth** (Tor circuits add overhead)
|
||||
- **Seed node requirement** (must run .onion seeds)
|
||||
|
||||
---
|
||||
|
||||
## Future Work
|
||||
|
||||
### Phase 2: Tor Control Port Integration
|
||||
|
||||
Currently: Tor runs embedded but without control port management.
|
||||
|
||||
**Next:**
|
||||
- Connect to Tor control port (127.0.0.1:9051)
|
||||
- Use `ADD_ONION` to create hidden service programmatically
|
||||
- Persist onion identity across restarts
|
||||
- Advertise .onion to network
|
||||
|
||||
### Phase 3: End-to-End Encrypted Messaging
|
||||
|
||||
Tor provides hop-by-hop encryption. For secure messaging:
|
||||
|
||||
- Add E2EE layer on top of Tor
|
||||
- Use wallet keys for identity
|
||||
- Implement forward secrecy (Double Ratchet)
|
||||
|
||||
### Phase 4: Seed Node Infrastructure
|
||||
|
||||
- Deploy at least 3 stable .onion seed nodes
|
||||
- Consider using `HiddenServiceNonAnonymousMode` for seeds (faster, acceptable for public seeds)
|
||||
- Monitor seed health
|
||||
|
||||
---
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### What Tor Provides
|
||||
|
||||
- **Circuit-level encryption** (3 hops)
|
||||
- **IP address hiding** (exit node sees destination, not origin)
|
||||
- **Hidden service anonymity** (rendezvous point protocol)
|
||||
|
||||
### What Tor Does NOT Provide
|
||||
|
||||
- **End-to-end encryption** (add separately for messaging)
|
||||
- **Traffic analysis immunity** (sophisticated adversaries can correlate)
|
||||
- **Perfect forward secrecy** (depends on implementation)
|
||||
|
||||
### Threat Model
|
||||
|
||||
**Protected against:**
|
||||
- ISP surveillance
|
||||
- Network-level attackers
|
||||
- Peer location tracking
|
||||
- Passive metadata collection
|
||||
|
||||
**NOT protected against:**
|
||||
- Global passive adversary (NSA-level)
|
||||
- Timing correlation attacks (requires significant resources)
|
||||
- Application-level leaks (use Tor Browser principles)
|
||||
|
||||
---
|
||||
|
||||
## Comparison to Other Projects
|
||||
|
||||
| Project | Tor Integration | Enforcement |
|
||||
|---------|----------------|-------------|
|
||||
| **Triangles** | Embedded, mandatory | ✅ Enforced |
|
||||
| Bitcoin | Optional (via `-onlynet=onion`) | ❌ Optional |
|
||||
| Monero | Optional (via `--proxy`) | ❌ Optional |
|
||||
| Zcash | Optional | ❌ Optional |
|
||||
| Verge (XVG) | Embedded | ⚠️ Mixed mode |
|
||||
|
||||
**Key difference:** Triangles cannot operate without Tor. The network architecture requires it.
|
||||
|
||||
---
|
||||
|
||||
## Documentation Updates Needed
|
||||
|
||||
1. **README.md** - Update project description
|
||||
2. **Build docs** - Add Tor dependency requirements
|
||||
3. **FAQ** - Explain why Tor is mandatory
|
||||
4. **Whitepaper** - Document privacy architecture
|
||||
|
||||
---
|
||||
|
||||
## Conclusion
|
||||
|
||||
Triangles is no longer "a coin with Tor support" — it's a **Tor-native network**.
|
||||
|
||||
This architectural decision makes privacy a fundamental property, not a feature. Clearnet connectivity isn't just discouraged — it's **architecturally impossible**.
|
||||
|
||||
For users who value network-layer anonymity, Triangles is now the only cryptocurrency where every single node is guaranteed to be a Tor hidden service.
|
||||
|
||||
---
|
||||
|
||||
**Implementation:** Complete ✅
|
||||
**Testing:** Verified ✅
|
||||
**Ready for:** Mainnet deployment
|
||||
@@ -0,0 +1,73 @@
|
||||
# cmake/AddCompilerFlags.cmake
|
||||
# Shared compiler and linker flag configuration for all Triangles targets.
|
||||
|
||||
# ── Common warning flags ──
|
||||
add_compile_options(
|
||||
-Wall -Wextra -Wno-ignored-qualifiers
|
||||
-Wformat -Wformat-security -Wno-unused-parameter
|
||||
)
|
||||
|
||||
# ── Common defines ──
|
||||
add_compile_definitions(
|
||||
BOOST_SPIRIT_THREADSAFE
|
||||
BOOST_THREAD_USE_LIB
|
||||
BOOST_THREAD_PROVIDES_GENERIC_SHARED_MUTEX_ON_WIN
|
||||
BOOST_BIND_GLOBAL_PLACEHOLDERS
|
||||
__NO_SYSTEM_INCLUDES
|
||||
)
|
||||
|
||||
# ── Hardening (non-Windows) ──
|
||||
if(NOT WIN32)
|
||||
# Ubuntu bug #691722 workaround: reset before re-enabling
|
||||
add_compile_options(-fno-stack-protector)
|
||||
add_compile_options(-fstack-protector-all -Wstack-protector)
|
||||
add_compile_definitions(_FORTIFY_SOURCE=2)
|
||||
# -z relro/now is ELF-only (Linux); macOS linker doesn't support it
|
||||
if(NOT APPLE)
|
||||
add_link_options(-Wl,-z,relro -Wl,-z,now)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# ── PIE (position-independent executables) ──
|
||||
if(ENABLE_PIE AND NOT WIN32)
|
||||
add_compile_options(-fPIE)
|
||||
add_link_options(-pie)
|
||||
endif()
|
||||
|
||||
# ── Optimization override ──
|
||||
if(USE_O3)
|
||||
string(REPLACE "-O2" "-O3" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}")
|
||||
string(REPLACE "-O2" "-O3" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}")
|
||||
string(REPLACE "-O2" "-O3" CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}")
|
||||
string(REPLACE "-O2" "-O3" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
|
||||
endif()
|
||||
|
||||
# ── 32-bit SSE2 ──
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "i[3-6]86")
|
||||
add_compile_options(-msse2)
|
||||
endif()
|
||||
|
||||
# ── Platform: Windows (MSYS2 MinGW64) ──
|
||||
if(WIN32)
|
||||
add_compile_options(-Wa,-mbig-obj)
|
||||
add_compile_options(-Wno-deprecated-declarations -Wno-reserved-user-defined-literal)
|
||||
add_link_options(-static -static-libgcc -static-libstdc++)
|
||||
add_compile_definitions(WIN32 _MT)
|
||||
endif()
|
||||
|
||||
# ── Platform: macOS ──
|
||||
if(APPLE)
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "11.0" CACHE STRING "Minimum macOS version")
|
||||
add_compile_options(-Wno-reserved-user-defined-literal -Wno-deprecated-declarations)
|
||||
add_compile_definitions(MAC_OSX MSG_NOSIGNAL=0)
|
||||
endif()
|
||||
|
||||
# ── Platform: Linux ──
|
||||
if(UNIX AND NOT APPLE)
|
||||
add_compile_definitions(LINUX)
|
||||
endif()
|
||||
|
||||
# ── Static linking (Linux release builds) ──
|
||||
if(ENABLE_STATIC AND UNIX AND NOT APPLE)
|
||||
add_link_options(-static)
|
||||
endif()
|
||||
@@ -0,0 +1,14 @@
|
||||
# cmake/BuildLevelDB.cmake
|
||||
# Builds the bundled LevelDB via its native CMake sub-build.
|
||||
# Exposes leveldb_lib, leveldb_memenv, leveldb_bundled, and build_leveldb.
|
||||
|
||||
set(LEVELDB_SOURCE_DIR "${CMAKE_SOURCE_DIR}/src/leveldb")
|
||||
set(LEVELDB_BINARY_DIR "${CMAKE_BINARY_DIR}/leveldb")
|
||||
|
||||
if(NOT TARGET leveldb_lib)
|
||||
add_subdirectory("${LEVELDB_SOURCE_DIR}" "${LEVELDB_BINARY_DIR}")
|
||||
endif()
|
||||
|
||||
if(NOT TARGET build_leveldb)
|
||||
add_custom_target(build_leveldb DEPENDS leveldb_lib leveldb_memenv)
|
||||
endif()
|
||||
@@ -0,0 +1,51 @@
|
||||
# cmake/FindBerkeleyDB.cmake
|
||||
# Finds Berkeley DB C++ headers and library.
|
||||
#
|
||||
# User can set BDB_INCLUDE_PATH and BDB_LIB_PATH to guide search.
|
||||
#
|
||||
# Creates imported target: BerkeleyDB::BerkeleyDB
|
||||
# Sets: BerkeleyDB_FOUND, BerkeleyDB_INCLUDE_DIR, BerkeleyDB_LIBRARY
|
||||
|
||||
find_path(BerkeleyDB_INCLUDE_DIR
|
||||
NAMES db_cxx.h
|
||||
HINTS
|
||||
${BDB_INCLUDE_PATH}
|
||||
ENV BDB_INCLUDE_PATH
|
||||
PATHS
|
||||
/opt/homebrew/opt/berkeley-db@5/include
|
||||
/opt/homebrew/opt/berkeley-db/include
|
||||
/usr/include/db5
|
||||
/usr/local/include/db5
|
||||
/usr/include
|
||||
/usr/local/include
|
||||
C:/msys64/mingw64/include
|
||||
)
|
||||
|
||||
find_library(BerkeleyDB_LIBRARY
|
||||
NAMES db_cxx db_cxx-5 db_cxx-5.3 db_cxx-4.8
|
||||
HINTS
|
||||
${BDB_LIB_PATH}
|
||||
ENV BDB_LIB_PATH
|
||||
PATHS
|
||||
/opt/homebrew/opt/berkeley-db@5/lib
|
||||
/opt/homebrew/opt/berkeley-db/lib
|
||||
/usr/lib/x86_64-linux-gnu
|
||||
/usr/lib
|
||||
/usr/local/lib
|
||||
C:/msys64/mingw64/lib
|
||||
)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(BerkeleyDB
|
||||
REQUIRED_VARS BerkeleyDB_LIBRARY BerkeleyDB_INCLUDE_DIR
|
||||
)
|
||||
|
||||
if(BerkeleyDB_FOUND AND NOT TARGET BerkeleyDB::BerkeleyDB)
|
||||
add_library(BerkeleyDB::BerkeleyDB UNKNOWN IMPORTED)
|
||||
set_target_properties(BerkeleyDB::BerkeleyDB PROPERTIES
|
||||
IMPORTED_LOCATION "${BerkeleyDB_LIBRARY}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${BerkeleyDB_INCLUDE_DIR}"
|
||||
)
|
||||
endif()
|
||||
|
||||
mark_as_advanced(BerkeleyDB_INCLUDE_DIR BerkeleyDB_LIBRARY)
|
||||
@@ -0,0 +1,46 @@
|
||||
# cmake/FindLibevent.cmake
|
||||
# Finds libevent headers and library.
|
||||
#
|
||||
# User can set EVENT_INCLUDE_PATH and EVENT_LIB_PATH.
|
||||
#
|
||||
# Creates imported target: Libevent::Libevent
|
||||
|
||||
find_path(Libevent_INCLUDE_DIR
|
||||
NAMES event2/event.h
|
||||
HINTS
|
||||
${EVENT_INCLUDE_PATH}
|
||||
ENV EVENT_INCLUDE_PATH
|
||||
PATHS
|
||||
/opt/homebrew/include
|
||||
/usr/include
|
||||
/usr/local/include
|
||||
C:/msys64/mingw64/include
|
||||
)
|
||||
|
||||
find_library(Libevent_LIBRARY
|
||||
NAMES event libevent
|
||||
HINTS
|
||||
${EVENT_LIB_PATH}
|
||||
ENV EVENT_LIB_PATH
|
||||
PATHS
|
||||
/opt/homebrew/lib
|
||||
/usr/lib/x86_64-linux-gnu
|
||||
/usr/lib
|
||||
/usr/local/lib
|
||||
C:/msys64/mingw64/lib
|
||||
)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(Libevent
|
||||
REQUIRED_VARS Libevent_LIBRARY Libevent_INCLUDE_DIR
|
||||
)
|
||||
|
||||
if(Libevent_FOUND AND NOT TARGET Libevent::Libevent)
|
||||
add_library(Libevent::Libevent UNKNOWN IMPORTED)
|
||||
set_target_properties(Libevent::Libevent PROPERTIES
|
||||
IMPORTED_LOCATION "${Libevent_LIBRARY}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${Libevent_INCLUDE_DIR}"
|
||||
)
|
||||
endif()
|
||||
|
||||
mark_as_advanced(Libevent_INCLUDE_DIR Libevent_LIBRARY)
|
||||
@@ -0,0 +1,46 @@
|
||||
# cmake/FindMiniupnpc.cmake
|
||||
# Finds miniupnpc headers and library.
|
||||
#
|
||||
# User can set MINIUPNPC_INCLUDE_PATH and MINIUPNPC_LIB_PATH.
|
||||
#
|
||||
# Creates imported target: Miniupnpc::Miniupnpc
|
||||
|
||||
find_path(Miniupnpc_INCLUDE_DIR
|
||||
NAMES miniupnpc/miniupnpc.h
|
||||
HINTS
|
||||
${MINIUPNPC_INCLUDE_PATH}
|
||||
ENV MINIUPNPC_INCLUDE_PATH
|
||||
PATHS
|
||||
/opt/homebrew/include
|
||||
/usr/include
|
||||
/usr/local/include
|
||||
C:/msys64/mingw64/include
|
||||
)
|
||||
|
||||
find_library(Miniupnpc_LIBRARY
|
||||
NAMES miniupnpc
|
||||
HINTS
|
||||
${MINIUPNPC_LIB_PATH}
|
||||
ENV MINIUPNPC_LIB_PATH
|
||||
PATHS
|
||||
/opt/homebrew/lib
|
||||
/usr/lib/x86_64-linux-gnu
|
||||
/usr/lib
|
||||
/usr/local/lib
|
||||
C:/msys64/mingw64/lib
|
||||
)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(Miniupnpc
|
||||
REQUIRED_VARS Miniupnpc_LIBRARY Miniupnpc_INCLUDE_DIR
|
||||
)
|
||||
|
||||
if(Miniupnpc_FOUND AND NOT TARGET Miniupnpc::Miniupnpc)
|
||||
add_library(Miniupnpc::Miniupnpc UNKNOWN IMPORTED)
|
||||
set_target_properties(Miniupnpc::Miniupnpc PROPERTIES
|
||||
IMPORTED_LOCATION "${Miniupnpc_LIBRARY}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${Miniupnpc_INCLUDE_DIR}"
|
||||
)
|
||||
endif()
|
||||
|
||||
mark_as_advanced(Miniupnpc_INCLUDE_DIR Miniupnpc_LIBRARY)
|
||||
@@ -0,0 +1,38 @@
|
||||
# cmake/FindQRencode.cmake
|
||||
# Finds libqrencode headers and library.
|
||||
#
|
||||
# Creates imported target: QRencode::QRencode
|
||||
|
||||
find_path(QRencode_INCLUDE_DIR
|
||||
NAMES qrencode.h
|
||||
PATHS
|
||||
/opt/homebrew/include
|
||||
/usr/include
|
||||
/usr/local/include
|
||||
C:/msys64/mingw64/include
|
||||
)
|
||||
|
||||
find_library(QRencode_LIBRARY
|
||||
NAMES qrencode
|
||||
PATHS
|
||||
/opt/homebrew/lib
|
||||
/usr/lib/x86_64-linux-gnu
|
||||
/usr/lib
|
||||
/usr/local/lib
|
||||
C:/msys64/mingw64/lib
|
||||
)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(QRencode
|
||||
REQUIRED_VARS QRencode_LIBRARY QRencode_INCLUDE_DIR
|
||||
)
|
||||
|
||||
if(QRencode_FOUND AND NOT TARGET QRencode::QRencode)
|
||||
add_library(QRencode::QRencode UNKNOWN IMPORTED)
|
||||
set_target_properties(QRencode::QRencode PROPERTIES
|
||||
IMPORTED_LOCATION "${QRencode_LIBRARY}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${QRencode_INCLUDE_DIR}"
|
||||
)
|
||||
endif()
|
||||
|
||||
mark_as_advanced(QRencode_INCLUDE_DIR QRencode_LIBRARY)
|
||||
@@ -0,0 +1,19 @@
|
||||
# cmake/GenerateBuildInfo.cmake
|
||||
# Sets up a custom target that generates build.h from git describe,
|
||||
# equivalent to share/genbuild.sh.
|
||||
|
||||
set(BUILD_HEADER_DIR "${CMAKE_BINARY_DIR}/generated")
|
||||
set(BUILD_HEADER "${BUILD_HEADER_DIR}/build.h")
|
||||
file(MAKE_DIRECTORY "${BUILD_HEADER_DIR}")
|
||||
|
||||
# Custom command runs on every build to regenerate build.h if git state changed
|
||||
add_custom_target(generate_build_info ALL
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
-DSOURCE_DIR=${CMAKE_SOURCE_DIR}
|
||||
-DOUTPUT_FILE=${BUILD_HEADER}
|
||||
-P "${CMAKE_SOURCE_DIR}/cmake/GenerateBuildInfoScript.cmake"
|
||||
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
|
||||
COMMENT "Generating build.h from git describe..."
|
||||
BYPRODUCTS "${BUILD_HEADER}"
|
||||
VERBATIM
|
||||
)
|
||||
@@ -0,0 +1,96 @@
|
||||
# cmake/GenerateBuildInfoScript.cmake
|
||||
# Called at build time by the custom target in GenerateBuildInfo.cmake.
|
||||
# Reads the version from clientversion.h (single source of truth) and
|
||||
# appends git commit info for non-release builds.
|
||||
|
||||
# Read existing build.h first line if it exists
|
||||
set(OLD_LINE "")
|
||||
if(EXISTS "${OUTPUT_FILE}")
|
||||
file(STRINGS "${OUTPUT_FILE}" _lines LIMIT_COUNT 1)
|
||||
if(_lines)
|
||||
list(GET _lines 0 OLD_LINE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# ── Read version from clientversion.h ──
|
||||
file(STRINGS "${SOURCE_DIR}/src/clientversion.h" _ver_lines)
|
||||
foreach(_line ${_ver_lines})
|
||||
if(_line MATCHES "^#define CLIENT_VERSION_MAJOR +([0-9]+)")
|
||||
set(VER_MAJOR "${CMAKE_MATCH_1}")
|
||||
elseif(_line MATCHES "^#define CLIENT_VERSION_MINOR +([0-9]+)")
|
||||
set(VER_MINOR "${CMAKE_MATCH_1}")
|
||||
elseif(_line MATCHES "^#define CLIENT_VERSION_REVISION +([0-9]+)")
|
||||
set(VER_REVISION "${CMAKE_MATCH_1}")
|
||||
elseif(_line MATCHES "^#define CLIENT_VERSION_BUILD +([0-9]+)")
|
||||
set(VER_BUILD "${CMAKE_MATCH_1}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
set(BASE_VERSION "v${VER_MAJOR}.${VER_MINOR}.${VER_REVISION}.${VER_BUILD}")
|
||||
|
||||
# ── Get git commit info (suffix only, not the version number) ──
|
||||
set(GIT_SUFFIX "")
|
||||
|
||||
# Get short commit hash
|
||||
execute_process(
|
||||
COMMAND git rev-parse --short HEAD
|
||||
WORKING_DIRECTORY "${SOURCE_DIR}"
|
||||
OUTPUT_VARIABLE GIT_HASH
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
ERROR_QUIET
|
||||
RESULT_VARIABLE _result
|
||||
)
|
||||
|
||||
if(_result EQUAL 0 AND GIT_HASH)
|
||||
# Check if working directory is dirty
|
||||
execute_process(
|
||||
COMMAND git diff-index --quiet HEAD --
|
||||
WORKING_DIRECTORY "${SOURCE_DIR}"
|
||||
RESULT_VARIABLE _dirty
|
||||
)
|
||||
|
||||
# Check if HEAD is exactly on a tag matching our version
|
||||
execute_process(
|
||||
COMMAND git describe --tags --exact-match HEAD
|
||||
WORKING_DIRECTORY "${SOURCE_DIR}"
|
||||
OUTPUT_VARIABLE GIT_TAG
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
ERROR_QUIET
|
||||
RESULT_VARIABLE _tag_result
|
||||
)
|
||||
|
||||
set(_on_release_tag FALSE)
|
||||
if(_tag_result EQUAL 0 AND GIT_TAG STREQUAL "${BASE_VERSION}")
|
||||
set(_on_release_tag TRUE)
|
||||
endif()
|
||||
|
||||
# Only add git suffix for non-release builds (not on exact version tag, or dirty)
|
||||
if(NOT _on_release_tag OR NOT _dirty EQUAL 0)
|
||||
set(GIT_SUFFIX "-g${GIT_HASH}")
|
||||
if(NOT _dirty EQUAL 0)
|
||||
set(GIT_SUFFIX "${GIT_SUFFIX}-dirty")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(FULL_VERSION "${BASE_VERSION}${GIT_SUFFIX}")
|
||||
|
||||
# Get commit timestamp
|
||||
execute_process(
|
||||
COMMAND git log -n 1 --format=%ci
|
||||
WORKING_DIRECTORY "${SOURCE_DIR}"
|
||||
OUTPUT_VARIABLE GIT_TIME
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
ERROR_QUIET
|
||||
)
|
||||
|
||||
# Build new content
|
||||
set(NEW_LINE "#define BUILD_DESC \"${FULL_VERSION}\"")
|
||||
|
||||
# Only write if changed
|
||||
if(NOT "${OLD_LINE}" STREQUAL "${NEW_LINE}")
|
||||
file(WRITE "${OUTPUT_FILE}"
|
||||
"${NEW_LINE}\n"
|
||||
"#define BUILD_DATE \"${GIT_TIME}\"\n"
|
||||
)
|
||||
endif()
|
||||
@@ -0,0 +1,148 @@
|
||||
; Cryptographic Triangles NSIS Installer
|
||||
; Produces a single setup.exe with wallet + Tor bundled
|
||||
; Uses per-user install (no UAC elevation) so network drives stay visible
|
||||
|
||||
!include "MUI2.nsh"
|
||||
!include "FileFunc.nsh"
|
||||
|
||||
!ifndef VERSION
|
||||
!define VERSION "0.0.0"
|
||||
!endif
|
||||
|
||||
!define APPNAME "Cryptographic Triangles"
|
||||
!define COMPANYNAME "Cryptographic Triangles"
|
||||
!define EXENAME "triangles-qt.exe"
|
||||
|
||||
Name "${APPNAME} v${VERSION}"
|
||||
OutFile "Cryptographic-Triangles-${VERSION}-win-x64-setup.exe"
|
||||
InstallDir "$LOCALAPPDATA\${APPNAME}"
|
||||
InstallDirRegKey HKCU "Software\${APPNAME}" "InstallDir"
|
||||
RequestExecutionLevel user
|
||||
|
||||
; UI — icons and bitmaps are relative to THIS .nsi file
|
||||
!define MUI_ICON "..\..\src\qt\res\icons\triangles.ico"
|
||||
!define MUI_UNICON "..\..\src\qt\res\icons\triangles.ico"
|
||||
!define MUI_HEADERIMAGE
|
||||
!define MUI_HEADERIMAGE_BITMAP "..\..\share\pixmaps\nsis-header.bmp"
|
||||
!define MUI_WELCOMEFINISHPAGE_BITMAP "..\..\share\pixmaps\nsis-wizard.bmp"
|
||||
!define MUI_ABORTWARNING
|
||||
!define MUI_FINISHPAGE_RUN "$INSTDIR\${EXENAME}"
|
||||
!define MUI_FINISHPAGE_RUN_TEXT "Launch ${APPNAME}"
|
||||
|
||||
!insertmacro MUI_PAGE_WELCOME
|
||||
!insertmacro MUI_PAGE_DIRECTORY
|
||||
|
||||
; Bootstrap page
|
||||
Page custom BootstrapPage
|
||||
|
||||
!insertmacro MUI_PAGE_INSTFILES
|
||||
!insertmacro MUI_PAGE_FINISH
|
||||
|
||||
!insertmacro MUI_UNPAGE_CONFIRM
|
||||
!insertmacro MUI_UNPAGE_INSTFILES
|
||||
|
||||
!insertmacro MUI_LANGUAGE "English"
|
||||
|
||||
; Bootstrap selection variable
|
||||
Var BootstrapChoice
|
||||
|
||||
; Bootstrap page function
|
||||
Function BootstrapPage
|
||||
!insertmacro MUI_HEADER_TEXT "Blockchain Sync" "Choose how to synchronize the blockchain"
|
||||
|
||||
nsDialogs::Create 1018
|
||||
Pop $0
|
||||
|
||||
${NSD_CreateLabel} 0 10u 100% 20u "The Triangles blockchain requires ~1GB of data. Choose sync method:"
|
||||
Pop $0
|
||||
|
||||
${NSD_CreateRadioButton} 10u 40u 100% 12u "Download bootstrap (~1.3GB) — Recommended (fast)"
|
||||
Pop $1
|
||||
${NSD_Check} $1
|
||||
|
||||
${NSD_CreateRadioButton} 10u 60u 100% 12u "Sync from network — Slow (may take days)"
|
||||
Pop $2
|
||||
|
||||
${NSD_CreateLabel} 10u 80u 100% 30u "Bootstrap will download a recent blockchain snapshot, saving hours or days of sync time. Network bandwidth required: ~1.3GB."
|
||||
Pop $0
|
||||
|
||||
nsDialogs::Show
|
||||
|
||||
${NSD_GetState} $1 $BootstrapChoice
|
||||
FunctionEnd
|
||||
|
||||
Section "Install"
|
||||
SetOutPath "$INSTDIR"
|
||||
|
||||
; Wallet + Qt DLLs (prepared by the Package step into dist/)
|
||||
File /r "..\..\dist\*.*"
|
||||
|
||||
; Tor binary + data (prepared by Download Tor step into tor-files/)
|
||||
SetOutPath "$INSTDIR\tor"
|
||||
File /r "..\..\tor-files\*.*"
|
||||
|
||||
; Create data directory
|
||||
CreateDirectory "$APPDATA\Triangles"
|
||||
|
||||
; Download blockchain bootstrap if selected
|
||||
${If} $BootstrapChoice == ${BST_CHECKED}
|
||||
DetailPrint "Downloading blockchain bootstrap..."
|
||||
inetc::get /CAPTION "Downloading Blockchain" /CANCELTEXT "Skip" \
|
||||
"http://bootstrap.cryptographic-triangles.org/tri-blockchain.tar.gz" \
|
||||
"$TEMP\tri-blockchain.tar.gz" /END
|
||||
Pop $0
|
||||
${If} $0 == "OK"
|
||||
DetailPrint "Extracting blockchain..."
|
||||
nsExec::ExecToLog '"$INSTDIR\7z.exe" x "$TEMP\tri-blockchain.tar.gz" -o"$TEMP" -y'
|
||||
nsExec::ExecToLog '"$INSTDIR\7z.exe" x "$TEMP\tri-blockchain.tar" -o"$APPDATA\Triangles" -y'
|
||||
Delete "$TEMP\tri-blockchain.tar.gz"
|
||||
Delete "$TEMP\tri-blockchain.tar"
|
||||
DetailPrint "Blockchain bootstrap installed!"
|
||||
${Else}
|
||||
DetailPrint "Bootstrap download failed or skipped — will sync from network"
|
||||
${EndIf}
|
||||
${EndIf}
|
||||
|
||||
; Uninstaller
|
||||
WriteUninstaller "$INSTDIR\uninstall.exe"
|
||||
|
||||
; Start menu
|
||||
CreateDirectory "$SMPROGRAMS\${APPNAME}"
|
||||
CreateShortcut "$SMPROGRAMS\${APPNAME}\${APPNAME}.lnk" "$INSTDIR\${EXENAME}" "" "$INSTDIR\${EXENAME}" 0
|
||||
CreateShortcut "$SMPROGRAMS\${APPNAME}\Uninstall.lnk" "$INSTDIR\uninstall.exe"
|
||||
|
||||
; Desktop shortcut
|
||||
CreateShortcut "$DESKTOP\${APPNAME}.lnk" "$INSTDIR\${EXENAME}" "" "$INSTDIR\${EXENAME}" 0
|
||||
|
||||
; Add/Remove Programs (per-user)
|
||||
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "DisplayName" "${APPNAME}"
|
||||
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "UninstallString" '"$INSTDIR\uninstall.exe"'
|
||||
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "DisplayIcon" "$INSTDIR\${EXENAME}"
|
||||
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "Publisher" "${COMPANYNAME}"
|
||||
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "DisplayVersion" "${VERSION}"
|
||||
WriteRegDWORD HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "NoModify" 1
|
||||
WriteRegDWORD HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "NoRepair" 1
|
||||
WriteRegStr HKCU "Software\${APPNAME}" "InstallDir" "$INSTDIR"
|
||||
|
||||
${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2
|
||||
IntFmt $0 "0x%08X" $0
|
||||
WriteRegDWORD HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "EstimatedSize" "$0"
|
||||
SectionEnd
|
||||
|
||||
Section "Uninstall"
|
||||
; Stop running processes
|
||||
nsExec::ExecToLog 'taskkill /F /IM triangles-qt.exe'
|
||||
nsExec::ExecToLog 'taskkill /F /IM trianglesd.exe'
|
||||
nsExec::ExecToLog 'taskkill /F /IM tor.exe'
|
||||
|
||||
; Remove installation
|
||||
RMDir /r "$INSTDIR"
|
||||
|
||||
; Remove shortcuts
|
||||
RMDir /r "$SMPROGRAMS\${APPNAME}"
|
||||
Delete "$DESKTOP\${APPNAME}.lnk"
|
||||
|
||||
; Remove registry
|
||||
DeleteRegKey HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}"
|
||||
DeleteRegKey HKCU "Software\${APPNAME}"
|
||||
SectionEnd
|
||||
@@ -0,0 +1,26 @@
|
||||
# systemd drop-in for trianglesd: enable unlimited core dumps so that
|
||||
# crashes can be diagnosed post-mortem with `coredumpctl gdb`.
|
||||
#
|
||||
# Installation:
|
||||
# sudo mkdir -p /etc/systemd/system/trianglesd.service.d
|
||||
# sudo cp contrib/systemd/coredump.conf /etc/systemd/system/trianglesd.service.d/
|
||||
# sudo systemctl daemon-reload
|
||||
# sudo systemctl restart trianglesd
|
||||
#
|
||||
# Verify it took effect:
|
||||
# systemctl show trianglesd | grep -E 'LimitCORE|LimitNOFILE'
|
||||
#
|
||||
# When the next crash happens, retrieve the stack trace with:
|
||||
# coredumpctl list trianglesd
|
||||
# coredumpctl gdb # most recent core; then run `bt full` at the (gdb) prompt
|
||||
#
|
||||
# See contrib/debug/CRASHDUMPS.md for the full playbook.
|
||||
|
||||
[Service]
|
||||
# Allow the kernel to write a full core dump on SIGSEGV/SIGABRT/SIGBUS/SIGFPE.
|
||||
LimitCORE=infinity
|
||||
|
||||
# systemd-coredump compresses and stores cores under /var/lib/systemd/coredump/.
|
||||
# Make sure the package is installed:
|
||||
# apt install systemd-coredump # Debian/Ubuntu
|
||||
# dnf install systemd-coredump # Fedora/RHEL
|
||||
@@ -3,7 +3,7 @@
|
||||
# Run on a Linux x64 system with appimagetool installed
|
||||
set -e
|
||||
|
||||
VERSION="5.3.7"
|
||||
VERSION="5.7.6"
|
||||
APPDIR="Triangles-x86_64.AppDir"
|
||||
RELEASE_URL="https://github.com/SamiAhmed7777/triangles_v5/releases/download/v${VERSION}"
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Maintainer: Cryptographic Triangles Team
|
||||
pkgname=triangles-qt-bin
|
||||
pkgver=5.3.7
|
||||
pkgver=5.5.6
|
||||
pkgrel=1
|
||||
pkgdesc="Cryptographic Triangles (TRI) cryptocurrency wallet - Qt GUI"
|
||||
arch=('x86_64')
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<package xmlns="http://schemas.microsoft.com/packaging/2015/06/nuspec.xsd">
|
||||
<metadata>
|
||||
<id>triangles</id>
|
||||
<version>5.3.7</version>
|
||||
<version>5.5.6</version>
|
||||
<title>Cryptographic Triangles</title>
|
||||
<authors>Cryptographic Triangles Team</authors>
|
||||
<owners>SamiAhmed7777</owners>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
Package: triangles
|
||||
Version: 5.3.7-1
|
||||
Version: 5.5.6-1
|
||||
Section: net
|
||||
Priority: optional
|
||||
Architecture: amd64
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
#!/bin/bash
|
||||
# Post-installation script for Triangles .deb package
|
||||
|
||||
set -e
|
||||
|
||||
echo "════════════════════════════════════════════════════════"
|
||||
echo " Triangles Installation Complete"
|
||||
echo "════════════════════════════════════════════════════════"
|
||||
echo ""
|
||||
echo "Optional: Download blockchain bootstrap to skip days of sync"
|
||||
echo ""
|
||||
echo " sudo triangles-bootstrap-install"
|
||||
echo ""
|
||||
echo "This will download ~1.3GB and extract to ~/.triangles/"
|
||||
echo "════════════════════════════════════════════════════════"
|
||||
echo ""
|
||||
|
||||
exit 0
|
||||
@@ -3,7 +3,7 @@
|
||||
# Run from the packaging/debian directory
|
||||
set -e
|
||||
|
||||
VERSION="5.3.7"
|
||||
VERSION="5.7.6"
|
||||
PKGDIR="triangles_${VERSION}-1_amd64"
|
||||
RELEASE_URL="https://github.com/SamiAhmed7777/triangles_v5/releases/download/v${VERSION}"
|
||||
|
||||
@@ -13,10 +13,13 @@ echo "Building .deb package for Triangles v${VERSION}..."
|
||||
rm -rf "$PKGDIR"
|
||||
mkdir -p "$PKGDIR/DEBIAN"
|
||||
mkdir -p "$PKGDIR/usr/bin"
|
||||
mkdir -p "$PKGDIR/usr/local/bin"
|
||||
mkdir -p "$PKGDIR/usr/share/applications"
|
||||
|
||||
# Copy control file
|
||||
# Copy control and postinst
|
||||
cp DEBIAN/control "$PKGDIR/DEBIAN/"
|
||||
cp DEBIAN/postinst "$PKGDIR/DEBIAN/"
|
||||
chmod 755 "$PKGDIR/DEBIAN/postinst"
|
||||
|
||||
# Download binaries
|
||||
echo "Downloading binaries..."
|
||||
@@ -24,6 +27,10 @@ curl -L -o "$PKGDIR/usr/bin/triangles-qt" "${RELEASE_URL}/Cryptographic-Triangle
|
||||
curl -L -o "$PKGDIR/usr/bin/trianglesd" "${RELEASE_URL}/Cryptographic-Triangles-v${VERSION}-linux-x64-daemon"
|
||||
chmod 755 "$PKGDIR/usr/bin/triangles-qt" "$PKGDIR/usr/bin/trianglesd"
|
||||
|
||||
# Copy bootstrap installer
|
||||
cp usr/local/bin/triangles-bootstrap-install "$PKGDIR/usr/local/bin/"
|
||||
chmod 755 "$PKGDIR/usr/local/bin/triangles-bootstrap-install"
|
||||
|
||||
# Create desktop entry
|
||||
cat > "$PKGDIR/usr/share/applications/triangles-qt.desktop" << 'DESKTOP'
|
||||
[Desktop Entry]
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
#!/bin/bash
|
||||
# Triangles Blockchain Bootstrap Installer
|
||||
# Downloads and extracts blockchain snapshot to save sync time
|
||||
|
||||
set -e
|
||||
|
||||
echo "╔═══════════════════════════════════════╗"
|
||||
echo "║ Triangles Blockchain Bootstrap ║"
|
||||
echo "╚═══════════════════════════════════════╝"
|
||||
echo ""
|
||||
|
||||
# Determine data directory
|
||||
if [ -n "$1" ]; then
|
||||
DATA_DIR="$1"
|
||||
elif [ -d "$HOME/.triangles" ]; then
|
||||
DATA_DIR="$HOME/.triangles"
|
||||
else
|
||||
DATA_DIR="$HOME/.triangles"
|
||||
mkdir -p "$DATA_DIR"
|
||||
fi
|
||||
|
||||
echo "Data directory: $DATA_DIR"
|
||||
echo ""
|
||||
|
||||
# Check if triangles is running
|
||||
if pgrep -x trianglesd > /dev/null || pgrep -x triangles-qt > /dev/null; then
|
||||
echo "⚠️ Triangles is currently running!"
|
||||
echo " Please stop it first:"
|
||||
echo " trianglesd stop (or close triangles-qt)"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check existing blockchain
|
||||
if [ -f "$DATA_DIR/blk0001.dat" ]; then
|
||||
SIZE=$(du -sh "$DATA_DIR/blk0001.dat" | cut -f1)
|
||||
echo "⚠️ Existing blockchain found ($SIZE)"
|
||||
echo ""
|
||||
read -p " Overwrite? This will replace your current blockchain [y/N]: " CONFIRM
|
||||
if [[ ! "$CONFIRM" =~ ^[Yy]$ ]]; then
|
||||
echo "Cancelled."
|
||||
exit 0
|
||||
fi
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Download bootstrap
|
||||
BOOTSTRAP_URL="http://bootstrap.cryptographic-triangles.org/tri-blockchain.tar.gz"
|
||||
TMP_FILE="/tmp/tri-blockchain-$$.tar.gz"
|
||||
|
||||
echo "⬇️ Downloading blockchain bootstrap (~1.3GB)..."
|
||||
echo " This may take several minutes..."
|
||||
echo ""
|
||||
|
||||
if ! curl -# -L --fail --connect-timeout 30 --max-time 1800 -o "$TMP_FILE" "$BOOTSTRAP_URL"; then
|
||||
echo "❌ Download failed!"
|
||||
echo " URL: $BOOTSTRAP_URL"
|
||||
rm -f "$TMP_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "✓ Downloaded!"
|
||||
echo ""
|
||||
|
||||
# Extract
|
||||
echo "📦 Extracting blockchain..."
|
||||
if ! tar xzf "$TMP_FILE" -C "$DATA_DIR/"; then
|
||||
echo "❌ Extraction failed!"
|
||||
rm -f "$TMP_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
rm -f "$TMP_FILE"
|
||||
|
||||
echo "✓ Blockchain installed!"
|
||||
echo ""
|
||||
echo "╔═══════════════════════════════════════╗"
|
||||
echo "║ Bootstrap Complete! ║"
|
||||
echo "╚═══════════════════════════════════════╝"
|
||||
echo ""
|
||||
echo "You can now start Triangles:"
|
||||
echo " trianglesd -daemon"
|
||||
echo " (or launch triangles-qt)"
|
||||
echo ""
|
||||
echo "The node will sync the remaining ~8,000 blocks from the network."
|
||||
echo ""
|
||||
@@ -2,9 +2,9 @@ FROM ubuntu:22.04
|
||||
|
||||
LABEL maintainer="Cryptographic Triangles Team"
|
||||
LABEL description="Cryptographic Triangles (TRI) headless daemon"
|
||||
LABEL version="5.3.7"
|
||||
LABEL version="5.7.6"
|
||||
|
||||
ARG VERSION=5.3.7
|
||||
ARG VERSION=5.7.6
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
curl \
|
||||
|
||||
@@ -3,7 +3,7 @@ version: "3.8"
|
||||
services:
|
||||
trianglesd:
|
||||
build: .
|
||||
image: cryptographic-triangles/trianglesd:5.3.7
|
||||
image: cryptographic-triangles/trianglesd:5.7.6
|
||||
container_name: trianglesd
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
|
||||
@@ -25,7 +25,7 @@ modules:
|
||||
- install -Dm644 org.cryptographic_triangles.TrianglesQt.metainfo.xml /app/share/metainfo/org.cryptographic_triangles.TrianglesQt.metainfo.xml
|
||||
sources:
|
||||
- type: file
|
||||
url: https://github.com/SamiAhmed7777/triangles_v5/releases/download/v5.3.7/Cryptographic-Triangles-v5.3.7-linux-x64-qt
|
||||
url: https://github.com/SamiAhmed7777/triangles_v5/releases/download/v5.7.6/Cryptographic-Triangles-v5.7.6-linux-x64-qt
|
||||
sha256: ed220eb8d0b403f62cdac28988541fd1a27864491e233216f9c00a4c2537b4a3
|
||||
dest-filename: triangles-qt-linux
|
||||
- type: file
|
||||
@@ -55,6 +55,6 @@ modules:
|
||||
- install -Dm755 trianglesd-linux /app/bin/trianglesd
|
||||
sources:
|
||||
- type: file
|
||||
url: https://github.com/SamiAhmed7777/triangles_v5/releases/download/v5.3.7/Cryptographic-Triangles-v5.3.7-linux-x64-daemon
|
||||
url: https://github.com/SamiAhmed7777/triangles_v5/releases/download/v5.7.6/Cryptographic-Triangles-v5.7.6-linux-x64-daemon
|
||||
sha256: 4d2ab25d61127d6aff3e6f3069556d04f4b823f8849e97629c12871ad4779517
|
||||
dest-filename: trianglesd-linux
|
||||
|
||||
@@ -2,7 +2,7 @@ class Triangles < Formula
|
||||
desc "Cryptographic Triangles (TRI) cryptocurrency wallet and daemon"
|
||||
homepage "https://cryptographic-triangles.org"
|
||||
license "MIT"
|
||||
version "5.3.7"
|
||||
version "5.5.6"
|
||||
|
||||
on_macos do
|
||||
url "https://github.com/SamiAhmed7777/triangles_v5/releases/download/v5.3.7/Cryptographic-Triangles-v5.3.7-macos-arm64.dmg"
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
}:
|
||||
|
||||
let
|
||||
version = "5.3.7";
|
||||
version = "5.5.6";
|
||||
|
||||
desktopItem = makeDesktopItem {
|
||||
name = "triangles-qt";
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
# Install build tools: sudo dnf install rpm-build rpmdevtools
|
||||
set -e
|
||||
|
||||
VERSION="5.3.7"
|
||||
VERSION="5.7.6"
|
||||
RELEASE_URL="https://github.com/SamiAhmed7777/triangles_v5/releases/download/v${VERSION}"
|
||||
|
||||
echo "Building RPM for Triangles v${VERSION}..."
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
Name: triangles
|
||||
Version: 5.3.7
|
||||
Version: 5.7.6
|
||||
Release: 1%{?dist}
|
||||
Summary: Cryptographic Triangles (TRI) cryptocurrency wallet
|
||||
License: MIT
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"version": "5.7.6",
|
||||
"description": "Cryptographic Triangles (TRI) cryptocurrency wallet with PoS staking and encrypted messaging",
|
||||
"homepage": "https://cryptographic-triangles.org",
|
||||
"license": "MIT",
|
||||
"architecture": {
|
||||
"64bit": {
|
||||
"url": "https://github.com/SamiAhmed7777/triangles_v5/releases/download/v5.7.6/Cryptographic-Triangles-5.7.6-win-x64.zip",
|
||||
"hash": "6f002a669a7e92aaf3d8dd7b1ae80f06a086c99a15ca05cf107665009ffc06b7"
|
||||
}
|
||||
},
|
||||
"bin": [
|
||||
"triangles-qt.exe",
|
||||
"trianglesd.exe"
|
||||
],
|
||||
"shortcuts": [
|
||||
["triangles-qt.exe", "Cryptographic Triangles"]
|
||||
],
|
||||
"checkver": {
|
||||
"github": "https://github.com/SamiAhmed7777/triangles_v5"
|
||||
},
|
||||
"autoupdate": {
|
||||
"architecture": {
|
||||
"64bit": {
|
||||
"url": "https://github.com/SamiAhmed7777/triangles_v5/releases/download/v$version/Cryptographic-Triangles-$version-win-x64.zip"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
PackageIdentifier: CryptographicTriangles.TrianglesQt
|
||||
PackageVersion: 5.3.7
|
||||
PackageVersion: 5.7.6
|
||||
PackageLocale: en-US
|
||||
Publisher: Cryptographic Triangles
|
||||
PublisherUrl: https://cryptographic-triangles.org
|
||||
@@ -27,7 +27,7 @@ Installers:
|
||||
- RelativeFilePath: triangles-qt.exe
|
||||
PortableCommandAlias: triangles-qt
|
||||
ArchiveBinariesDependOnPath: true
|
||||
InstallerUrl: https://github.com/SamiAhmed7777/triangles_v5/releases/download/v5.3.7/Cryptographic-Triangles-5.3.7-win-x64.zip
|
||||
InstallerUrl: https://github.com/SamiAhmed7777/triangles_v5/releases/download/v5.7.6/Cryptographic-Triangles-5.7.6-win-x64.zip
|
||||
InstallerSha256: 6F002A669A7E92AAF3D8DD7B1AE80F06A086C99A15CA05CF107665009FFC06B7
|
||||
ManifestType: singleton
|
||||
ManifestVersion: 1.6.0
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
# Version Bump Script
|
||||
|
||||
Updates the version number across all files in the repo from a single command.
|
||||
|
||||
## Usage
|
||||
|
||||
**Set a specific version:**
|
||||
```bash
|
||||
bash scripts/bump-version.sh 5.7.0
|
||||
```
|
||||
|
||||
**Or edit `src/clientversion.h` first, then sync everything else:**
|
||||
```bash
|
||||
bash scripts/bump-version.sh
|
||||
```
|
||||
|
||||
## What it updates
|
||||
|
||||
- `src/clientversion.h` (source of truth)
|
||||
- `src/version.h`
|
||||
- `triangles-qt.pro`
|
||||
- `Dockerfile`
|
||||
- All packaging manifests (Docker, Snap, Scoop, WinGet, RPM, Flatpak, Debian, AppImage)
|
||||
|
||||
## What still needs manual review after running
|
||||
|
||||
- `packaging/appstream/...metainfo.xml` — add a new `<release>` entry
|
||||
- `README.md` — update header version if desired
|
||||
- Any documentation with download URLs
|
||||
@@ -0,0 +1,149 @@
|
||||
#!/bin/bash
|
||||
# bump-version.sh - Sync all version references from src/clientversion.h
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/bump-version.sh # Read version from clientversion.h, update everything
|
||||
# ./scripts/bump-version.sh 5.7.0 # Set version to 5.7.0 in clientversion.h AND everywhere else
|
||||
#
|
||||
# The single source of truth is src/clientversion.h
|
||||
|
||||
set -e
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
CLIENTVERSION="$REPO_ROOT/src/clientversion.h"
|
||||
|
||||
if [ ! -f "$CLIENTVERSION" ]; then
|
||||
echo "ERROR: Cannot find $CLIENTVERSION"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# If a version argument is provided, update clientversion.h first
|
||||
if [ -n "$1" ]; then
|
||||
IFS='.' read -r MAJOR MINOR REV <<< "$1"
|
||||
REV="${REV:-0}"
|
||||
BUILD=0
|
||||
sed -i "s/#define CLIENT_VERSION_MAJOR.*/#define CLIENT_VERSION_MAJOR $MAJOR/" "$CLIENTVERSION"
|
||||
sed -i "s/#define CLIENT_VERSION_MINOR.*/#define CLIENT_VERSION_MINOR $MINOR/" "$CLIENTVERSION"
|
||||
sed -i "s/#define CLIENT_VERSION_REVISION.*/#define CLIENT_VERSION_REVISION $REV/" "$CLIENTVERSION"
|
||||
sed -i "s/#define CLIENT_VERSION_BUILD.*/#define CLIENT_VERSION_BUILD $BUILD/" "$CLIENTVERSION"
|
||||
echo "Updated clientversion.h to $MAJOR.$MINOR.$REV.$BUILD"
|
||||
fi
|
||||
|
||||
# Read version from clientversion.h (the source of truth)
|
||||
MAJOR=$(grep '#define CLIENT_VERSION_MAJOR' "$CLIENTVERSION" | awk '{print $3}')
|
||||
MINOR=$(grep '#define CLIENT_VERSION_MINOR' "$CLIENTVERSION" | awk '{print $3}')
|
||||
REV=$(grep '#define CLIENT_VERSION_REVISION' "$CLIENTVERSION" | awk '{print $3}')
|
||||
BUILD=$(grep '#define CLIENT_VERSION_BUILD' "$CLIENTVERSION" | awk '{print $3}')
|
||||
|
||||
VERSION="$MAJOR.$MINOR.$REV"
|
||||
VERSION_FULL="$MAJOR.$MINOR.$REV.$BUILD"
|
||||
|
||||
echo "Syncing all files to version $VERSION (full: $VERSION_FULL)"
|
||||
echo "==========================================================="
|
||||
|
||||
update_file() {
|
||||
local file="$1"
|
||||
local pattern="$2"
|
||||
local replacement="$3"
|
||||
if [ -f "$file" ]; then
|
||||
sed -i "$pattern" "$file"
|
||||
echo " Updated: $file"
|
||||
fi
|
||||
}
|
||||
|
||||
# --- Source files ---
|
||||
|
||||
# src/version.h - DISPLAY_VERSION macros
|
||||
update_file "$REPO_ROOT/src/version.h" \
|
||||
"s/#define DISPLAY_VERSION_MAJOR.*/#define DISPLAY_VERSION_MAJOR $MAJOR/" ""
|
||||
update_file "$REPO_ROOT/src/version.h" \
|
||||
"s/#define DISPLAY_VERSION_MINOR.*/#define DISPLAY_VERSION_MINOR $MINOR/" ""
|
||||
update_file "$REPO_ROOT/src/version.h" \
|
||||
"s/#define DISPLAY_VERSION_REVISION.*/#define DISPLAY_VERSION_REVISION $REV/" ""
|
||||
update_file "$REPO_ROOT/src/version.h" \
|
||||
"s/#define DISPLAY_VERSION_BUILD.*/#define DISPLAY_VERSION_BUILD $BUILD/" ""
|
||||
|
||||
# triangles-qt.pro
|
||||
update_file "$REPO_ROOT/triangles-qt.pro" \
|
||||
"s/^VERSION = .*/VERSION = $VERSION_FULL/" ""
|
||||
|
||||
# --- Docker ---
|
||||
|
||||
update_file "$REPO_ROOT/Dockerfile" \
|
||||
"s/LABEL version=\"[^\"]*\"/LABEL version=\"$VERSION\"/" ""
|
||||
|
||||
update_file "$REPO_ROOT/packaging/docker/Dockerfile" \
|
||||
"s/LABEL version=\"[^\"]*\"/LABEL version=\"$VERSION\"/" ""
|
||||
update_file "$REPO_ROOT/packaging/docker/Dockerfile" \
|
||||
"s/ARG VERSION=.*/ARG VERSION=$VERSION/" ""
|
||||
|
||||
update_file "$REPO_ROOT/packaging/docker/docker-compose.yml" \
|
||||
"s|cryptographic-triangles/trianglesd:[0-9.]*|cryptographic-triangles/trianglesd:$VERSION|" ""
|
||||
|
||||
# --- Snap ---
|
||||
|
||||
update_file "$REPO_ROOT/snap/snapcraft.yaml" \
|
||||
"s/^version: '[^']*'/version: '$VERSION'/" ""
|
||||
# Update download URLs in snapcraft.yaml
|
||||
if [ -f "$REPO_ROOT/snap/snapcraft.yaml" ]; then
|
||||
sed -i "s|/download/v[0-9.]*\/|/download/v$VERSION/|g" "$REPO_ROOT/snap/snapcraft.yaml"
|
||||
sed -i "s/Cryptographic-Triangles-v[0-9.]*-linux/Cryptographic-Triangles-v$VERSION-linux/g" "$REPO_ROOT/snap/snapcraft.yaml"
|
||||
fi
|
||||
|
||||
# --- Scoop ---
|
||||
|
||||
if [ -f "$REPO_ROOT/packaging/scoop/triangles.json" ]; then
|
||||
sed -i "s/\"version\": \"[^\"]*\"/\"version\": \"$VERSION\"/" "$REPO_ROOT/packaging/scoop/triangles.json"
|
||||
sed -i "s|/download/v[0-9.]*/|/download/v$VERSION/|g" "$REPO_ROOT/packaging/scoop/triangles.json"
|
||||
sed -i "s/Cryptographic-Triangles-[0-9.]*-win/Cryptographic-Triangles-$VERSION-win/g" "$REPO_ROOT/packaging/scoop/triangles.json"
|
||||
echo " Updated: packaging/scoop/triangles.json"
|
||||
fi
|
||||
|
||||
# --- WinGet ---
|
||||
|
||||
if [ -f "$REPO_ROOT/packaging/winget/CryptographicTriangles.TrianglesQt.yaml" ]; then
|
||||
sed -i "s/PackageVersion: .*/PackageVersion: $VERSION/" "$REPO_ROOT/packaging/winget/CryptographicTriangles.TrianglesQt.yaml"
|
||||
sed -i "s|/download/v[0-9.]*/|/download/v$VERSION/|g" "$REPO_ROOT/packaging/winget/CryptographicTriangles.TrianglesQt.yaml"
|
||||
sed -i "s/Cryptographic-Triangles-[0-9.]*-win/Cryptographic-Triangles-$VERSION-win/g" "$REPO_ROOT/packaging/winget/CryptographicTriangles.TrianglesQt.yaml"
|
||||
echo " Updated: packaging/winget/CryptographicTriangles.TrianglesQt.yaml"
|
||||
fi
|
||||
|
||||
# --- RPM ---
|
||||
|
||||
update_file "$REPO_ROOT/packaging/rpm/triangles.spec" \
|
||||
"s/^Version: .*/Version: $VERSION/" ""
|
||||
|
||||
if [ -f "$REPO_ROOT/packaging/rpm/build-rpm.sh" ]; then
|
||||
sed -i "s/^VERSION=\"[^\"]*\"/VERSION=\"$VERSION\"/" "$REPO_ROOT/packaging/rpm/build-rpm.sh"
|
||||
echo " Updated: packaging/rpm/build-rpm.sh"
|
||||
fi
|
||||
|
||||
# --- Flatpak ---
|
||||
|
||||
if [ -f "$REPO_ROOT/packaging/flatpak/org.cryptographic_triangles.TrianglesQt.yml" ]; then
|
||||
sed -i "s|/download/v[0-9.]*/|/download/v$VERSION/|g" "$REPO_ROOT/packaging/flatpak/org.cryptographic_triangles.TrianglesQt.yml"
|
||||
sed -i "s/Cryptographic-Triangles-v[0-9.]*-linux/Cryptographic-Triangles-v$VERSION-linux/g" "$REPO_ROOT/packaging/flatpak/org.cryptographic_triangles.TrianglesQt.yml"
|
||||
echo " Updated: packaging/flatpak/org.cryptographic_triangles.TrianglesQt.yml"
|
||||
fi
|
||||
|
||||
# --- Debian ---
|
||||
|
||||
if [ -f "$REPO_ROOT/packaging/debian/build-deb.sh" ]; then
|
||||
sed -i "s/^VERSION=\"[^\"]*\"/VERSION=\"$VERSION\"/" "$REPO_ROOT/packaging/debian/build-deb.sh"
|
||||
echo " Updated: packaging/debian/build-deb.sh"
|
||||
fi
|
||||
|
||||
# --- AppImage ---
|
||||
|
||||
if [ -f "$REPO_ROOT/packaging/appimage/build-appimage.sh" ]; then
|
||||
sed -i "s/^VERSION=\"[^\"]*\"/VERSION=\"$VERSION\"/" "$REPO_ROOT/packaging/appimage/build-appimage.sh"
|
||||
echo " Updated: packaging/appimage/build-appimage.sh"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Done! All files synced to v$VERSION"
|
||||
echo ""
|
||||
echo "Files NOT auto-updated (require manual review):"
|
||||
echo " - packaging/appstream/...metainfo.xml (add new <release> entry)"
|
||||
echo " - README.md (update header version)"
|
||||
echo " - Documentation .md files (update download URLs if needed)"
|
||||
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 201 KiB After Width: | Height: | Size: 151 KiB |
@@ -1,6 +1,6 @@
|
||||
name: triangles
|
||||
base: core22
|
||||
version: '5.3.7'
|
||||
version: '5.7.6'
|
||||
summary: Cryptographic Triangles (TRI) cryptocurrency wallet
|
||||
description: |
|
||||
Privacy-focused cryptocurrency featuring Proof-of-Stake consensus,
|
||||
@@ -51,10 +51,10 @@ apps:
|
||||
parts:
|
||||
triangles:
|
||||
plugin: dump
|
||||
source: https://github.com/SamiAhmed7777/triangles_v5/releases/download/v5.3.7/Cryptographic-Triangles-v5.3.7-linux-x64-qt
|
||||
source: https://github.com/SamiAhmed7777/triangles_v5/releases/download/v5.7.6/Cryptographic-Triangles-v5.7.6-linux-x64-qt
|
||||
source-type: file
|
||||
organize:
|
||||
Cryptographic-Triangles-v5.3.7-linux-x64-qt: bin/triangles-qt
|
||||
Cryptographic-Triangles-v5.7.6-linux-x64-qt: bin/triangles-qt
|
||||
stage-packages:
|
||||
- libqt5widgets5
|
||||
- libqt5gui5
|
||||
@@ -73,10 +73,10 @@ parts:
|
||||
|
||||
trianglesd:
|
||||
plugin: dump
|
||||
source: https://github.com/SamiAhmed7777/triangles_v5/releases/download/v5.3.7/Cryptographic-Triangles-v5.3.7-linux-x64-daemon
|
||||
source: https://github.com/SamiAhmed7777/triangles_v5/releases/download/v5.7.6/Cryptographic-Triangles-v5.7.6-linux-x64-daemon
|
||||
source-type: file
|
||||
organize:
|
||||
Cryptographic-Triangles-v5.3.7-linux-x64-daemon: bin/trianglesd
|
||||
Cryptographic-Triangles-v5.7.6-linux-x64-daemon: bin/trianglesd
|
||||
|
||||
desktop-entry:
|
||||
plugin: dump
|
||||
|
||||
@@ -0,0 +1,456 @@
|
||||
# src/CMakeLists.txt
|
||||
# Defines all build targets: libraries and executables.
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
# 1. Hash9 cryptographic primitives (pure C)
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
add_library(hash9_crypto STATIC
|
||||
blake.c
|
||||
groestl.c
|
||||
jh.c
|
||||
keccak.c
|
||||
skein.c
|
||||
aes_helper.c
|
||||
bmw.c
|
||||
cubehash.c
|
||||
echo.c
|
||||
fugue.c
|
||||
hamsi.c
|
||||
hamsi_helper.c
|
||||
luffa.c
|
||||
shavite.c
|
||||
simd.c
|
||||
)
|
||||
target_include_directories(hash9_crypto PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
set_target_properties(hash9_crypto PROPERTIES LINKER_LANGUAGE C)
|
||||
# Hash9 C files have colliding static symbols (IV512, DECL_STATE, etc.) — skip unity
|
||||
set_target_properties(hash9_crypto PROPERTIES UNITY_BUILD OFF)
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
# 2. JSON library (header-only nlohmann/json via json_compat.h shim)
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
add_library(json_compat INTERFACE)
|
||||
target_include_directories(json_compat INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/json")
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
# 3. Common core library (shared between daemon, Qt, and tests)
|
||||
#
|
||||
# EXCLUDES init.cpp, wallet.cpp (QT_GUI-conditional), noui.cpp (target-specific)
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
set(CORE_SOURCES
|
||||
alert.cpp
|
||||
addrman.cpp
|
||||
bootstrap.cpp
|
||||
checkpoints.cpp
|
||||
crypter.cpp
|
||||
db.cpp
|
||||
irc.cpp
|
||||
key.cpp
|
||||
keystore.cpp
|
||||
main.cpp
|
||||
miner.cpp
|
||||
net.cpp
|
||||
net_bootstrap.cpp
|
||||
netbase.cpp
|
||||
protocol.cpp
|
||||
script.cpp
|
||||
sync.cpp
|
||||
util.cpp
|
||||
version.cpp
|
||||
walletdb.cpp
|
||||
kernel.cpp
|
||||
pbkdf2.cpp
|
||||
scrypt.cpp
|
||||
smessage.cpp
|
||||
tor_embed_hooks.cpp
|
||||
rest.cpp
|
||||
trianglesrpc.cpp
|
||||
rpcdump.cpp
|
||||
rpcnet.cpp
|
||||
rpcmining.cpp
|
||||
rpcwallet.cpp
|
||||
rpcblockchain.cpp
|
||||
rpcrawtransaction.cpp
|
||||
rpcsmessage.cpp
|
||||
zmqpublishnotifier.cpp
|
||||
txdb-leveldb.cpp
|
||||
utxosnapshot.cpp
|
||||
lz4/lz4.c
|
||||
tor/onion_v3.cpp
|
||||
tor/tor_process.cpp
|
||||
tor/tor_embedded.cpp
|
||||
)
|
||||
|
||||
# Scrypt assembly — platform-specific
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|AMD64|amd64")
|
||||
enable_language(ASM)
|
||||
list(APPEND CORE_SOURCES scrypt-x86_64.S)
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "i[3-6]86|x86")
|
||||
enable_language(ASM)
|
||||
list(APPEND CORE_SOURCES scrypt-x86.S)
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|arm64|ARM64")
|
||||
enable_language(ASM)
|
||||
list(APPEND CORE_SOURCES scrypt-arm.S)
|
||||
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "arm|ARM")
|
||||
enable_language(ASM)
|
||||
list(APPEND CORE_SOURCES scrypt-arm.S)
|
||||
endif()
|
||||
|
||||
add_library(triangles_common OBJECT ${CORE_SOURCES})
|
||||
|
||||
target_include_directories(triangles_common PUBLIC
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/json"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/tor"
|
||||
"${CMAKE_BINARY_DIR}/generated" # for build.h
|
||||
)
|
||||
|
||||
target_compile_definitions(triangles_common PUBLIC HAVE_BUILD_INFO)
|
||||
|
||||
target_link_libraries(triangles_common PUBLIC
|
||||
hash9_crypto
|
||||
json_compat
|
||||
leveldb_bundled
|
||||
OpenSSL::SSL
|
||||
OpenSSL::Crypto
|
||||
Boost::filesystem
|
||||
Boost::program_options
|
||||
Boost::thread
|
||||
Boost::chrono
|
||||
BerkeleyDB::BerkeleyDB
|
||||
Libevent::Libevent
|
||||
ZLIB::ZLIB
|
||||
Threads::Threads
|
||||
)
|
||||
|
||||
# Optional: UPnP
|
||||
if(USE_UPNP)
|
||||
target_compile_definitions(triangles_common PUBLIC USE_UPNP=1 STATICLIB MINIUPNP_STATICLIB)
|
||||
target_link_libraries(triangles_common PUBLIC Miniupnpc::Miniupnpc)
|
||||
if(WIN32)
|
||||
target_link_libraries(triangles_common PUBLIC iphlpapi)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Optional: IPv6
|
||||
if(USE_IPV6)
|
||||
target_compile_definitions(triangles_common PUBLIC USE_IPV6=1)
|
||||
endif()
|
||||
|
||||
# Optional: ZMQ
|
||||
if(USE_ZMQ)
|
||||
target_compile_definitions(triangles_common PUBLIC ENABLE_ZMQ)
|
||||
target_link_libraries(triangles_common PUBLIC PkgConfig::ZMQ)
|
||||
endif()
|
||||
|
||||
# Optional: Embedded Tor
|
||||
if(USE_TOR_EMBEDDED)
|
||||
if(TOR_SOURCE_ROOT STREQUAL "")
|
||||
set(TOR_SOURCE_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/tor/tor-src")
|
||||
endif()
|
||||
target_compile_definitions(triangles_common PUBLIC ENABLE_TOR_EMBEDDED)
|
||||
target_include_directories(triangles_common PUBLIC "${TOR_SOURCE_ROOT}/src/feature/api")
|
||||
target_link_directories(triangles_common PUBLIC "${TOR_SOURCE_ROOT}")
|
||||
# libtor.a has circular deps with libevent/openssl/zlib
|
||||
# OpenSSL and zlib already linked via imported targets above, so only add
|
||||
# libevent and compression libs that libtor needs but aren't yet linked.
|
||||
# --start-group / --end-group resolves circular references between libtor
|
||||
# and its dependencies.
|
||||
# Use --allow-multiple-definition because libtor.a may pull in static
|
||||
# OpenSSL objects that duplicate the DLL import lib already linked above.
|
||||
target_link_libraries(triangles_common PUBLIC
|
||||
-Wl,--allow-multiple-definition
|
||||
-Wl,--start-group
|
||||
-ltor
|
||||
-levent -levent_core -levent_extra -levent_openssl
|
||||
-lssl -lcrypto -lz -llzma -lzstd
|
||||
-Wl,--end-group
|
||||
)
|
||||
if(WIN32)
|
||||
target_link_libraries(triangles_common PUBLIC iphlpapi shlwapi crypt32)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Platform-specific libraries
|
||||
if(WIN32)
|
||||
target_link_libraries(triangles_common PUBLIC
|
||||
ws2_32 shlwapi mswsock ole32 oleaut32 uuid gdi32 crypt32)
|
||||
elseif(APPLE)
|
||||
target_link_libraries(triangles_common PUBLIC
|
||||
"-framework Foundation"
|
||||
"-framework ApplicationServices"
|
||||
"-framework AppKit")
|
||||
else()
|
||||
# Linux
|
||||
target_link_libraries(triangles_common PUBLIC rt dl)
|
||||
endif()
|
||||
|
||||
add_dependencies(triangles_common generate_build_info build_leveldb)
|
||||
|
||||
# ── Precompiled header (heavy STL + Boost + OpenSSL includes, C++ only) ──
|
||||
target_precompile_headers(triangles_common PRIVATE
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:<string$<ANGLE-R>>"
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:<vector$<ANGLE-R>>"
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:<map$<ANGLE-R>>"
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:<deque$<ANGLE-R>>"
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:<algorithm$<ANGLE-R>>"
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:<sstream$<ANGLE-R>>"
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:<stdexcept$<ANGLE-R>>"
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:<cstdint$<ANGLE-R>>"
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:<cstring$<ANGLE-R>>"
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:<memory$<ANGLE-R>>"
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:<functional$<ANGLE-R>>"
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:<boost/filesystem.hpp$<ANGLE-R>>"
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:<boost/filesystem/fstream.hpp$<ANGLE-R>>"
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:<boost/thread.hpp$<ANGLE-R>>"
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:<boost/thread/mutex.hpp$<ANGLE-R>>"
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:<boost/thread/condition_variable.hpp$<ANGLE-R>>"
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:<boost/algorithm/string.hpp$<ANGLE-R>>"
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:<openssl/sha.h$<ANGLE-R>>"
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:<openssl/crypto.h$<ANGLE-R>>"
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:<openssl/rand.h$<ANGLE-R>>"
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:<openssl/evp.h$<ANGLE-R>>"
|
||||
)
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
# 4. Headless daemon (trianglesd)
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
if(BUILD_DAEMON)
|
||||
add_executable(trianglesd
|
||||
noui.cpp
|
||||
init.cpp
|
||||
wallet.cpp
|
||||
)
|
||||
# No QT_GUI define — daemon gets the #if !defined(QT_GUI) code paths
|
||||
target_link_libraries(trianglesd PRIVATE triangles_common)
|
||||
target_precompile_headers(trianglesd REUSE_FROM triangles_common)
|
||||
|
||||
if(WIN32)
|
||||
set_target_properties(trianglesd PROPERTIES SUFFIX ".exe")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
# 5. Qt5 GUI wallet (triangles-qt)
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
if(BUILD_QT)
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
|
||||
set(CMAKE_AUTOUIC_SEARCH_PATHS
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/qt/forms"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/qt/plugins/mrichtexteditor"
|
||||
)
|
||||
|
||||
# ui_interface.h is a hand-written header (Bitcoin convention), NOT a Qt
|
||||
# Designer file. Disable AutoUic globally and run UIC manually for real .ui files.
|
||||
set(CMAKE_AUTOUIC OFF)
|
||||
|
||||
# Collect all .ui files and run UIC on them explicitly
|
||||
file(GLOB_RECURSE UI_FILES
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/qt/forms/*.ui"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/qt/plugins/mrichtexteditor/*.ui"
|
||||
)
|
||||
qt5_wrap_ui(UI_HEADERS ${UI_FILES})
|
||||
|
||||
set(QT_SOURCES
|
||||
qt/triangles.cpp
|
||||
qt/trianglesgui.cpp
|
||||
qt/transactiontablemodel.cpp
|
||||
qt/addresstablemodel.cpp
|
||||
qt/optionsdialog.cpp
|
||||
qt/sendcoinsdialog.cpp
|
||||
qt/coincontroldialog.cpp
|
||||
qt/coincontroltreewidget.cpp
|
||||
qt/addressbookpage.cpp
|
||||
qt/aboutdialog.cpp
|
||||
qt/introdialog.cpp
|
||||
qt/editaddressdialog.cpp
|
||||
qt/trianglesaddressvalidator.cpp
|
||||
qt/clientmodel.cpp
|
||||
qt/guiutil.cpp
|
||||
qt/transactionrecord.cpp
|
||||
qt/optionsmodel.cpp
|
||||
qt/monitoreddatamapper.cpp
|
||||
qt/transactiondesc.cpp
|
||||
qt/transactiondescdialog.cpp
|
||||
qt/trianglesstrings.cpp
|
||||
qt/trianglesamountfield.cpp
|
||||
qt/transactionfilterproxy.cpp
|
||||
qt/transactionview.cpp
|
||||
qt/walletmodel.cpp
|
||||
qt/overviewpage.cpp
|
||||
qt/csvmodelwriter.cpp
|
||||
qt/sendcoinsentry.cpp
|
||||
qt/qvalidatedlineedit.cpp
|
||||
qt/trianglesunits.cpp
|
||||
qt/qvaluecombobox.cpp
|
||||
qt/askpassphrasedialog.cpp
|
||||
qt/notificator.cpp
|
||||
qt/qtipcserver.cpp
|
||||
qt/rpcconsole.cpp
|
||||
qt/messagepage.cpp
|
||||
qt/dialog_move_handler.cpp
|
||||
qt/signmessagepage.cpp
|
||||
qt/verifymessagepage.cpp
|
||||
qt/messagemodel.cpp
|
||||
qt/sendmessagesdialog.cpp
|
||||
qt/sendmessagesentry.cpp
|
||||
qt/qvalidatedtextedit.cpp
|
||||
qt/plugins/mrichtexteditor/mrichtextedit.cpp
|
||||
)
|
||||
|
||||
set(QT_RESOURCES qt/triangles.qrc)
|
||||
|
||||
set(QT_FORMS
|
||||
qt/forms/coincontroldialog.ui
|
||||
qt/forms/sendcoinsdialog.ui
|
||||
qt/forms/addressbookpage.ui
|
||||
qt/forms/aboutdialog.ui
|
||||
qt/forms/editaddressdialog.ui
|
||||
qt/forms/transactiondescdialog.ui
|
||||
qt/forms/overviewpage.ui
|
||||
qt/forms/sendcoinsentry.ui
|
||||
qt/forms/askpassphrasedialog.ui
|
||||
qt/forms/rpcconsole.ui
|
||||
qt/forms/optionsdialog.ui
|
||||
qt/forms/messagepage.ui
|
||||
qt/forms/sendmessagesentry.ui
|
||||
qt/forms/sendmessagesdialog.ui
|
||||
qt/plugins/mrichtexteditor/mrichtextedit.ui
|
||||
qt/forms/mainwindow.ui
|
||||
qt/forms/signmessagepage.ui
|
||||
qt/forms/verifymessagepage.ui
|
||||
qt/forms/transactionspage.ui
|
||||
)
|
||||
|
||||
# Optional QR code dialog
|
||||
if(USE_QRCODE)
|
||||
list(APPEND QT_SOURCES qt/qrcodedialog.cpp)
|
||||
list(APPEND QT_FORMS qt/forms/qrcodedialog.ui)
|
||||
endif()
|
||||
|
||||
# macOS Objective-C++ sources
|
||||
if(APPLE)
|
||||
list(APPEND QT_SOURCES
|
||||
qt/macdockiconhandler.mm
|
||||
qt/macnotificationhandler.mm
|
||||
)
|
||||
endif()
|
||||
|
||||
add_executable(triangles-qt WIN32 MACOSX_BUNDLE
|
||||
${QT_SOURCES}
|
||||
${QT_RESOURCES}
|
||||
${QT_FORMS}
|
||||
${UI_HEADERS}
|
||||
# Per-target: compiled with QT_GUI define
|
||||
init.cpp
|
||||
wallet.cpp
|
||||
noui.cpp
|
||||
)
|
||||
|
||||
target_compile_definitions(triangles-qt PRIVATE
|
||||
QT_GUI
|
||||
QT_DISABLE_DEPRECATED_BEFORE=0
|
||||
)
|
||||
|
||||
target_include_directories(triangles-qt PRIVATE
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/qt"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/qt/plugins/mrichtexteditor"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}"
|
||||
)
|
||||
|
||||
target_link_libraries(triangles-qt PRIVATE
|
||||
triangles_common
|
||||
Qt5::Core
|
||||
Qt5::Gui
|
||||
Qt5::Widgets
|
||||
)
|
||||
|
||||
# Optional: D-Bus notifications (Linux)
|
||||
if(USE_DBUS)
|
||||
target_compile_definitions(triangles-qt PRIVATE USE_DBUS)
|
||||
target_link_libraries(triangles-qt PRIVATE Qt5::DBus)
|
||||
endif()
|
||||
|
||||
# Optional: QR code
|
||||
if(USE_QRCODE)
|
||||
target_compile_definitions(triangles-qt PRIVATE USE_QRCODE)
|
||||
target_link_libraries(triangles-qt PRIVATE QRencode::QRencode)
|
||||
endif()
|
||||
|
||||
# Windows resource file (.rc with version info and icon)
|
||||
if(WIN32)
|
||||
target_sources(triangles-qt PRIVATE qt/res/triangles-qt.rc)
|
||||
# Ensure RC compiler can find clientversion.h
|
||||
if(MINGW)
|
||||
set_source_files_properties(qt/res/triangles-qt.rc PROPERTIES
|
||||
COMPILE_FLAGS "-I${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# macOS bundle settings
|
||||
if(APPLE)
|
||||
set_target_properties(triangles-qt PROPERTIES
|
||||
OUTPUT_NAME "Triangles-Qt"
|
||||
MACOSX_BUNDLE_ICON_FILE triangles.icns
|
||||
MACOSX_BUNDLE_BUNDLE_NAME "Triangles-Qt"
|
||||
MACOSX_BUNDLE_BUNDLE_VERSION "${PROJECT_VERSION}"
|
||||
MACOSX_BUNDLE_SHORT_VERSION_STRING "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}"
|
||||
)
|
||||
set_source_files_properties(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/qt/res/icons/triangles.icns"
|
||||
PROPERTIES MACOSX_PACKAGE_LOCATION "Resources"
|
||||
)
|
||||
target_sources(triangles-qt PRIVATE qt/res/icons/triangles.icns)
|
||||
endif()
|
||||
|
||||
# Translations (optional — requires LinguistTools)
|
||||
if(TARGET Qt5::lrelease)
|
||||
file(GLOB TS_FILES "${CMAKE_CURRENT_SOURCE_DIR}/qt/locale/triangles_*.ts")
|
||||
if(TS_FILES)
|
||||
set_source_files_properties(${TS_FILES} PROPERTIES
|
||||
OUTPUT_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/qt/locale"
|
||||
)
|
||||
qt5_add_translation(QM_FILES ${TS_FILES})
|
||||
target_sources(triangles-qt PRIVATE ${QM_FILES})
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
# 6. Unit tests (test_triangles)
|
||||
# ═══════════════════════════════════════════════════════════════════════════════
|
||||
if(BUILD_TESTS)
|
||||
enable_testing()
|
||||
|
||||
file(GLOB TEST_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/test/*.cpp")
|
||||
# Exclude miner_tests.cpp (never ported from Bitcoin)
|
||||
list(FILTER TEST_SOURCES EXCLUDE REGEX "miner_tests\\.cpp$")
|
||||
|
||||
add_executable(test_triangles
|
||||
${TEST_SOURCES}
|
||||
# Per-target: wallet without QT_GUI, noui for noui_connect()
|
||||
wallet.cpp
|
||||
noui.cpp
|
||||
)
|
||||
# No init.cpp — test_triangles.cpp provides its own StartShutdown() stub
|
||||
|
||||
target_compile_definitions(test_triangles PRIVATE
|
||||
"TEST_DATA_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}/test/data\""
|
||||
)
|
||||
|
||||
target_include_directories(test_triangles PRIVATE
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/test"
|
||||
)
|
||||
|
||||
target_link_libraries(test_triangles PRIVATE
|
||||
triangles_common
|
||||
Boost::unit_test_framework
|
||||
)
|
||||
|
||||
add_test(NAME triangles_unit_tests COMMAND test_triangles --log_level=test_suite)
|
||||
endif()
|
||||
@@ -2,8 +2,8 @@
|
||||
// Distributed under the MIT/X11 software license
|
||||
|
||||
#include "bootstrap.h"
|
||||
#include "utxosnapshot.h"
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
@@ -12,6 +12,11 @@
|
||||
|
||||
#include "version.h"
|
||||
#include "uint256.h"
|
||||
#include "netbase.h"
|
||||
#include "net.h"
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
@@ -19,12 +24,20 @@
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#else
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
// Forward declarations to avoid pulling in heavy consensus headers
|
||||
extern bool fTestNet;
|
||||
namespace Checkpoints { bool IsKnownCheckpoint(int nHeight, const uint256& hash); }
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
namespace Bootstrap {
|
||||
|
||||
@@ -33,63 +46,300 @@ bool NeedsBootstrap(const fs::path& dataDir)
|
||||
return !fs::exists(dataDir / "blk0001.dat");
|
||||
}
|
||||
|
||||
// Direct TCP connection bypassing Tor SOCKS proxy.
|
||||
// Used for bootstrap downloads where the server is on clearnet.
|
||||
static SOCKET ConnectDirectTCP(const std::string& host, int port, std::string& strError)
|
||||
{
|
||||
struct addrinfo hints, *result, *rp;
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
|
||||
std::string portStr = std::to_string(port);
|
||||
int rc = getaddrinfo(host.c_str(), portStr.c_str(), &hints, &result);
|
||||
if (rc != 0) {
|
||||
strError = "DNS resolution failed for " + host;
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
SOCKET hSocket = INVALID_SOCKET;
|
||||
for (rp = result; rp != NULL; rp = rp->ai_next) {
|
||||
hSocket = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
|
||||
if (hSocket == INVALID_SOCKET)
|
||||
continue;
|
||||
|
||||
if (connect(hSocket, rp->ai_addr, (int)rp->ai_addrlen) == 0)
|
||||
break; // success
|
||||
|
||||
closesocket(hSocket);
|
||||
hSocket = INVALID_SOCKET;
|
||||
}
|
||||
freeaddrinfo(result);
|
||||
|
||||
if (hSocket == INVALID_SOCKET)
|
||||
strError = "Cannot connect to " + host + ":" + portStr;
|
||||
|
||||
return hSocket;
|
||||
}
|
||||
|
||||
// RAII wrapper for an HTTP(S) connection (socket + optional TLS)
|
||||
struct HttpConn {
|
||||
SOCKET sock;
|
||||
SSL_CTX* ctx;
|
||||
SSL* ssl;
|
||||
|
||||
HttpConn() : sock(INVALID_SOCKET), ctx(nullptr), ssl(nullptr) {}
|
||||
~HttpConn() { Close(); }
|
||||
|
||||
void Close() {
|
||||
if (ssl) { SSL_shutdown(ssl); SSL_free(ssl); ssl = nullptr; }
|
||||
if (ctx) { SSL_CTX_free(ctx); ctx = nullptr; }
|
||||
if (sock != INVALID_SOCKET) { closesocket(sock); sock = INVALID_SOCKET; }
|
||||
}
|
||||
|
||||
bool Send(const char* data, size_t len) {
|
||||
while (len > 0) {
|
||||
int n = ssl ? SSL_write(ssl, data, (int)std::min(len, (size_t)65536))
|
||||
: send(sock, data, (int)std::min(len, (size_t)65536), MSG_NOSIGNAL);
|
||||
if (n <= 0) return false;
|
||||
data += n;
|
||||
len -= n;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int Recv(char* buf, int len) {
|
||||
return ssl ? SSL_read(ssl, buf, len) : recv(sock, buf, len, 0);
|
||||
}
|
||||
|
||||
// Read until delimiter found. Returns data including delimiter.
|
||||
bool RecvUntil(std::string& out, const std::string& delim) {
|
||||
out.clear();
|
||||
char c;
|
||||
while (true) {
|
||||
int n = Recv(&c, 1);
|
||||
if (n <= 0) return false;
|
||||
out += c;
|
||||
if (out.size() >= delim.size() &&
|
||||
out.compare(out.size() - delim.size(), delim.size(), delim) == 0)
|
||||
return true;
|
||||
if (out.size() > 64 * 1024) return false; // header too large
|
||||
}
|
||||
}
|
||||
|
||||
// Establish TLS on an already-connected socket
|
||||
bool StartTLS(const std::string& hostname, std::string& strError) {
|
||||
ctx = SSL_CTX_new(TLS_client_method());
|
||||
if (!ctx) {
|
||||
strError = "Failed to create SSL context";
|
||||
return false;
|
||||
}
|
||||
// Skip cert verification — we verify data integrity via checkpoint hashes
|
||||
SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, nullptr);
|
||||
|
||||
ssl = SSL_new(ctx);
|
||||
if (!ssl) {
|
||||
strError = "Failed to create SSL object";
|
||||
return false;
|
||||
}
|
||||
SSL_set_fd(ssl, (int)sock);
|
||||
SSL_set_tlsext_host_name(ssl, hostname.c_str()); // SNI
|
||||
|
||||
if (SSL_connect(ssl) != 1) {
|
||||
unsigned long err = ERR_get_error();
|
||||
char errBuf[256];
|
||||
ERR_error_string_n(err, errBuf, sizeof(errBuf));
|
||||
strError = "TLS handshake failed with " + hostname + ": " + errBuf;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// Parse host, port, and path from an absolute URL.
|
||||
// Sets useSSL, host, port, path. Returns false for unsupported schemes.
|
||||
static bool ParseAbsoluteUrl(const std::string& url,
|
||||
bool& useSSL, std::string& host,
|
||||
int& port, std::string& path)
|
||||
{
|
||||
if (url.compare(0, 8, "https://") == 0) {
|
||||
useSSL = true;
|
||||
std::string rest = url.substr(8);
|
||||
size_t pathStart = rest.find('/');
|
||||
if (pathStart != std::string::npos) {
|
||||
host = rest.substr(0, pathStart);
|
||||
path = rest.substr(pathStart);
|
||||
} else {
|
||||
host = rest;
|
||||
path = "/";
|
||||
}
|
||||
size_t colonPos = host.find(':');
|
||||
if (colonPos != std::string::npos) {
|
||||
port = std::atoi(host.c_str() + colonPos + 1);
|
||||
host = host.substr(0, colonPos);
|
||||
} else {
|
||||
port = 443;
|
||||
}
|
||||
return true;
|
||||
} else if (url.compare(0, 7, "http://") == 0) {
|
||||
useSSL = false;
|
||||
std::string rest = url.substr(7);
|
||||
size_t pathStart = rest.find('/');
|
||||
if (pathStart != std::string::npos) {
|
||||
host = rest.substr(0, pathStart);
|
||||
path = rest.substr(pathStart);
|
||||
} else {
|
||||
host = rest;
|
||||
path = "/";
|
||||
}
|
||||
size_t colonPos = host.find(':');
|
||||
if (colonPos != std::string::npos) {
|
||||
port = std::atoi(host.c_str() + colonPos + 1);
|
||||
host = host.substr(0, colonPos);
|
||||
} else {
|
||||
port = 80;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DownloadFile(const std::string& host, const std::string& urlPath,
|
||||
const fs::path& destPath,
|
||||
ProgressCallback progressFn,
|
||||
std::string& strError)
|
||||
std::string& strError,
|
||||
bool noProxy)
|
||||
{
|
||||
try {
|
||||
boost::asio::io_context io_context;
|
||||
tcp::resolver resolver(io_context);
|
||||
std::string currentHost = host;
|
||||
std::string currentPath = urlPath;
|
||||
int currentPort = PORT;
|
||||
bool useSSL = false;
|
||||
std::string headerData;
|
||||
int redirectCount = 0;
|
||||
const int MAX_REDIRECTS = 5;
|
||||
|
||||
boost::system::error_code resolve_ec;
|
||||
tcp::resolver::results_type endpoints =
|
||||
resolver.resolve(host, std::to_string(PORT), resolve_ec);
|
||||
if (resolve_ec) {
|
||||
strError = "Cannot resolve host: " + host;
|
||||
return false;
|
||||
}
|
||||
HttpConn conn;
|
||||
|
||||
tcp::socket socket(io_context);
|
||||
boost::asio::connect(socket, endpoints);
|
||||
// Connection + redirect loop
|
||||
while (true) {
|
||||
conn.Close(); // clean slate for each attempt
|
||||
|
||||
// Send HTTP GET request
|
||||
std::string request =
|
||||
"GET " + urlPath + " HTTP/1.1\r\n"
|
||||
"Host: " + host + "\r\n"
|
||||
"Connection: close\r\n"
|
||||
"User-Agent: Triangles\r\n"
|
||||
"\r\n";
|
||||
boost::asio::write(socket, boost::asio::buffer(request));
|
||||
|
||||
// Read response headers
|
||||
boost::asio::streambuf response_buf;
|
||||
boost::asio::read_until(socket, response_buf, "\r\n\r\n");
|
||||
|
||||
std::istream response_stream(&response_buf);
|
||||
|
||||
// Parse status line
|
||||
std::string http_version;
|
||||
unsigned int status_code = 0;
|
||||
response_stream >> http_version >> status_code;
|
||||
std::string status_message;
|
||||
std::getline(response_stream, status_message);
|
||||
|
||||
if (status_code != 200) {
|
||||
strError = "HTTP error " + std::to_string(status_code) + " for " + urlPath;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse headers for Content-Length
|
||||
int64_t content_length = 0;
|
||||
std::string header_line;
|
||||
while (std::getline(response_stream, header_line) && header_line != "\r") {
|
||||
std::string lower_header = header_line;
|
||||
std::transform(lower_header.begin(), lower_header.end(),
|
||||
lower_header.begin(), ::tolower);
|
||||
if (lower_header.find("content-length:") == 0) {
|
||||
content_length = std::stoll(header_line.substr(header_line.find(':') + 1));
|
||||
if (noProxy) {
|
||||
conn.sock = ConnectDirectTCP(currentHost, currentPort, strError);
|
||||
if (conn.sock == INVALID_SOCKET)
|
||||
return false;
|
||||
} else {
|
||||
CService addr;
|
||||
if (!ConnectSocketByName(addr, conn.sock, currentHost.c_str(), currentPort, 30)) {
|
||||
strError = "Cannot connect to " + currentHost + " (check Tor proxy)";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Establish TLS when needed
|
||||
if (useSSL) {
|
||||
if (!conn.StartTLS(currentHost, strError))
|
||||
return false;
|
||||
printf("Bootstrap: TLS established with %s:%d\n",
|
||||
currentHost.c_str(), currentPort);
|
||||
}
|
||||
|
||||
// Send HTTP GET request
|
||||
std::string request =
|
||||
"GET " + currentPath + " HTTP/1.1\r\n"
|
||||
"Host: " + currentHost + "\r\n"
|
||||
"Connection: close\r\n"
|
||||
"User-Agent: Triangles\r\n"
|
||||
"\r\n";
|
||||
|
||||
if (!conn.Send(request.data(), request.size())) {
|
||||
strError = "Failed to send request to " + currentHost;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read response headers
|
||||
if (!conn.RecvUntil(headerData, "\r\n\r\n")) {
|
||||
strError = "Failed to read HTTP headers from " + currentHost;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse status code from "HTTP/1.x NNN ..."
|
||||
unsigned int status_code = 0;
|
||||
size_t sp = headerData.find(' ');
|
||||
if (sp != std::string::npos)
|
||||
status_code = atoi(headerData.c_str() + sp + 1);
|
||||
|
||||
// Handle HTTP redirects
|
||||
if (status_code == 301 || status_code == 302 ||
|
||||
status_code == 307 || status_code == 308) {
|
||||
|
||||
if (++redirectCount > MAX_REDIRECTS) {
|
||||
strError = "Too many redirects for " + urlPath;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find Location header (case-insensitive)
|
||||
std::string lowerHdr = headerData;
|
||||
std::transform(lowerHdr.begin(), lowerHdr.end(),
|
||||
lowerHdr.begin(), ::tolower);
|
||||
size_t locPos = lowerHdr.find("\nlocation:");
|
||||
if (locPos == std::string::npos) {
|
||||
strError = "Redirect " + std::to_string(status_code) + " without Location header";
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t valStart = locPos + 10; // skip "\nlocation:"
|
||||
while (valStart < headerData.size() && headerData[valStart] == ' ')
|
||||
valStart++;
|
||||
size_t lineEnd = headerData.find("\r\n", valStart);
|
||||
std::string location;
|
||||
if (lineEnd != std::string::npos)
|
||||
location = headerData.substr(valStart, lineEnd - valStart);
|
||||
else
|
||||
location = headerData.substr(valStart);
|
||||
boost::trim(location);
|
||||
|
||||
// Parse redirect URL — supports http://, https://, and relative paths
|
||||
if (location.compare(0, 7, "http://") == 0 ||
|
||||
location.compare(0, 8, "https://") == 0) {
|
||||
if (!ParseAbsoluteUrl(location, useSSL, currentHost,
|
||||
currentPort, currentPath)) {
|
||||
strError = "Unsupported redirect location: " + location;
|
||||
return false;
|
||||
}
|
||||
} else if (!location.empty() && location[0] == '/') {
|
||||
currentPath = location;
|
||||
} else {
|
||||
strError = "Unsupported redirect location: " + location;
|
||||
return false;
|
||||
}
|
||||
|
||||
printf("Bootstrap: redirect %d -> %s%s%s (port %d)\n",
|
||||
status_code, useSSL ? "https://" : "http://",
|
||||
currentHost.c_str(), currentPath.c_str(), currentPort);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (status_code != 200) {
|
||||
strError = "HTTP error " + std::to_string(status_code) + " for " + currentPath;
|
||||
return false;
|
||||
}
|
||||
|
||||
break; // Got 200, proceed to download
|
||||
}
|
||||
|
||||
// Parse Content-Length
|
||||
int64_t content_length = 0;
|
||||
std::string lowerHeaders = headerData;
|
||||
std::transform(lowerHeaders.begin(), lowerHeaders.end(),
|
||||
lowerHeaders.begin(), ::tolower);
|
||||
size_t clPos = lowerHeaders.find("content-length:");
|
||||
if (clPos != std::string::npos) {
|
||||
size_t valStart = clPos + 15;
|
||||
size_t lineEnd = lowerHeaders.find("\r\n", valStart);
|
||||
if (lineEnd != std::string::npos)
|
||||
content_length = std::stoll(headerData.substr(valStart, lineEnd - valStart));
|
||||
}
|
||||
|
||||
// Open output file
|
||||
@@ -99,46 +349,32 @@ bool DownloadFile(const std::string& host, const std::string& urlPath,
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read body in chunks
|
||||
int64_t bytes_written = 0;
|
||||
|
||||
// Write any data remaining in the header buffer (body starts here)
|
||||
if (response_buf.size() > 0) {
|
||||
std::istreambuf_iterator<char> eos;
|
||||
std::string remaining(std::istreambuf_iterator<char>(response_stream), eos);
|
||||
if (!remaining.empty()) {
|
||||
fwrite(remaining.data(), 1, remaining.size(), file);
|
||||
bytes_written += remaining.size();
|
||||
}
|
||||
}
|
||||
|
||||
// Read remaining body in chunks
|
||||
std::vector<char> chunk(65536); // 64 KB
|
||||
boost::system::error_code ec;
|
||||
int64_t last_progress = 0;
|
||||
char chunk[65536];
|
||||
|
||||
while (true) {
|
||||
size_t n = socket.read_some(boost::asio::buffer(chunk), ec);
|
||||
if (n > 0) {
|
||||
fwrite(chunk.data(), 1, n, file);
|
||||
bytes_written += n;
|
||||
|
||||
// Report progress every 256 KB
|
||||
if (progressFn && (bytes_written - last_progress >= 262144)) {
|
||||
last_progress = bytes_written;
|
||||
progressFn(bytes_written, content_length);
|
||||
}
|
||||
}
|
||||
if (ec == boost::asio::error::eof)
|
||||
break;
|
||||
if (ec) {
|
||||
int n = conn.Recv(chunk, sizeof(chunk));
|
||||
if (n < 0) {
|
||||
fclose(file);
|
||||
fs::remove(destPath);
|
||||
strError = "Network error: " + ec.message();
|
||||
strError = "Network error during download";
|
||||
return false;
|
||||
}
|
||||
if (n == 0) break; // EOF
|
||||
|
||||
fwrite(chunk, 1, n, file);
|
||||
bytes_written += n;
|
||||
|
||||
if (progressFn && (bytes_written - last_progress >= 262144)) {
|
||||
last_progress = bytes_written;
|
||||
progressFn(bytes_written, content_length);
|
||||
}
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
// conn destructor handles socket + SSL cleanup
|
||||
|
||||
// Verify download size if Content-Length was provided
|
||||
if (content_length > 0 && bytes_written != content_length) {
|
||||
@@ -158,13 +394,14 @@ bool DownloadFile(const std::string& host, const std::string& urlPath,
|
||||
|
||||
bool FetchFileList(const std::string& host,
|
||||
std::vector<std::string>& files,
|
||||
std::string& strError)
|
||||
std::string& strError,
|
||||
bool noProxy)
|
||||
{
|
||||
// Download filelist.txt to a temp file
|
||||
fs::path tmpPath = fs::temp_directory_path() / "triangles_bootstrap_filelist.txt";
|
||||
|
||||
std::string urlPath = std::string(BASE_PATH) + "filelist.txt";
|
||||
if (!DownloadFile(host, urlPath, tmpPath, nullptr, strError))
|
||||
if (!DownloadFile(host, urlPath, tmpPath, nullptr, strError, noProxy))
|
||||
return false;
|
||||
|
||||
// Read lines
|
||||
@@ -433,10 +670,12 @@ bool DownloadBootstrap(const std::string& host,
|
||||
bool gotBlockFile = false;
|
||||
|
||||
// Try downloading bootstrap.tar.gz first
|
||||
// Bootstrap server is on clearnet — bypass Tor proxy for DNS + HTTP
|
||||
const bool noProxy = true;
|
||||
fs::path tmpTarGz = dataDir / "bootstrap.tar.gz.tmp";
|
||||
std::string tarUrl = std::string(BASE_PATH) + "bootstrap.tar.gz";
|
||||
std::string tarUrl = std::string(BASE_PATH) + "triangles-bootstrap.tar.gz";
|
||||
|
||||
bool tarDownloaded = DownloadFile(host, tarUrl, tmpTarGz, progressFn, strError);
|
||||
bool tarDownloaded = DownloadFile(host, tarUrl, tmpTarGz, progressFn, strError, noProxy);
|
||||
|
||||
if (tarDownloaded) {
|
||||
bool extractOk = ExtractTarGz(tmpTarGz, dataDir, strError);
|
||||
@@ -451,7 +690,7 @@ bool DownloadBootstrap(const std::string& host,
|
||||
// Fallback: try filelist.txt + individual file downloads
|
||||
std::string fallbackError;
|
||||
std::vector<std::string> files;
|
||||
if (!FetchFileList(host, files, fallbackError)) {
|
||||
if (!FetchFileList(host, files, fallbackError, noProxy)) {
|
||||
if (!tarDownloaded)
|
||||
strError = strError + " (fallback also failed: " + fallbackError + ")";
|
||||
else
|
||||
@@ -464,7 +703,7 @@ bool DownloadBootstrap(const std::string& host,
|
||||
fs::create_directories(destPath.parent_path());
|
||||
|
||||
std::string urlPath = std::string(BASE_PATH) + files[i];
|
||||
if (!DownloadFile(host, urlPath, destPath, progressFn, strError))
|
||||
if (!DownloadFile(host, urlPath, destPath, progressFn, strError, noProxy))
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -528,4 +767,38 @@ bool DownloadBootstrap(const std::string& host,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DownloadUtxoSnapshot(const std::string& host,
|
||||
const fs::path& dataDir,
|
||||
ProgressCallback progressFn,
|
||||
std::string& strError)
|
||||
{
|
||||
const bool noProxy = true;
|
||||
const char* snapshotFilename = "utxo-snapshot.bin";
|
||||
|
||||
// Download utxo-snapshot.bin to a temp file
|
||||
fs::path tmpPath = dataDir / "utxo-snapshot.bin.tmp";
|
||||
std::string urlPath = std::string(BASE_PATH) + snapshotFilename;
|
||||
|
||||
printf("Bootstrap: downloading UTXO snapshot from %s%s...\n", host.c_str(), urlPath.c_str());
|
||||
|
||||
if (!DownloadFile(host, urlPath, tmpPath, progressFn, strError, noProxy)) {
|
||||
fs::remove(tmpPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
printf("Bootstrap: UTXO snapshot downloaded, loading into database...\n");
|
||||
|
||||
// Load the snapshot into a fresh txleveldb
|
||||
if (!UtxoSnapshot::LoadSnapshot(tmpPath, dataDir, strError)) {
|
||||
fs::remove(tmpPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Clean up the temp file
|
||||
fs::remove(tmpPath);
|
||||
|
||||
printf("Bootstrap: UTXO snapshot loaded successfully.\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Bootstrap
|
||||
|
||||
@@ -13,7 +13,6 @@ namespace Bootstrap {
|
||||
|
||||
// Bootstrap server configuration
|
||||
static const char* DEFAULT_HOST = "bootstrap.cryptographic-triangles.org";
|
||||
static const char* FALLBACK_HOST = "194.233.88.206";
|
||||
static const char* BASE_PATH = "/";
|
||||
static const int PORT = 80;
|
||||
|
||||
@@ -23,16 +22,20 @@ namespace Bootstrap {
|
||||
// Check if data dir already has blockchain data
|
||||
bool NeedsBootstrap(const boost::filesystem::path& dataDir);
|
||||
|
||||
// Download a single file via HTTP GET, write to destPath
|
||||
// Download a single file via HTTP GET, write to destPath.
|
||||
// If noProxy is true, bypass Tor SOCKS proxy and connect directly
|
||||
// (used for clearnet bootstrap downloads).
|
||||
bool DownloadFile(const std::string& host, const std::string& urlPath,
|
||||
const boost::filesystem::path& destPath,
|
||||
ProgressCallback progressFn,
|
||||
std::string& strError);
|
||||
std::string& strError,
|
||||
bool noProxy = false);
|
||||
|
||||
// Fetch the file manifest (list of relative paths to download)
|
||||
bool FetchFileList(const std::string& host,
|
||||
std::vector<std::string>& files,
|
||||
std::string& strError);
|
||||
std::string& strError,
|
||||
bool noProxy = false);
|
||||
|
||||
// Download bootstrap.tar.gz and extract to dataDir.
|
||||
// Falls back to filelist.txt + individual file download if tar.gz unavailable.
|
||||
@@ -59,6 +62,14 @@ namespace Bootstrap {
|
||||
bool VerifyManifest(const SnapshotManifest& manifest,
|
||||
std::string& strError);
|
||||
|
||||
// Download a UTXO snapshot and load it into a fresh txleveldb.
|
||||
// This is much faster than downloading the full bootstrap archive.
|
||||
// Returns true if snapshot was downloaded and loaded successfully.
|
||||
bool DownloadUtxoSnapshot(const std::string& host,
|
||||
const boost::filesystem::path& dataDir,
|
||||
ProgressCallback progressFn,
|
||||
std::string& strError);
|
||||
|
||||
} // namespace Bootstrap
|
||||
|
||||
#endif // TRIANGLES_BOOTSTRAP_H
|
||||
|
||||
@@ -33,6 +33,13 @@ namespace Checkpoints
|
||||
{ 10881, uint256("0x4b6554c45e1e6764a6f3c309c47baf53c9edd81f624e52b072518cd15da237e6")},
|
||||
{ 17650, uint256("0x224940e1f986a202209b8e762728d1452ab45870c308abf84905674acf326a47")},
|
||||
{2186940, uint256("0xbd952e8d4a612e336d840ad924a7e09395e36bcd9d929b302e47e60b5c3098c0")},
|
||||
{2190000, uint256("0x682baf783581468ba18f9967254a7f3944e8b8c4cc7101e7d99b68f4f9dd5271")},
|
||||
{2200000, uint256("0x0a8d0442f031f1258120f713f34e45f4f9a625fb753558e27b89b32ad5a9a740")},
|
||||
{2205000, uint256("0xf7aa893ec012181e321783d6a5487addf6997377908faa3e760c9054a4217d29")},
|
||||
{2206000, uint256("0x780ae878f8b10b6cbd51ceb2c0799c90d3551fa916be62974375071b9e36581c")},
|
||||
{2207000, uint256("0x8836d67b0f08036c4a7c26ff0a29d4461a52b6d8f552165ad9c1abec2f3cadfd")},
|
||||
{2208000, uint256("0xe4a19e8a29fa7aae47f7563377af3e896fee18ed069a64e325b8c2c6c820a1be")},
|
||||
{2209000, uint256("0x04c78a6fc863bed918a9364c58c64489943b2e85d84ddb1ac2fba584f390d5dc")},
|
||||
};
|
||||
|
||||
static MapCheckpoints mapCheckpointsTestnet = {
|
||||
@@ -49,6 +56,13 @@ namespace Checkpoints
|
||||
{ 10881, uint256("0x4b6554c45e1e6764a6f3c309c47baf53c9edd81f624e52b072518cd15da237e6")},
|
||||
{ 17650, uint256("0x224940e1f986a202209b8e762728d1452ab45870c308abf84905674acf326a47")},
|
||||
{2186940, uint256("0xbd952e8d4a612e336d840ad924a7e09395e36bcd9d929b302e47e60b5c3098c0")},
|
||||
{2190000, uint256("0x682baf783581468ba18f9967254a7f3944e8b8c4cc7101e7d99b68f4f9dd5271")},
|
||||
{2200000, uint256("0x0a8d0442f031f1258120f713f34e45f4f9a625fb753558e27b89b32ad5a9a740")},
|
||||
{2205000, uint256("0xf7aa893ec012181e321783d6a5487addf6997377908faa3e760c9054a4217d29")},
|
||||
{2206000, uint256("0x780ae878f8b10b6cbd51ceb2c0799c90d3551fa916be62974375071b9e36581c")},
|
||||
{2207000, uint256("0x8836d67b0f08036c4a7c26ff0a29d4461a52b6d8f552165ad9c1abec2f3cadfd")},
|
||||
{2208000, uint256("0xe4a19e8a29fa7aae47f7563377af3e896fee18ed069a64e325b8c2c6c820a1be")},
|
||||
{2209000, uint256("0x04c78a6fc863bed918a9364c58c64489943b2e85d84ddb1ac2fba584f390d5dc")},
|
||||
};
|
||||
|
||||
bool CheckHardened(int nHeight, const uint256& hash)
|
||||
|
||||
@@ -0,0 +1,181 @@
|
||||
// Copyright (c) 2012-2013 The Bitcoin developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#ifndef TRIANGLES_CHECKQUEUE_H
|
||||
#define TRIANGLES_CHECKQUEUE_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <deque>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/thread/condition_variable.hpp>
|
||||
#include <boost/thread/mutex.hpp>
|
||||
#include <boost/thread/thread.hpp>
|
||||
|
||||
template<typename T>
|
||||
class CCheckQueue
|
||||
{
|
||||
private:
|
||||
boost::mutex mutex;
|
||||
boost::condition_variable condWorker;
|
||||
boost::condition_variable condMaster;
|
||||
|
||||
std::deque<T> queue;
|
||||
unsigned int nIdle;
|
||||
unsigned int nTotal;
|
||||
bool fAllOk;
|
||||
unsigned int nTodo;
|
||||
bool fQuit;
|
||||
|
||||
unsigned int nBatchSize;
|
||||
|
||||
bool Loop(bool fMaster)
|
||||
{
|
||||
boost::unique_lock<boost::mutex> lock(mutex);
|
||||
if (!fMaster)
|
||||
nTotal++;
|
||||
nIdle++;
|
||||
|
||||
bool fOk = true;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
while (queue.empty())
|
||||
{
|
||||
if (fQuit)
|
||||
{
|
||||
nIdle--;
|
||||
if (!fMaster)
|
||||
nTotal--;
|
||||
return false;
|
||||
}
|
||||
if (fMaster && nTodo == 0)
|
||||
{
|
||||
bool fRet = fAllOk;
|
||||
nIdle--;
|
||||
return fRet;
|
||||
}
|
||||
if (fMaster)
|
||||
condMaster.wait(lock);
|
||||
else
|
||||
condWorker.wait(lock);
|
||||
}
|
||||
|
||||
unsigned int nNow = std::max(1U, std::min((unsigned int)queue.size() / (nTotal + 1), nBatchSize));
|
||||
std::vector<T> vChecks(nNow);
|
||||
for (unsigned int i = 0; i < nNow; i++)
|
||||
{
|
||||
vChecks[i].swap(queue.front());
|
||||
queue.pop_front();
|
||||
}
|
||||
nIdle--;
|
||||
lock.unlock();
|
||||
|
||||
for (unsigned int i = 0; i < vChecks.size(); i++)
|
||||
{
|
||||
if (fOk)
|
||||
fOk = vChecks[i]();
|
||||
}
|
||||
vChecks.clear();
|
||||
|
||||
lock.lock();
|
||||
nIdle++;
|
||||
nTodo -= nNow;
|
||||
if (!fOk)
|
||||
fAllOk = false;
|
||||
|
||||
if (nTodo == 0)
|
||||
condMaster.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
CCheckQueue(unsigned int nBatchSizeIn = 128)
|
||||
: nIdle(0), nTotal(0), fAllOk(true), nTodo(0), fQuit(false),
|
||||
nBatchSize(nBatchSizeIn) {}
|
||||
|
||||
void Thread()
|
||||
{
|
||||
Loop(false);
|
||||
}
|
||||
|
||||
void StartBatch()
|
||||
{
|
||||
boost::unique_lock<boost::mutex> lock(mutex);
|
||||
fAllOk = true;
|
||||
nTodo = 0;
|
||||
}
|
||||
|
||||
void Add(std::vector<T>& vChecks)
|
||||
{
|
||||
if (vChecks.empty())
|
||||
return;
|
||||
|
||||
boost::unique_lock<boost::mutex> lock(mutex);
|
||||
for (typename std::vector<T>::iterator it = vChecks.begin(); it != vChecks.end(); ++it)
|
||||
{
|
||||
queue.push_back(T());
|
||||
queue.back().swap(*it);
|
||||
}
|
||||
nTodo += vChecks.size();
|
||||
if (vChecks.size() == 1)
|
||||
condWorker.notify_one();
|
||||
else
|
||||
condWorker.notify_all();
|
||||
}
|
||||
|
||||
bool Wait()
|
||||
{
|
||||
return Loop(true);
|
||||
}
|
||||
|
||||
void Quit()
|
||||
{
|
||||
boost::unique_lock<boost::mutex> lock(mutex);
|
||||
fQuit = true;
|
||||
condWorker.notify_all();
|
||||
condMaster.notify_all();
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class CCheckQueueControl
|
||||
{
|
||||
private:
|
||||
CCheckQueue<T>* pqueue;
|
||||
bool fDone;
|
||||
|
||||
CCheckQueueControl(const CCheckQueueControl&);
|
||||
CCheckQueueControl& operator=(const CCheckQueueControl&);
|
||||
|
||||
public:
|
||||
CCheckQueueControl(CCheckQueue<T>* pqueueIn)
|
||||
: pqueue(pqueueIn), fDone(false)
|
||||
{
|
||||
if (pqueue)
|
||||
pqueue->StartBatch();
|
||||
}
|
||||
|
||||
bool Wait()
|
||||
{
|
||||
if (!pqueue || fDone)
|
||||
return true;
|
||||
fDone = true;
|
||||
return pqueue->Wait();
|
||||
}
|
||||
|
||||
void Add(std::vector<T>& vChecks)
|
||||
{
|
||||
if (pqueue)
|
||||
pqueue->Add(vChecks);
|
||||
}
|
||||
|
||||
~CCheckQueueControl()
|
||||
{
|
||||
if (!fDone)
|
||||
Wait();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // TRIANGLES_CHECKQUEUE_H
|
||||
@@ -7,8 +7,8 @@
|
||||
|
||||
// These need to be macros, as version.cpp's and triangles-qt.rc's voodoo requires it
|
||||
#define CLIENT_VERSION_MAJOR 5
|
||||
#define CLIENT_VERSION_MINOR 5
|
||||
#define CLIENT_VERSION_REVISION 1
|
||||
#define CLIENT_VERSION_MINOR 9
|
||||
#define CLIENT_VERSION_REVISION 0
|
||||
#define CLIENT_VERSION_BUILD 0
|
||||
|
||||
// Converts the parameter X to a string after macro replacement on X has been performed.
|
||||
|
||||
@@ -28,6 +28,7 @@ extern unsigned int nWalletDBUpdated;
|
||||
|
||||
void ThreadFlushWalletDB(void* parg);
|
||||
bool BackupWallet(const CWallet& wallet, const std::string& strDest);
|
||||
bool AutoBackupWallet(const boost::filesystem::path& walletPath);
|
||||
|
||||
|
||||
class CDBEnv
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "walletdb.h"
|
||||
#include "trianglesrpc.h"
|
||||
#include "net.h"
|
||||
#include "netbase.h"
|
||||
#include "init.h"
|
||||
#include "util.h"
|
||||
#include "ui_interface.h"
|
||||
@@ -13,6 +14,7 @@
|
||||
#include "smessage.h"
|
||||
#include "openssl_compat.h"
|
||||
#include "bootstrap.h"
|
||||
#include "utxosnapshot.h"
|
||||
#include "tor/tor_embedded.h"
|
||||
#include "tor/onion_v3.h"
|
||||
#include "tor/tor_process.h"
|
||||
@@ -21,6 +23,7 @@
|
||||
#endif
|
||||
#include "notificationqueue.h"
|
||||
#include "addressindex.h"
|
||||
#include <boost/thread.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
// boost/filesystem/convenience.hpp removed in modern Boost; functionality is in filesystem.hpp
|
||||
@@ -50,6 +53,14 @@ enum Checkpoints::CPMode CheckpointsMode;
|
||||
|
||||
static CCriticalSection cs_DeferredStartup;
|
||||
static bool fDeferredStartupRunning = false;
|
||||
static boost::thread_group* pScriptCheckThreads = NULL;
|
||||
|
||||
static void ThreadScriptCheck()
|
||||
{
|
||||
RenameThread("Triangles-scrchk");
|
||||
if (pScriptCheckQueue)
|
||||
pScriptCheckQueue->Thread();
|
||||
}
|
||||
|
||||
static void StartupPerfLog(const char* phase, int64_t elapsedMs)
|
||||
{
|
||||
@@ -181,6 +192,19 @@ void Shutdown(void* parg)
|
||||
nTransactionsUpdated++;
|
||||
StopNode();
|
||||
|
||||
if (pScriptCheckQueue)
|
||||
{
|
||||
pScriptCheckQueue->Quit();
|
||||
if (pScriptCheckThreads)
|
||||
{
|
||||
pScriptCheckThreads->join_all();
|
||||
delete pScriptCheckThreads;
|
||||
pScriptCheckThreads = NULL;
|
||||
}
|
||||
delete pScriptCheckQueue;
|
||||
pScriptCheckQueue = NULL;
|
||||
}
|
||||
|
||||
// NOW safe to destroy Tor state - all threads have stopped
|
||||
ShutdownTorV3();
|
||||
StopEmbeddedTor();
|
||||
@@ -357,7 +381,7 @@ std::string HelpMessage()
|
||||
//" -proxy=<ip:port> " + _("Connect through socks proxy") + "\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"
|
||||
" -notor " + _("Disable Tor startup and .onion connectivity") + "\n" +
|
||||
" -notor " + _("Disable Tor (WARNING: wallet will not start - Tor is required)") + "\n" +
|
||||
" -torsocks=<port> " + _("Set embedded or managed Tor SOCKS proxy port (default: 19099)") + "\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" +
|
||||
@@ -382,6 +406,7 @@ std::string HelpMessage()
|
||||
" -noseedurl " + _("Disable HTTP seed list fetch on startup") + "\n" +
|
||||
" -banscore=<n> " + _("Threshold for disconnecting misbehaving peers (default: 100)") + "\n" +
|
||||
" -bantime=<n> " + _("Number of seconds to keep misbehaving peers from reconnecting (default: 86400)") + "\n" +
|
||||
" -par=<n> " + _("Set the number of script verification threads (default: auto, 0 = auto, 1 = single-threaded)") + "\n" +
|
||||
" -maxreceivebuffer=<n> " + _("Maximum per-connection receive buffer, <n>*1000 bytes (default: 5000)") + "\n" +
|
||||
" -maxsendbuffer=<n> " + _("Maximum per-connection send buffer, <n>*1000 bytes (default: 1000)") + "\n" +
|
||||
#ifdef USE_UPNP
|
||||
@@ -423,6 +448,7 @@ std::string HelpMessage()
|
||||
" -keypool=<n> " + _("Set key pool size to <n> (default: 100)") + "\n" +
|
||||
" -rescan " + _("Rescan the block chain for missing wallet transactions") + "\n" +
|
||||
" -postibdrescan " + _("Run the wallet rescan after initial sync in a background thread (default: 1)") + "\n" +
|
||||
" -zapwallettxes " + _("Delete all wallet transactions and only recover from blockchain on startup") + "\n" +
|
||||
" -salvagewallet " + _("Attempt to recover private keys from a corrupt wallet.dat") + "\n" +
|
||||
" -checkblocks=<n> " + _("How many blocks to check at startup (default: 2500, 0 = all)") + "\n" +
|
||||
" -checklevel=<n> " + _("How thorough the block verification is (0-6, default: 1)") + "\n" +
|
||||
@@ -577,6 +603,11 @@ bool AppInit2()
|
||||
SoftSetBoolArg("-rescan", true);
|
||||
}
|
||||
|
||||
if (GetBoolArg("-zapwallettxes")) {
|
||||
// Zap all tx from wallet: rescan to rebuild from blockchain
|
||||
SoftSetBoolArg("-rescan", true);
|
||||
}
|
||||
|
||||
// ********************************************************* Step 3: parameter-to-internal-flags
|
||||
|
||||
fDebug = GetBoolArg("-debug");
|
||||
@@ -632,6 +663,20 @@ bool AppInit2()
|
||||
fConfChange = GetBoolArg("-confchange", false);
|
||||
fEnforceCanonical = GetBoolArg("-enforcecanonical", true);
|
||||
|
||||
int nScriptCheckThreads = GetArg("-par", 0);
|
||||
if (nScriptCheckThreads <= 0)
|
||||
nScriptCheckThreads = boost::thread::hardware_concurrency();
|
||||
if (nScriptCheckThreads > 16)
|
||||
nScriptCheckThreads = 16;
|
||||
if (nScriptCheckThreads > 1)
|
||||
{
|
||||
pScriptCheckQueue = new CCheckQueue<CScriptCheck>(32);
|
||||
pScriptCheckThreads = new boost::thread_group();
|
||||
for (int i = 0; i < nScriptCheckThreads - 1; ++i)
|
||||
pScriptCheckThreads->create_thread(&ThreadScriptCheck);
|
||||
printf("Script verification threads: %d workers + main thread\n", nScriptCheckThreads - 1);
|
||||
}
|
||||
|
||||
fAddressIndex = GetBoolArg("-addressindex", false);
|
||||
if (fAddressIndex)
|
||||
printf("Address index enabled\n");
|
||||
@@ -720,6 +765,13 @@ bool AppInit2()
|
||||
return false;
|
||||
}
|
||||
|
||||
if (GetBoolArg("-zapwallettxes") && fs::exists(GetDataDir() / strWalletFileName))
|
||||
{
|
||||
uiInterface.InitMessage(_("Zapping all transactions from wallet..."));
|
||||
if (!CWalletDB::ZapWalletTx(strWalletFileName))
|
||||
return InitError(_("Error: could not zap wallet transactions"));
|
||||
}
|
||||
|
||||
if (fs::exists(GetDataDir() / strWalletFileName))
|
||||
{
|
||||
CDBEnv::VerifyResult r = bitdb.Verify(strWalletFileName, CWalletDB::Recover);
|
||||
@@ -744,9 +796,9 @@ bool AppInit2()
|
||||
//if (nSocksVersion != 4 && nSocksVersion != 5)
|
||||
// return InitError(strprintf(_("Unknown -socks proxy version requested: %i"), nSocksVersion));
|
||||
|
||||
// Network selection: enable all networks (IPv4, IPv6, Tor)
|
||||
// Tor is always enabled; clearnet is also allowed for seed node discovery
|
||||
// Users can restrict to Tor-only with -onlynet=tor
|
||||
// Network selection: Tor-native mode
|
||||
// All traffic routes through embedded Tor. Clearnet (IPv4/IPv6) is disabled
|
||||
// after Tor starts successfully. Only .onion peers are accepted.
|
||||
if (mapArgs.count("-onlynet")) {
|
||||
std::set<enum Network> nets;
|
||||
for (std::string snet : mapMultiArgs["-onlynet"]) {
|
||||
@@ -834,17 +886,28 @@ bool AppInit2()
|
||||
StartupPerfLog("network_init", GetTimeMillis() - nStart, strprintf("listen=%d seednodes=%" PRIszu, !fNoListen, mapMultiArgs["-seednode"].size()));
|
||||
|
||||
// ********************************************************* Step 6b: bootstrap download (daemon)
|
||||
// Automatic: if data dir has no blockchain, bootstrap without asking.
|
||||
// Can also be forced with -bootstrap flag, or disabled with -nobootstrap.
|
||||
#ifndef QT_GUI
|
||||
if (GetBoolArg("-bootstrap", false))
|
||||
{
|
||||
bool wantsBootstrap = GetBoolArg("-bootstrap", false);
|
||||
bool noBootstrap = GetBoolArg("-nobootstrap", false);
|
||||
fs::path dataPath = GetDataDir();
|
||||
bool needsBootstrap = Bootstrap::NeedsBootstrap(dataPath);
|
||||
|
||||
if (needsBootstrap && !noBootstrap) {
|
||||
printf("Bootstrap: no blockchain data found — downloading automatically.\n");
|
||||
printf("Bootstrap: (use -nobootstrap to skip)\n");
|
||||
wantsBootstrap = true;
|
||||
}
|
||||
|
||||
if (wantsBootstrap)
|
||||
{
|
||||
int64_t nBootstrapStart = GetTimeMillis();
|
||||
fs::path dataPath = GetDataDir();
|
||||
std::string host = Bootstrap::DEFAULT_HOST;
|
||||
std::string strError;
|
||||
|
||||
uiInterface.InitMessage(_("Downloading blockchain snapshot..."));
|
||||
printf("Bootstrap: contacting %s...\n", host.c_str());
|
||||
|
||||
auto progressFn = [](int64_t bytesDownloaded, int64_t totalBytes) {
|
||||
if (totalBytes > 0) {
|
||||
printf("\rBootstrap: %lld / %lld MB (%lld%%)",
|
||||
@@ -855,24 +918,66 @@ bool AppInit2()
|
||||
}
|
||||
};
|
||||
|
||||
bool success = Bootstrap::DownloadBootstrap(host, dataPath, progressFn, strError);
|
||||
if (!success) {
|
||||
host = Bootstrap::FALLBACK_HOST;
|
||||
printf("\nBootstrap: primary host failed, trying fallback %s...\n", host.c_str());
|
||||
success = Bootstrap::DownloadBootstrap(host, dataPath, progressFn, strError);
|
||||
// Try UTXO snapshot first (fast: ~2-10 MB download)
|
||||
bool success = false;
|
||||
bool triedUtxoSnapshot = false;
|
||||
if (needsBootstrap && !fs::exists(dataPath / "txleveldb")) {
|
||||
uiInterface.InitMessage(_("Downloading UTXO snapshot..."));
|
||||
printf("Bootstrap: trying UTXO snapshot from %s (fast path)...\n", host.c_str());
|
||||
|
||||
std::string utxoError;
|
||||
if (Bootstrap::DownloadUtxoSnapshot(host, dataPath, progressFn, utxoError)) {
|
||||
printf("\nBootstrap: UTXO snapshot loaded — will sync remaining blocks from network.\n");
|
||||
success = true;
|
||||
} else {
|
||||
printf("\nBootstrap: UTXO snapshot unavailable: %s\n", utxoError.c_str());
|
||||
printf("Bootstrap: falling back to full bootstrap download...\n");
|
||||
}
|
||||
triedUtxoSnapshot = true;
|
||||
}
|
||||
|
||||
// Fall back to full bootstrap.tar.gz if UTXO snapshot failed
|
||||
if (!success) {
|
||||
printf("\nBootstrap: failed: %s\n", strError.c_str());
|
||||
printf("Bootstrap: skipping, will sync from network.\n");
|
||||
} else {
|
||||
printf("\nBootstrap: done.\n");
|
||||
uiInterface.InitMessage(_("Downloading blockchain snapshot..."));
|
||||
printf("Bootstrap: contacting %s...\n", host.c_str());
|
||||
|
||||
success = Bootstrap::DownloadBootstrap(host, dataPath, progressFn, strError);
|
||||
|
||||
if (!success) {
|
||||
printf("\nBootstrap: failed: %s\n", strError.c_str());
|
||||
printf("Bootstrap: skipping, will sync from network.\n");
|
||||
} else {
|
||||
printf("\nBootstrap: done.\n");
|
||||
}
|
||||
}
|
||||
|
||||
StartupPerfLog("bootstrap_download", GetTimeMillis() - nBootstrapStart,
|
||||
strprintf("host=%s success=%d", host.c_str(), success));
|
||||
strprintf("host=%s success=%d utxo_snapshot=%d", host.c_str(), success, triedUtxoSnapshot));
|
||||
}
|
||||
} // end bootstrap scope
|
||||
#endif
|
||||
|
||||
// ********************************************************* Step 6c: manual UTXO snapshot loading
|
||||
// If utxo-snapshot.bin exists in data dir and no txleveldb, load it.
|
||||
{
|
||||
fs::path dataPath = GetDataDir();
|
||||
fs::path snapshotFile = dataPath / "utxo-snapshot.bin";
|
||||
fs::path txleveldbDir = dataPath / "txleveldb";
|
||||
|
||||
if (fs::exists(snapshotFile) && !fs::exists(txleveldbDir)) {
|
||||
printf("Found utxo-snapshot.bin — loading UTXO snapshot...\n");
|
||||
uiInterface.InitMessage(_("Loading UTXO snapshot..."));
|
||||
|
||||
std::string strError;
|
||||
if (UtxoSnapshot::LoadSnapshot(snapshotFile, dataPath, strError)) {
|
||||
printf("UTXO snapshot loaded successfully.\n");
|
||||
} else {
|
||||
printf("UTXO snapshot load failed: %s\n", strError.c_str());
|
||||
printf("Will proceed with normal sync.\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ********************************************************* Step 7: load blockchain
|
||||
|
||||
if (!bitdb.Open(GetDataDir()))
|
||||
@@ -891,6 +996,18 @@ bool AppInit2()
|
||||
return false;
|
||||
}
|
||||
|
||||
// Handle -reindex: delete the LevelDB block index so it gets rebuilt
|
||||
// from the raw blk*.dat files via FastImportBlockFile().
|
||||
// This recalculates money supply, tx index, and UTXO set from scratch.
|
||||
if (GetBoolArg("-reindex", false))
|
||||
{
|
||||
printf("Reindex requested: removing block index database...\n");
|
||||
uiInterface.InitMessage(_("Removing block index for reindex..."));
|
||||
fs::path txleveldbPath = GetDataDir() / "txleveldb";
|
||||
if (fs::exists(txleveldbPath))
|
||||
fs::remove_all(txleveldbPath);
|
||||
}
|
||||
|
||||
uiInterface.InitMessage(_("Loading block index..."));
|
||||
printf("Loading block index...\n");
|
||||
nStart = GetTimeMillis();
|
||||
@@ -954,7 +1071,11 @@ bool AppInit2()
|
||||
{
|
||||
CBlockIndex* pindex = (*mi).second;
|
||||
CBlock block;
|
||||
block.ReadFromDisk(pindex);
|
||||
if (!block.ReadFromDisk(pindex))
|
||||
{
|
||||
printf("Error: Failed to read block %s from disk\n", hash.ToString().c_str());
|
||||
continue;
|
||||
}
|
||||
block.BuildMerkleTree();
|
||||
block.print();
|
||||
printf("\n");
|
||||
@@ -976,6 +1097,21 @@ bool AppInit2()
|
||||
nStart = GetTimeMillis();
|
||||
bool fFirstRun = true;
|
||||
pwalletMain = new CWallet(strWalletFileName);
|
||||
|
||||
// Auto-backup wallet.dat before loading (protects against corruption during load/flush)
|
||||
{
|
||||
fs::path walletPath = GetDataDir() / strWalletFileName;
|
||||
if (fs::exists(walletPath)) {
|
||||
uintmax_t wsize = fs::file_size(walletPath);
|
||||
printf("Wallet file size: %llu bytes\n", (unsigned long long)wsize);
|
||||
if (wsize < 1024) {
|
||||
strErrors << _("WARNING: wallet.dat is suspiciously small (") << wsize << _(" bytes). It may be corrupt.\n");
|
||||
printf("WARNING: wallet.dat is only %llu bytes - possibly corrupt!\n", (unsigned long long)wsize);
|
||||
}
|
||||
AutoBackupWallet(walletPath);
|
||||
}
|
||||
}
|
||||
|
||||
DBErrors nLoadWalletRet = pwalletMain->LoadWallet(fFirstRun);
|
||||
if (nLoadWalletRet != DB_LOAD_OK)
|
||||
{
|
||||
@@ -1131,9 +1267,32 @@ bool AppInit2()
|
||||
if (torStarted) {
|
||||
printf("Tor process running, SOCKS proxy at %s\n",
|
||||
CTorEmbedded::GetInstance()->GetSocksProxy().c_str());
|
||||
|
||||
// TOR-NATIVE MODE: Force all traffic through embedded Tor
|
||||
int socksPort = CTorEmbedded::GetInstance()->GetSocksPort();
|
||||
CService torProxyAddr("127.0.0.1", socksPort);
|
||||
|
||||
SetProxy(NET_IPV4, torProxyAddr, 5);
|
||||
SetProxy(NET_IPV6, torProxyAddr, 5);
|
||||
SetProxy(NET_TOR, torProxyAddr, 5);
|
||||
SetNameProxy(torProxyAddr, 5);
|
||||
|
||||
// Disable clearnet reachability - ONION ONLY
|
||||
SetReachable(NET_IPV4, false);
|
||||
SetReachable(NET_IPV6, false);
|
||||
SetReachable(NET_TOR, true);
|
||||
|
||||
printf("TOR-NATIVE MODE: All network traffic forced through Tor\n");
|
||||
printf(" Clearnet disabled - .onion addresses only\n");
|
||||
|
||||
#ifdef USE_UPNP
|
||||
fUseUPnP = false;
|
||||
#endif
|
||||
} else {
|
||||
printf("WARNING: Tor not available. .onion peers will not be reachable.\n");
|
||||
printf(" Clearnet connections will still work normally.\n");
|
||||
std::string torError = CTorEmbedded::GetInstance()->GetStartupError();
|
||||
if (torError.empty())
|
||||
torError = "No detailed Tor startup error was recorded.";
|
||||
return InitError(strprintf(_("Tor failed to start. Triangles requires Tor to operate.\n\nDetails: %s"), torError.c_str()));
|
||||
}
|
||||
|
||||
// Initialize Tor V3 identity (Ed25519 keys, onion address)
|
||||
|
||||
@@ -0,0 +1,284 @@
|
||||
// Copyright (c) 2024-2026 Triangles developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
//
|
||||
// Drop-in compatibility layer: json_spirit API backed by nlohmann/json.
|
||||
// Provides the same types (Value, Object, Array, Pair) and functions
|
||||
// (find_value, write_string, read_string) in the json_spirit namespace,
|
||||
// so existing RPC code compiles without changes.
|
||||
|
||||
#ifndef JSON_COMPAT_H
|
||||
#define JSON_COMPAT_H
|
||||
|
||||
#include "nlohmann_json.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace json_spirit {
|
||||
|
||||
// Use ordered_json to preserve insertion order (matches json_spirit's vector<Pair> behavior)
|
||||
typedef nlohmann::ordered_json json_internal;
|
||||
|
||||
// ---------- Type enum (matches json_spirit exactly) ----------
|
||||
|
||||
enum Value_type {
|
||||
obj_type = 0,
|
||||
array_type = 1,
|
||||
str_type = 2,
|
||||
bool_type = 3,
|
||||
int_type = 4,
|
||||
real_type = 5,
|
||||
null_type = 6
|
||||
};
|
||||
|
||||
inline const char* ValueTypeName(Value_type type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case obj_type: return "obj";
|
||||
case array_type: return "array";
|
||||
case str_type: return "str";
|
||||
case bool_type: return "bool";
|
||||
case int_type: return "int";
|
||||
case real_type: return "real";
|
||||
case null_type: return "null";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
// ---------- Forward declarations ----------
|
||||
|
||||
class Value;
|
||||
struct Pair;
|
||||
|
||||
// ---------- Value ----------
|
||||
|
||||
class Value {
|
||||
json_internal j_;
|
||||
|
||||
friend std::string write_string(const Value& val, bool pretty);
|
||||
friend bool read_string(const std::string& s, Value& val);
|
||||
friend bool read(const std::string& s, Value& val);
|
||||
|
||||
public:
|
||||
// Default: null
|
||||
Value() : j_(nullptr) {}
|
||||
|
||||
// Primitives
|
||||
Value(const char* s) : j_(std::string(s)) {}
|
||||
Value(const std::string& s) : j_(s) {}
|
||||
Value(bool b) : j_(b) {}
|
||||
Value(int i) : j_(static_cast<int64_t>(i)) {}
|
||||
Value(unsigned int u) : j_(static_cast<uint64_t>(u)) {}
|
||||
Value(int64_t i) : j_(i) {}
|
||||
Value(uint64_t u) : j_(u) {}
|
||||
Value(double d) : j_(d) {}
|
||||
|
||||
// Compound types (defined after Pair/Object/Array)
|
||||
inline Value(const std::vector<Pair>& obj);
|
||||
inline Value(const std::vector<Value>& arr);
|
||||
|
||||
// Type introspection
|
||||
Value_type type() const {
|
||||
if (j_.is_null()) return null_type;
|
||||
if (j_.is_object()) return obj_type;
|
||||
if (j_.is_array()) return array_type;
|
||||
if (j_.is_string()) return str_type;
|
||||
if (j_.is_boolean()) return bool_type;
|
||||
if (j_.is_number_integer()) return int_type;
|
||||
if (j_.is_number_float()) return real_type;
|
||||
return null_type;
|
||||
}
|
||||
|
||||
bool is_null() const { return j_.is_null(); }
|
||||
bool is_uint64() const { return j_.is_number_unsigned(); }
|
||||
|
||||
// Accessors (throw on type mismatch, matching json_spirit semantics)
|
||||
const std::string& get_str() const {
|
||||
if (!j_.is_string())
|
||||
throw std::runtime_error("value type is not str");
|
||||
return j_.get_ref<const std::string&>();
|
||||
}
|
||||
|
||||
bool get_bool() const {
|
||||
if (!j_.is_boolean())
|
||||
throw std::runtime_error("value type is not bool");
|
||||
return j_.get<bool>();
|
||||
}
|
||||
|
||||
int get_int() const {
|
||||
if (!j_.is_number_integer())
|
||||
throw std::runtime_error("value type is not int");
|
||||
return j_.get<int>();
|
||||
}
|
||||
|
||||
int64_t get_int64() const {
|
||||
if (!j_.is_number_integer())
|
||||
throw std::runtime_error("value type is not int");
|
||||
return j_.get<int64_t>();
|
||||
}
|
||||
|
||||
uint64_t get_uint64() const {
|
||||
if (!j_.is_number_integer())
|
||||
throw std::runtime_error("value type is not int");
|
||||
return j_.get<uint64_t>();
|
||||
}
|
||||
|
||||
double get_real() const {
|
||||
// json_spirit converts ints to double in get_real()
|
||||
if (!j_.is_number())
|
||||
throw std::runtime_error("value type is not numeric");
|
||||
return j_.get<double>();
|
||||
}
|
||||
|
||||
// Compound accessors (defined after Pair/Object/Array)
|
||||
inline std::vector<Pair> get_obj() const;
|
||||
inline std::vector<Value> get_array() const;
|
||||
|
||||
// Map-based object accessor for mValue compatibility
|
||||
inline std::map<std::string, Value> get_map_obj() const;
|
||||
|
||||
// Template accessor (used by ConvertTo<T>)
|
||||
template<typename T> T get_value() const;
|
||||
|
||||
// Static null constant (matches json_spirit::Value::null)
|
||||
static const Value null;
|
||||
|
||||
bool operator==(const Value& o) const { return j_ == o.j_; }
|
||||
bool operator!=(const Value& o) const { return j_ != o.j_; }
|
||||
};
|
||||
|
||||
// Define the static member
|
||||
inline const Value Value::null{};
|
||||
|
||||
// ---------- Pair ----------
|
||||
|
||||
struct Pair {
|
||||
std::string name_;
|
||||
Value value_;
|
||||
|
||||
Pair() {}
|
||||
Pair(const std::string& name, const Value& value)
|
||||
: name_(name), value_(value) {}
|
||||
|
||||
bool operator==(const Pair& o) const { return name_ == o.name_; }
|
||||
};
|
||||
|
||||
// ---------- Object / Array typedefs ----------
|
||||
|
||||
typedef std::vector<Pair> Object;
|
||||
typedef std::vector<Value> Array;
|
||||
|
||||
// ---------- Map-based aliases (mValue/mObject for messagemodel.cpp compat) ----------
|
||||
|
||||
typedef Value mValue;
|
||||
typedef std::map<std::string, Value> mObject;
|
||||
|
||||
// ---------- Value compound constructors ----------
|
||||
|
||||
inline Value::Value(const std::vector<Pair>& obj) {
|
||||
j_ = json_internal::object();
|
||||
for (const auto& p : obj)
|
||||
j_[p.name_] = p.value_.j_;
|
||||
}
|
||||
|
||||
inline Value::Value(const std::vector<Value>& arr) {
|
||||
j_ = json_internal::array();
|
||||
for (const auto& v : arr)
|
||||
j_.push_back(v.j_);
|
||||
}
|
||||
|
||||
// ---------- Value compound accessors ----------
|
||||
|
||||
inline std::vector<Pair> Value::get_obj() const {
|
||||
if (!j_.is_object())
|
||||
throw std::runtime_error("value type is not obj");
|
||||
std::vector<Pair> result;
|
||||
result.reserve(j_.size());
|
||||
for (auto it = j_.begin(); it != j_.end(); ++it) {
|
||||
Value v;
|
||||
v.j_ = it.value();
|
||||
result.emplace_back(it.key(), v);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
inline std::vector<Value> Value::get_array() const {
|
||||
if (!j_.is_array())
|
||||
throw std::runtime_error("value type is not array");
|
||||
std::vector<Value> result;
|
||||
result.reserve(j_.size());
|
||||
for (const auto& elem : j_) {
|
||||
Value v;
|
||||
v.j_ = elem;
|
||||
result.push_back(v);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
inline std::map<std::string, Value> Value::get_map_obj() const {
|
||||
if (!j_.is_object())
|
||||
throw std::runtime_error("value type is not obj");
|
||||
std::map<std::string, Value> result;
|
||||
for (auto it = j_.begin(); it != j_.end(); ++it) {
|
||||
Value v;
|
||||
v.j_ = it.value();
|
||||
result[it.key()] = v;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// ---------- Template get_value specializations ----------
|
||||
|
||||
template<> inline std::string Value::get_value<std::string>() const { return get_str(); }
|
||||
template<> inline bool Value::get_value<bool>() const { return get_bool(); }
|
||||
template<> inline int Value::get_value<int>() const { return get_int(); }
|
||||
template<> inline int64_t Value::get_value<int64_t>() const { return get_int64(); }
|
||||
template<> inline uint64_t Value::get_value<uint64_t>() const { return get_uint64(); }
|
||||
template<> inline double Value::get_value<double>() const { return get_real(); }
|
||||
template<> inline Object Value::get_value<Object>() const { return get_obj(); }
|
||||
template<> inline Array Value::get_value<Array>() const { return get_array(); }
|
||||
|
||||
// ---------- Utility functions ----------
|
||||
|
||||
inline const Value& find_value(const Object& obj, const std::string& name) {
|
||||
for (const auto& p : obj)
|
||||
if (p.name_ == name)
|
||||
return p.value_;
|
||||
static const Value null_value;
|
||||
return null_value;
|
||||
}
|
||||
|
||||
inline std::string write_string(const Value& val, bool pretty = false) {
|
||||
return val.j_.dump(pretty ? 4 : -1);
|
||||
}
|
||||
|
||||
inline bool read_string(const std::string& s, Value& val) {
|
||||
try {
|
||||
val.j_ = json_internal::parse(s);
|
||||
return true;
|
||||
} catch (const nlohmann::detail::parse_error&) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Alias matching json_spirit::read() signature (used by messagemodel.cpp)
|
||||
inline bool read(const std::string& s, Value& val) {
|
||||
return read_string(s, val);
|
||||
}
|
||||
|
||||
// Stream-based reader (used by test harness read_json)
|
||||
template<typename Istream>
|
||||
inline bool read_stream(Istream& is, Value& val) {
|
||||
std::string s((std::istreambuf_iterator<char>(is)),
|
||||
std::istreambuf_iterator<char>());
|
||||
return read_string(s, val);
|
||||
}
|
||||
|
||||
} // namespace json_spirit
|
||||
|
||||
#endif // JSON_COMPAT_H
|
||||
@@ -1,18 +0,0 @@
|
||||
#ifndef JSON_SPIRIT
|
||||
#define JSON_SPIRIT
|
||||
|
||||
// Copyright John W. Wilkinson 2007 - 2009.
|
||||
// Distributed under the MIT License, see accompanying file LICENSE.txt
|
||||
|
||||
// json spirit version 4.03
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
|
||||
# pragma once
|
||||
#endif
|
||||
|
||||
#include "json_spirit_value.h"
|
||||
#include "json_spirit_reader.h"
|
||||
#include "json_spirit_writer.h"
|
||||
#include "json_spirit_utils.h"
|
||||
|
||||
#endif
|
||||
@@ -1,54 +0,0 @@
|
||||
#ifndef JSON_SPIRIT_ERROR_POSITION
|
||||
#define JSON_SPIRIT_ERROR_POSITION
|
||||
|
||||
// Copyright John W. Wilkinson 2007 - 2009.
|
||||
// Distributed under the MIT License, see accompanying file LICENSE.txt
|
||||
|
||||
// json spirit version 4.03
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
|
||||
# pragma once
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace json_spirit
|
||||
{
|
||||
// An Error_position exception is thrown by the "read_or_throw" functions below on finding an error.
|
||||
// Note the "read_or_throw" functions are around 3 times slower than the standard functions "read"
|
||||
// functions that return a bool.
|
||||
//
|
||||
struct Error_position
|
||||
{
|
||||
Error_position();
|
||||
Error_position( unsigned int line, unsigned int column, const std::string& reason );
|
||||
bool operator==( const Error_position& lhs ) const;
|
||||
unsigned int line_;
|
||||
unsigned int column_;
|
||||
std::string reason_;
|
||||
};
|
||||
|
||||
inline Error_position::Error_position()
|
||||
: line_( 0 )
|
||||
, column_( 0 )
|
||||
{
|
||||
}
|
||||
|
||||
inline Error_position::Error_position( unsigned int line, unsigned int column, const std::string& reason )
|
||||
: line_( line )
|
||||
, column_( column )
|
||||
, reason_( reason )
|
||||
{
|
||||
}
|
||||
|
||||
inline bool Error_position::operator==( const Error_position& lhs ) const
|
||||
{
|
||||
if( this == &lhs ) return true;
|
||||
|
||||
return ( reason_ == lhs.reason_ ) &&
|
||||
( line_ == lhs.line_ ) &&
|
||||
( column_ == lhs.column_ );
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,137 +0,0 @@
|
||||
// Copyright John W. Wilkinson 2007 - 2009.
|
||||
// Distributed under the MIT License, see accompanying file LICENSE.txt
|
||||
|
||||
// json spirit version 4.03
|
||||
|
||||
#include "json_spirit_reader.h"
|
||||
#include "json_spirit_reader_template.h"
|
||||
|
||||
using namespace json_spirit;
|
||||
|
||||
bool json_spirit::read( const std::string& s, Value& value )
|
||||
{
|
||||
return read_string( s, value );
|
||||
}
|
||||
|
||||
void json_spirit::read_or_throw( const std::string& s, Value& value )
|
||||
{
|
||||
read_string_or_throw( s, value );
|
||||
}
|
||||
|
||||
bool json_spirit::read( std::istream& is, Value& value )
|
||||
{
|
||||
return read_stream( is, value );
|
||||
}
|
||||
|
||||
void json_spirit::read_or_throw( std::istream& is, Value& value )
|
||||
{
|
||||
read_stream_or_throw( is, value );
|
||||
}
|
||||
|
||||
bool json_spirit::read( std::string::const_iterator& begin, std::string::const_iterator end, Value& value )
|
||||
{
|
||||
return read_range( begin, end, value );
|
||||
}
|
||||
|
||||
void json_spirit::read_or_throw( std::string::const_iterator& begin, std::string::const_iterator end, Value& value )
|
||||
{
|
||||
begin = read_range_or_throw( begin, end, value );
|
||||
}
|
||||
|
||||
#ifndef BOOST_NO_STD_WSTRING
|
||||
|
||||
bool json_spirit::read( const std::wstring& s, wValue& value )
|
||||
{
|
||||
return read_string( s, value );
|
||||
}
|
||||
|
||||
void json_spirit::read_or_throw( const std::wstring& s, wValue& value )
|
||||
{
|
||||
read_string_or_throw( s, value );
|
||||
}
|
||||
|
||||
bool json_spirit::read( std::wistream& is, wValue& value )
|
||||
{
|
||||
return read_stream( is, value );
|
||||
}
|
||||
|
||||
void json_spirit::read_or_throw( std::wistream& is, wValue& value )
|
||||
{
|
||||
read_stream_or_throw( is, value );
|
||||
}
|
||||
|
||||
bool json_spirit::read( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wValue& value )
|
||||
{
|
||||
return read_range( begin, end, value );
|
||||
}
|
||||
|
||||
void json_spirit::read_or_throw( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wValue& value )
|
||||
{
|
||||
begin = read_range_or_throw( begin, end, value );
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
bool json_spirit::read( const std::string& s, mValue& value )
|
||||
{
|
||||
return read_string( s, value );
|
||||
}
|
||||
|
||||
void json_spirit::read_or_throw( const std::string& s, mValue& value )
|
||||
{
|
||||
read_string_or_throw( s, value );
|
||||
}
|
||||
|
||||
bool json_spirit::read( std::istream& is, mValue& value )
|
||||
{
|
||||
return read_stream( is, value );
|
||||
}
|
||||
|
||||
void json_spirit::read_or_throw( std::istream& is, mValue& value )
|
||||
{
|
||||
read_stream_or_throw( is, value );
|
||||
}
|
||||
|
||||
bool json_spirit::read( std::string::const_iterator& begin, std::string::const_iterator end, mValue& value )
|
||||
{
|
||||
return read_range( begin, end, value );
|
||||
}
|
||||
|
||||
void json_spirit::read_or_throw( std::string::const_iterator& begin, std::string::const_iterator end, mValue& value )
|
||||
{
|
||||
begin = read_range_or_throw( begin, end, value );
|
||||
}
|
||||
|
||||
#ifndef BOOST_NO_STD_WSTRING
|
||||
|
||||
bool json_spirit::read( const std::wstring& s, wmValue& value )
|
||||
{
|
||||
return read_string( s, value );
|
||||
}
|
||||
|
||||
void json_spirit::read_or_throw( const std::wstring& s, wmValue& value )
|
||||
{
|
||||
read_string_or_throw( s, value );
|
||||
}
|
||||
|
||||
bool json_spirit::read( std::wistream& is, wmValue& value )
|
||||
{
|
||||
return read_stream( is, value );
|
||||
}
|
||||
|
||||
void json_spirit::read_or_throw( std::wistream& is, wmValue& value )
|
||||
{
|
||||
read_stream_or_throw( is, value );
|
||||
}
|
||||
|
||||
bool json_spirit::read( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wmValue& value )
|
||||
{
|
||||
return read_range( begin, end, value );
|
||||
}
|
||||
|
||||
void json_spirit::read_or_throw( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wmValue& value )
|
||||
{
|
||||
begin = read_range_or_throw( begin, end, value );
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,62 +0,0 @@
|
||||
#ifndef JSON_SPIRIT_READER
|
||||
#define JSON_SPIRIT_READER
|
||||
|
||||
// Copyright John W. Wilkinson 2007 - 2009.
|
||||
// Distributed under the MIT License, see accompanying file LICENSE.txt
|
||||
|
||||
// json spirit version 4.03
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
|
||||
# pragma once
|
||||
#endif
|
||||
|
||||
#include "json_spirit_value.h"
|
||||
#include "json_spirit_error_position.h"
|
||||
#include <iostream>
|
||||
|
||||
namespace json_spirit
|
||||
{
|
||||
// functions to reads a JSON values
|
||||
|
||||
bool read( const std::string& s, Value& value );
|
||||
bool read( std::istream& is, Value& value );
|
||||
bool read( std::string::const_iterator& begin, std::string::const_iterator end, Value& value );
|
||||
|
||||
void read_or_throw( const std::string& s, Value& value );
|
||||
void read_or_throw( std::istream& is, Value& value );
|
||||
void read_or_throw( std::string::const_iterator& begin, std::string::const_iterator end, Value& value );
|
||||
|
||||
#ifndef BOOST_NO_STD_WSTRING
|
||||
|
||||
bool read( const std::wstring& s, wValue& value );
|
||||
bool read( std::wistream& is, wValue& value );
|
||||
bool read( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wValue& value );
|
||||
|
||||
void read_or_throw( const std::wstring& s, wValue& value );
|
||||
void read_or_throw( std::wistream& is, wValue& value );
|
||||
void read_or_throw( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wValue& value );
|
||||
|
||||
#endif
|
||||
|
||||
bool read( const std::string& s, mValue& value );
|
||||
bool read( std::istream& is, mValue& value );
|
||||
bool read( std::string::const_iterator& begin, std::string::const_iterator end, mValue& value );
|
||||
|
||||
void read_or_throw( const std::string& s, mValue& value );
|
||||
void read_or_throw( std::istream& is, mValue& value );
|
||||
void read_or_throw( std::string::const_iterator& begin, std::string::const_iterator end, mValue& value );
|
||||
|
||||
#ifndef BOOST_NO_STD_WSTRING
|
||||
|
||||
bool read( const std::wstring& s, wmValue& value );
|
||||
bool read( std::wistream& is, wmValue& value );
|
||||
bool read( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wmValue& value );
|
||||
|
||||
void read_or_throw( const std::wstring& s, wmValue& value );
|
||||
void read_or_throw( std::wistream& is, wmValue& value );
|
||||
void read_or_throw( std::wstring::const_iterator& begin, std::wstring::const_iterator end, wmValue& value );
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,612 +0,0 @@
|
||||
#ifndef JSON_SPIRIT_READER_TEMPLATE
|
||||
#define JSON_SPIRIT_READER_TEMPLATE
|
||||
|
||||
// Copyright John W. Wilkinson 2007 - 2009.
|
||||
// Distributed under the MIT License, see accompanying file LICENSE.txt
|
||||
|
||||
// json spirit version 4.03
|
||||
|
||||
#include "json_spirit_value.h"
|
||||
#include "json_spirit_error_position.h"
|
||||
|
||||
//#define BOOST_SPIRIT_THREADSAFE // uncomment for multithreaded use, requires linking to boost.thread
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/function.hpp>
|
||||
#include <boost/version.hpp>
|
||||
|
||||
#if BOOST_VERSION >= 103800
|
||||
#include <boost/spirit/include/classic_core.hpp>
|
||||
#include <boost/spirit/include/classic_confix.hpp>
|
||||
#include <boost/spirit/include/classic_escape_char.hpp>
|
||||
#include <boost/spirit/include/classic_multi_pass.hpp>
|
||||
#include <boost/spirit/include/classic_position_iterator.hpp>
|
||||
#define spirit_namespace boost::spirit::classic
|
||||
#else
|
||||
#include <boost/spirit/core.hpp>
|
||||
#include <boost/spirit/utility/confix.hpp>
|
||||
#include <boost/spirit/utility/escape_char.hpp>
|
||||
#include <boost/spirit/iterator/multi_pass.hpp>
|
||||
#include <boost/spirit/iterator/position_iterator.hpp>
|
||||
#define spirit_namespace boost::spirit
|
||||
#endif
|
||||
|
||||
namespace json_spirit
|
||||
{
|
||||
const spirit_namespace::int_parser < boost::int64_t > int64_p = spirit_namespace::int_parser < boost::int64_t >();
|
||||
const spirit_namespace::uint_parser< boost::uint64_t > uint64_p = spirit_namespace::uint_parser< boost::uint64_t >();
|
||||
|
||||
template< class Iter_type >
|
||||
bool is_eq( Iter_type first, Iter_type last, const char* c_str )
|
||||
{
|
||||
for( Iter_type i = first; i != last; ++i, ++c_str )
|
||||
{
|
||||
if( *c_str == 0 ) return false;
|
||||
|
||||
if( *i != *c_str ) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template< class Char_type >
|
||||
Char_type hex_to_num( const Char_type c )
|
||||
{
|
||||
if( ( c >= '0' ) && ( c <= '9' ) ) return c - '0';
|
||||
if( ( c >= 'a' ) && ( c <= 'f' ) ) return c - 'a' + 10;
|
||||
if( ( c >= 'A' ) && ( c <= 'F' ) ) return c - 'A' + 10;
|
||||
return 0;
|
||||
}
|
||||
|
||||
template< class Char_type, class Iter_type >
|
||||
Char_type hex_str_to_char( Iter_type& begin )
|
||||
{
|
||||
const Char_type c1( *( ++begin ) );
|
||||
const Char_type c2( *( ++begin ) );
|
||||
|
||||
return ( hex_to_num( c1 ) << 4 ) + hex_to_num( c2 );
|
||||
}
|
||||
|
||||
template< class Char_type, class Iter_type >
|
||||
Char_type unicode_str_to_char( Iter_type& begin )
|
||||
{
|
||||
const Char_type c1( *( ++begin ) );
|
||||
const Char_type c2( *( ++begin ) );
|
||||
const Char_type c3( *( ++begin ) );
|
||||
const Char_type c4( *( ++begin ) );
|
||||
|
||||
return ( hex_to_num( c1 ) << 12 ) +
|
||||
( hex_to_num( c2 ) << 8 ) +
|
||||
( hex_to_num( c3 ) << 4 ) +
|
||||
hex_to_num( c4 );
|
||||
}
|
||||
|
||||
template< class String_type >
|
||||
void append_esc_char_and_incr_iter( String_type& s,
|
||||
typename String_type::const_iterator& begin,
|
||||
typename String_type::const_iterator end )
|
||||
{
|
||||
typedef typename String_type::value_type Char_type;
|
||||
|
||||
const Char_type c2( *begin );
|
||||
|
||||
switch( c2 )
|
||||
{
|
||||
case 't': s += '\t'; break;
|
||||
case 'b': s += '\b'; break;
|
||||
case 'f': s += '\f'; break;
|
||||
case 'n': s += '\n'; break;
|
||||
case 'r': s += '\r'; break;
|
||||
case '\\': s += '\\'; break;
|
||||
case '/': s += '/'; break;
|
||||
case '"': s += '"'; break;
|
||||
case 'x':
|
||||
{
|
||||
if( end - begin >= 3 ) // expecting "xHH..."
|
||||
{
|
||||
s += hex_str_to_char< Char_type >( begin );
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'u':
|
||||
{
|
||||
if( end - begin >= 5 ) // expecting "uHHHH..."
|
||||
{
|
||||
s += unicode_str_to_char< Char_type >( begin );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template< class String_type >
|
||||
String_type substitute_esc_chars( typename String_type::const_iterator begin,
|
||||
typename String_type::const_iterator end )
|
||||
{
|
||||
typedef typename String_type::const_iterator Iter_type;
|
||||
|
||||
if( end - begin < 2 ) return String_type( begin, end );
|
||||
|
||||
String_type result;
|
||||
|
||||
result.reserve( end - begin );
|
||||
|
||||
const Iter_type end_minus_1( end - 1 );
|
||||
|
||||
Iter_type substr_start = begin;
|
||||
Iter_type i = begin;
|
||||
|
||||
for( ; i < end_minus_1; ++i )
|
||||
{
|
||||
if( *i == '\\' )
|
||||
{
|
||||
result.append( substr_start, i );
|
||||
|
||||
++i; // skip the '\'
|
||||
|
||||
append_esc_char_and_incr_iter( result, i, end );
|
||||
|
||||
substr_start = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
result.append( substr_start, end );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template< class String_type >
|
||||
String_type get_str_( typename String_type::const_iterator begin,
|
||||
typename String_type::const_iterator end )
|
||||
{
|
||||
assert( end - begin >= 2 );
|
||||
|
||||
typedef typename String_type::const_iterator Iter_type;
|
||||
|
||||
Iter_type str_without_quotes( ++begin );
|
||||
Iter_type end_without_quotes( --end );
|
||||
|
||||
return substitute_esc_chars< String_type >( str_without_quotes, end_without_quotes );
|
||||
}
|
||||
|
||||
inline std::string get_str( std::string::const_iterator begin, std::string::const_iterator end )
|
||||
{
|
||||
return get_str_< std::string >( begin, end );
|
||||
}
|
||||
|
||||
inline std::wstring get_str( std::wstring::const_iterator begin, std::wstring::const_iterator end )
|
||||
{
|
||||
return get_str_< std::wstring >( begin, end );
|
||||
}
|
||||
|
||||
template< class String_type, class Iter_type >
|
||||
String_type get_str( Iter_type begin, Iter_type end )
|
||||
{
|
||||
const String_type tmp( begin, end ); // convert multipass iterators to string iterators
|
||||
|
||||
return get_str( tmp.begin(), tmp.end() );
|
||||
}
|
||||
|
||||
// this class's methods get called by the spirit parse resulting
|
||||
// in the creation of a JSON object or array
|
||||
//
|
||||
// NB Iter_type could be a std::string iterator, wstring iterator, a position iterator or a multipass iterator
|
||||
//
|
||||
template< class Value_type, class Iter_type >
|
||||
class Semantic_actions
|
||||
{
|
||||
public:
|
||||
|
||||
typedef typename Value_type::Config_type Config_type;
|
||||
typedef typename Config_type::String_type String_type;
|
||||
typedef typename Config_type::Object_type Object_type;
|
||||
typedef typename Config_type::Array_type Array_type;
|
||||
typedef typename String_type::value_type Char_type;
|
||||
|
||||
Semantic_actions( Value_type& value )
|
||||
: value_( value )
|
||||
, current_p_( 0 )
|
||||
{
|
||||
}
|
||||
|
||||
void begin_obj( Char_type c )
|
||||
{
|
||||
assert( c == '{' );
|
||||
|
||||
begin_compound< Object_type >();
|
||||
}
|
||||
|
||||
void end_obj( Char_type c )
|
||||
{
|
||||
assert( c == '}' );
|
||||
|
||||
end_compound();
|
||||
}
|
||||
|
||||
void begin_array( Char_type c )
|
||||
{
|
||||
assert( c == '[' );
|
||||
|
||||
begin_compound< Array_type >();
|
||||
}
|
||||
|
||||
void end_array( Char_type c )
|
||||
{
|
||||
assert( c == ']' );
|
||||
|
||||
end_compound();
|
||||
}
|
||||
|
||||
void new_name( Iter_type begin, Iter_type end )
|
||||
{
|
||||
assert( current_p_->type() == obj_type );
|
||||
|
||||
name_ = get_str< String_type >( begin, end );
|
||||
}
|
||||
|
||||
void new_str( Iter_type begin, Iter_type end )
|
||||
{
|
||||
add_to_current( get_str< String_type >( begin, end ) );
|
||||
}
|
||||
|
||||
void new_true( Iter_type begin, Iter_type end )
|
||||
{
|
||||
assert( is_eq( begin, end, "true" ) );
|
||||
|
||||
add_to_current( true );
|
||||
}
|
||||
|
||||
void new_false( Iter_type begin, Iter_type end )
|
||||
{
|
||||
assert( is_eq( begin, end, "false" ) );
|
||||
|
||||
add_to_current( false );
|
||||
}
|
||||
|
||||
void new_null( Iter_type begin, Iter_type end )
|
||||
{
|
||||
assert( is_eq( begin, end, "null" ) );
|
||||
|
||||
add_to_current( Value_type() );
|
||||
}
|
||||
|
||||
void new_int( boost::int64_t i )
|
||||
{
|
||||
add_to_current( i );
|
||||
}
|
||||
|
||||
void new_uint64( boost::uint64_t ui )
|
||||
{
|
||||
add_to_current( ui );
|
||||
}
|
||||
|
||||
void new_real( double d )
|
||||
{
|
||||
add_to_current( d );
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
Semantic_actions& operator=( const Semantic_actions& );
|
||||
// to prevent "assignment operator could not be generated" warning
|
||||
|
||||
Value_type* add_first( const Value_type& value )
|
||||
{
|
||||
assert( current_p_ == 0 );
|
||||
|
||||
value_ = value;
|
||||
current_p_ = &value_;
|
||||
return current_p_;
|
||||
}
|
||||
|
||||
template< class Array_or_obj >
|
||||
void begin_compound()
|
||||
{
|
||||
if( current_p_ == 0 )
|
||||
{
|
||||
add_first( Array_or_obj() );
|
||||
}
|
||||
else
|
||||
{
|
||||
stack_.push_back( current_p_ );
|
||||
|
||||
Array_or_obj new_array_or_obj; // avoid copy by building new array or object in place
|
||||
|
||||
current_p_ = add_to_current( new_array_or_obj );
|
||||
}
|
||||
}
|
||||
|
||||
void end_compound()
|
||||
{
|
||||
if( current_p_ != &value_ )
|
||||
{
|
||||
current_p_ = stack_.back();
|
||||
|
||||
stack_.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
Value_type* add_to_current( const Value_type& value )
|
||||
{
|
||||
if( current_p_ == 0 )
|
||||
{
|
||||
return add_first( value );
|
||||
}
|
||||
else if( current_p_->type() == array_type )
|
||||
{
|
||||
current_p_->get_array().push_back( value );
|
||||
|
||||
return ¤t_p_->get_array().back();
|
||||
}
|
||||
|
||||
assert( current_p_->type() == obj_type );
|
||||
|
||||
return &Config_type::add( current_p_->get_obj(), name_, value );
|
||||
}
|
||||
|
||||
Value_type& value_; // this is the object or array that is being created
|
||||
Value_type* current_p_; // the child object or array that is currently being constructed
|
||||
|
||||
std::vector< Value_type* > stack_; // previous child objects and arrays
|
||||
|
||||
String_type name_; // of current name/value pair
|
||||
};
|
||||
|
||||
template< typename Iter_type >
|
||||
void throw_error( spirit_namespace::position_iterator< Iter_type > i, const std::string& reason )
|
||||
{
|
||||
throw Error_position( i.get_position().line, i.get_position().column, reason );
|
||||
}
|
||||
|
||||
template< typename Iter_type >
|
||||
void throw_error( Iter_type i, const std::string& reason )
|
||||
{
|
||||
throw reason;
|
||||
}
|
||||
|
||||
// the spirit grammer
|
||||
//
|
||||
template< class Value_type, class Iter_type >
|
||||
class Json_grammer : public spirit_namespace::grammar< Json_grammer< Value_type, Iter_type > >
|
||||
{
|
||||
public:
|
||||
|
||||
typedef Semantic_actions< Value_type, Iter_type > Semantic_actions_t;
|
||||
|
||||
Json_grammer( Semantic_actions_t& semantic_actions )
|
||||
: actions_( semantic_actions )
|
||||
{
|
||||
}
|
||||
|
||||
static void throw_not_value( Iter_type begin, Iter_type end )
|
||||
{
|
||||
throw_error( begin, "not a value" );
|
||||
}
|
||||
|
||||
static void throw_not_array( Iter_type begin, Iter_type end )
|
||||
{
|
||||
throw_error( begin, "not an array" );
|
||||
}
|
||||
|
||||
static void throw_not_object( Iter_type begin, Iter_type end )
|
||||
{
|
||||
throw_error( begin, "not an object" );
|
||||
}
|
||||
|
||||
static void throw_not_pair( Iter_type begin, Iter_type end )
|
||||
{
|
||||
throw_error( begin, "not a pair" );
|
||||
}
|
||||
|
||||
static void throw_not_colon( Iter_type begin, Iter_type end )
|
||||
{
|
||||
throw_error( begin, "no colon in pair" );
|
||||
}
|
||||
|
||||
static void throw_not_string( Iter_type begin, Iter_type end )
|
||||
{
|
||||
throw_error( begin, "not a string" );
|
||||
}
|
||||
|
||||
template< typename ScannerT >
|
||||
class definition
|
||||
{
|
||||
public:
|
||||
|
||||
definition( const Json_grammer& self )
|
||||
{
|
||||
using namespace spirit_namespace;
|
||||
|
||||
typedef typename Value_type::String_type::value_type Char_type;
|
||||
|
||||
// first we convert the semantic action class methods to functors with the
|
||||
// parameter signature expected by spirit
|
||||
|
||||
typedef boost::function< void( Char_type ) > Char_action;
|
||||
typedef boost::function< void( Iter_type, Iter_type ) > Str_action;
|
||||
typedef boost::function< void( double ) > Real_action;
|
||||
typedef boost::function< void( boost::int64_t ) > Int_action;
|
||||
typedef boost::function< void( boost::uint64_t ) > Uint64_action;
|
||||
|
||||
Char_action begin_obj ( boost::bind( &Semantic_actions_t::begin_obj, &self.actions_, _1 ) );
|
||||
Char_action end_obj ( boost::bind( &Semantic_actions_t::end_obj, &self.actions_, _1 ) );
|
||||
Char_action begin_array( boost::bind( &Semantic_actions_t::begin_array, &self.actions_, _1 ) );
|
||||
Char_action end_array ( boost::bind( &Semantic_actions_t::end_array, &self.actions_, _1 ) );
|
||||
Str_action new_name ( boost::bind( &Semantic_actions_t::new_name, &self.actions_, _1, _2 ) );
|
||||
Str_action new_str ( boost::bind( &Semantic_actions_t::new_str, &self.actions_, _1, _2 ) );
|
||||
Str_action new_true ( boost::bind( &Semantic_actions_t::new_true, &self.actions_, _1, _2 ) );
|
||||
Str_action new_false ( boost::bind( &Semantic_actions_t::new_false, &self.actions_, _1, _2 ) );
|
||||
Str_action new_null ( boost::bind( &Semantic_actions_t::new_null, &self.actions_, _1, _2 ) );
|
||||
Real_action new_real ( boost::bind( &Semantic_actions_t::new_real, &self.actions_, _1 ) );
|
||||
Int_action new_int ( boost::bind( &Semantic_actions_t::new_int, &self.actions_, _1 ) );
|
||||
Uint64_action new_uint64 ( boost::bind( &Semantic_actions_t::new_uint64, &self.actions_, _1 ) );
|
||||
|
||||
// actual grammer
|
||||
|
||||
json_
|
||||
= value_ | eps_p[ &throw_not_value ]
|
||||
;
|
||||
|
||||
value_
|
||||
= string_[ new_str ]
|
||||
| number_
|
||||
| object_
|
||||
| array_
|
||||
| str_p( "true" ) [ new_true ]
|
||||
| str_p( "false" )[ new_false ]
|
||||
| str_p( "null" ) [ new_null ]
|
||||
;
|
||||
|
||||
object_
|
||||
= ch_p('{')[ begin_obj ]
|
||||
>> !members_
|
||||
>> ( ch_p('}')[ end_obj ] | eps_p[ &throw_not_object ] )
|
||||
;
|
||||
|
||||
members_
|
||||
= pair_ >> *( ',' >> pair_ )
|
||||
;
|
||||
|
||||
pair_
|
||||
= string_[ new_name ]
|
||||
>> ( ':' | eps_p[ &throw_not_colon ] )
|
||||
>> ( value_ | eps_p[ &throw_not_value ] )
|
||||
;
|
||||
|
||||
array_
|
||||
= ch_p('[')[ begin_array ]
|
||||
>> !elements_
|
||||
>> ( ch_p(']')[ end_array ] | eps_p[ &throw_not_array ] )
|
||||
;
|
||||
|
||||
elements_
|
||||
= value_ >> *( ',' >> value_ )
|
||||
;
|
||||
|
||||
string_
|
||||
= lexeme_d // this causes white space inside a string to be retained
|
||||
[
|
||||
confix_p
|
||||
(
|
||||
'"',
|
||||
*lex_escape_ch_p,
|
||||
'"'
|
||||
)
|
||||
]
|
||||
;
|
||||
|
||||
number_
|
||||
= strict_real_p[ new_real ]
|
||||
| int64_p [ new_int ]
|
||||
| uint64_p [ new_uint64 ]
|
||||
;
|
||||
}
|
||||
|
||||
spirit_namespace::rule< ScannerT > json_, object_, members_, pair_, array_, elements_, value_, string_, number_;
|
||||
|
||||
const spirit_namespace::rule< ScannerT >& start() const { return json_; }
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
Json_grammer& operator=( const Json_grammer& ); // to prevent "assignment operator could not be generated" warning
|
||||
|
||||
Semantic_actions_t& actions_;
|
||||
};
|
||||
|
||||
template< class Iter_type, class Value_type >
|
||||
Iter_type read_range_or_throw( Iter_type begin, Iter_type end, Value_type& value )
|
||||
{
|
||||
Semantic_actions< Value_type, Iter_type > semantic_actions( value );
|
||||
|
||||
const spirit_namespace::parse_info< Iter_type > info =
|
||||
spirit_namespace::parse( begin, end,
|
||||
Json_grammer< Value_type, Iter_type >( semantic_actions ),
|
||||
spirit_namespace::space_p );
|
||||
|
||||
if( !info.hit )
|
||||
{
|
||||
assert( false ); // in theory exception should already have been thrown
|
||||
throw_error( info.stop, "error" );
|
||||
}
|
||||
|
||||
return info.stop;
|
||||
}
|
||||
|
||||
template< class Iter_type, class Value_type >
|
||||
void add_posn_iter_and_read_range_or_throw( Iter_type begin, Iter_type end, Value_type& value )
|
||||
{
|
||||
typedef spirit_namespace::position_iterator< Iter_type > Posn_iter_t;
|
||||
|
||||
const Posn_iter_t posn_begin( begin, end );
|
||||
const Posn_iter_t posn_end( end, end );
|
||||
|
||||
read_range_or_throw( posn_begin, posn_end, value );
|
||||
}
|
||||
|
||||
template< class Iter_type, class Value_type >
|
||||
bool read_range( Iter_type& begin, Iter_type end, Value_type& value )
|
||||
{
|
||||
try
|
||||
{
|
||||
begin = read_range_or_throw( begin, end, value );
|
||||
|
||||
return true;
|
||||
}
|
||||
catch( ... )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
template< class String_type, class Value_type >
|
||||
void read_string_or_throw( const String_type& s, Value_type& value )
|
||||
{
|
||||
add_posn_iter_and_read_range_or_throw( s.begin(), s.end(), value );
|
||||
}
|
||||
|
||||
template< class String_type, class Value_type >
|
||||
bool read_string( const String_type& s, Value_type& value )
|
||||
{
|
||||
typename String_type::const_iterator begin = s.begin();
|
||||
|
||||
return read_range( begin, s.end(), value );
|
||||
}
|
||||
|
||||
template< class Istream_type >
|
||||
struct Multi_pass_iters
|
||||
{
|
||||
typedef typename Istream_type::char_type Char_type;
|
||||
typedef std::istream_iterator< Char_type, Char_type > istream_iter;
|
||||
typedef spirit_namespace::multi_pass< istream_iter > Mp_iter;
|
||||
|
||||
Multi_pass_iters( Istream_type& is )
|
||||
{
|
||||
is.unsetf( std::ios::skipws );
|
||||
|
||||
begin_ = spirit_namespace::make_multi_pass( istream_iter( is ) );
|
||||
end_ = spirit_namespace::make_multi_pass( istream_iter() );
|
||||
}
|
||||
|
||||
Mp_iter begin_;
|
||||
Mp_iter end_;
|
||||
};
|
||||
|
||||
template< class Istream_type, class Value_type >
|
||||
bool read_stream( Istream_type& is, Value_type& value )
|
||||
{
|
||||
Multi_pass_iters< Istream_type > mp_iters( is );
|
||||
|
||||
return read_range( mp_iters.begin_, mp_iters.end_, value );
|
||||
}
|
||||
|
||||
template< class Istream_type, class Value_type >
|
||||
void read_stream_or_throw( Istream_type& is, Value_type& value )
|
||||
{
|
||||
const Multi_pass_iters< Istream_type > mp_iters( is );
|
||||
|
||||
add_posn_iter_and_read_range_or_throw( mp_iters.begin_, mp_iters.end_, value );
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,70 +0,0 @@
|
||||
#ifndef JSON_SPIRIT_READ_STREAM
|
||||
#define JSON_SPIRIT_READ_STREAM
|
||||
|
||||
// Copyright John W. Wilkinson 2007 - 2009.
|
||||
// Distributed under the MIT License, see accompanying file LICENSE.txt
|
||||
|
||||
// json spirit version 4.03
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
|
||||
# pragma once
|
||||
#endif
|
||||
|
||||
#include "json_spirit_reader_template.h"
|
||||
|
||||
namespace json_spirit
|
||||
{
|
||||
// these classes allows you to read multiple top level contiguous values from a stream,
|
||||
// the normal stream read functions have a bug that prevent multiple top level values
|
||||
// from being read unless they are separated by spaces
|
||||
|
||||
template< class Istream_type, class Value_type >
|
||||
class Stream_reader
|
||||
{
|
||||
public:
|
||||
|
||||
Stream_reader( Istream_type& is )
|
||||
: iters_( is )
|
||||
{
|
||||
}
|
||||
|
||||
bool read_next( Value_type& value )
|
||||
{
|
||||
return read_range( iters_.begin_, iters_.end_, value );
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
typedef Multi_pass_iters< Istream_type > Mp_iters;
|
||||
|
||||
Mp_iters iters_;
|
||||
};
|
||||
|
||||
template< class Istream_type, class Value_type >
|
||||
class Stream_reader_thrower
|
||||
{
|
||||
public:
|
||||
|
||||
Stream_reader_thrower( Istream_type& is )
|
||||
: iters_( is )
|
||||
, posn_begin_( iters_.begin_, iters_.end_ )
|
||||
, posn_end_( iters_.end_, iters_.end_ )
|
||||
{
|
||||
}
|
||||
|
||||
void read_next( Value_type& value )
|
||||
{
|
||||
posn_begin_ = read_range_or_throw( posn_begin_, posn_end_, value );
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
typedef Multi_pass_iters< Istream_type > Mp_iters;
|
||||
typedef spirit_namespace::position_iterator< typename Mp_iters::Mp_iter > Posn_iter_t;
|
||||
|
||||
Mp_iters iters_;
|
||||
Posn_iter_t posn_begin_, posn_end_;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,61 +0,0 @@
|
||||
#ifndef JSON_SPIRIT_UTILS
|
||||
#define JSON_SPIRIT_UTILS
|
||||
|
||||
// Copyright John W. Wilkinson 2007 - 2009.
|
||||
// Distributed under the MIT License, see accompanying file LICENSE.txt
|
||||
|
||||
// json spirit version 4.03
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
|
||||
# pragma once
|
||||
#endif
|
||||
|
||||
#include "json_spirit_value.h"
|
||||
#include <map>
|
||||
|
||||
namespace json_spirit
|
||||
{
|
||||
template< class Obj_t, class Map_t >
|
||||
void obj_to_map( const Obj_t& obj, Map_t& mp_obj )
|
||||
{
|
||||
mp_obj.clear();
|
||||
|
||||
for( typename Obj_t::const_iterator i = obj.begin(); i != obj.end(); ++i )
|
||||
{
|
||||
mp_obj[ i->name_ ] = i->value_;
|
||||
}
|
||||
}
|
||||
|
||||
template< class Obj_t, class Map_t >
|
||||
void map_to_obj( const Map_t& mp_obj, Obj_t& obj )
|
||||
{
|
||||
obj.clear();
|
||||
|
||||
for( typename Map_t::const_iterator i = mp_obj.begin(); i != mp_obj.end(); ++i )
|
||||
{
|
||||
obj.push_back( typename Obj_t::value_type( i->first, i->second ) );
|
||||
}
|
||||
}
|
||||
|
||||
typedef std::map< std::string, Value > Mapped_obj;
|
||||
|
||||
#ifndef BOOST_NO_STD_WSTRING
|
||||
typedef std::map< std::wstring, wValue > wMapped_obj;
|
||||
#endif
|
||||
|
||||
template< class Object_type, class String_type >
|
||||
const typename Object_type::value_type::Value_type& find_value( const Object_type& obj, const String_type& name )
|
||||
{
|
||||
for( typename Object_type::const_iterator i = obj.begin(); i != obj.end(); ++i )
|
||||
{
|
||||
if( i->name_ == name )
|
||||
{
|
||||
return i->value_;
|
||||
}
|
||||
}
|
||||
|
||||
return Object_type::value_type::Value_type::null;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,8 +0,0 @@
|
||||
/* Copyright (c) 2007 John W Wilkinson
|
||||
|
||||
This source code can be used for any purpose as long as
|
||||
this comment is retained. */
|
||||
|
||||
// json spirit version 2.00
|
||||
|
||||
#include "json_spirit_value.h"
|
||||
@@ -1,534 +0,0 @@
|
||||
#ifndef JSON_SPIRIT_VALUE
|
||||
#define JSON_SPIRIT_VALUE
|
||||
|
||||
// Copyright John W. Wilkinson 2007 - 2009.
|
||||
// Distributed under the MIT License, see accompanying file LICENSE.txt
|
||||
|
||||
// json spirit version 4.03
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
|
||||
# pragma once
|
||||
#endif
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <cassert>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/cstdint.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/variant.hpp>
|
||||
|
||||
namespace json_spirit
|
||||
{
|
||||
enum Value_type{ obj_type, array_type, str_type, bool_type, int_type, real_type, null_type };
|
||||
static const char* Value_type_name[]={"obj", "array", "str", "bool", "int", "real", "null"};
|
||||
|
||||
template< class Config > // Config determines whether the value uses std::string or std::wstring and
|
||||
// whether JSON Objects are represented as vectors or maps
|
||||
class Value_impl
|
||||
{
|
||||
public:
|
||||
|
||||
typedef Config Config_type;
|
||||
typedef typename Config::String_type String_type;
|
||||
typedef typename Config::Object_type Object;
|
||||
typedef typename Config::Array_type Array;
|
||||
typedef typename String_type::const_pointer Const_str_ptr; // eg const char*
|
||||
|
||||
Value_impl(); // creates null value
|
||||
Value_impl( Const_str_ptr value );
|
||||
Value_impl( const String_type& value );
|
||||
Value_impl( const Object& value );
|
||||
Value_impl( const Array& value );
|
||||
Value_impl( bool value );
|
||||
Value_impl( int value );
|
||||
Value_impl( boost::int64_t value );
|
||||
Value_impl( boost::uint64_t value );
|
||||
Value_impl( double value );
|
||||
|
||||
Value_impl( const Value_impl& other );
|
||||
|
||||
bool operator==( const Value_impl& lhs ) const;
|
||||
|
||||
Value_impl& operator=( const Value_impl& lhs );
|
||||
|
||||
Value_type type() const;
|
||||
|
||||
bool is_uint64() const;
|
||||
bool is_null() const;
|
||||
|
||||
const String_type& get_str() const;
|
||||
const Object& get_obj() const;
|
||||
const Array& get_array() const;
|
||||
bool get_bool() const;
|
||||
int get_int() const;
|
||||
boost::int64_t get_int64() const;
|
||||
boost::uint64_t get_uint64() const;
|
||||
double get_real() const;
|
||||
|
||||
Object& get_obj();
|
||||
Array& get_array();
|
||||
|
||||
template< typename T > T get_value() const; // example usage: int i = value.get_value< int >();
|
||||
// or double d = value.get_value< double >();
|
||||
|
||||
static const Value_impl null;
|
||||
|
||||
private:
|
||||
|
||||
void check_type( const Value_type vtype ) const;
|
||||
|
||||
typedef boost::variant< String_type,
|
||||
boost::recursive_wrapper< Object >, boost::recursive_wrapper< Array >,
|
||||
bool, boost::int64_t, double > Variant;
|
||||
|
||||
Value_type type_;
|
||||
Variant v_;
|
||||
bool is_uint64_;
|
||||
};
|
||||
|
||||
// vector objects
|
||||
|
||||
template< class Config >
|
||||
struct Pair_impl
|
||||
{
|
||||
typedef typename Config::String_type String_type;
|
||||
typedef typename Config::Value_type Value_type;
|
||||
|
||||
Pair_impl( const String_type& name, const Value_type& value );
|
||||
|
||||
bool operator==( const Pair_impl& lhs ) const;
|
||||
|
||||
String_type name_;
|
||||
Value_type value_;
|
||||
};
|
||||
|
||||
template< class String >
|
||||
struct Config_vector
|
||||
{
|
||||
typedef String String_type;
|
||||
typedef Value_impl< Config_vector > Value_type;
|
||||
typedef Pair_impl < Config_vector > Pair_type;
|
||||
typedef std::vector< Value_type > Array_type;
|
||||
typedef std::vector< Pair_type > Object_type;
|
||||
|
||||
static Value_type& add( Object_type& obj, const String_type& name, const Value_type& value )
|
||||
{
|
||||
obj.push_back( Pair_type( name , value ) );
|
||||
|
||||
return obj.back().value_;
|
||||
}
|
||||
|
||||
static String_type get_name( const Pair_type& pair )
|
||||
{
|
||||
return pair.name_;
|
||||
}
|
||||
|
||||
static Value_type get_value( const Pair_type& pair )
|
||||
{
|
||||
return pair.value_;
|
||||
}
|
||||
};
|
||||
|
||||
// typedefs for ASCII
|
||||
|
||||
typedef Config_vector< std::string > Config;
|
||||
|
||||
typedef Config::Value_type Value;
|
||||
typedef Config::Pair_type Pair;
|
||||
typedef Config::Object_type Object;
|
||||
typedef Config::Array_type Array;
|
||||
|
||||
// typedefs for Unicode
|
||||
|
||||
#ifndef BOOST_NO_STD_WSTRING
|
||||
|
||||
typedef Config_vector< std::wstring > wConfig;
|
||||
|
||||
typedef wConfig::Value_type wValue;
|
||||
typedef wConfig::Pair_type wPair;
|
||||
typedef wConfig::Object_type wObject;
|
||||
typedef wConfig::Array_type wArray;
|
||||
#endif
|
||||
|
||||
// map objects
|
||||
|
||||
template< class String >
|
||||
struct Config_map
|
||||
{
|
||||
typedef String String_type;
|
||||
typedef Value_impl< Config_map > Value_type;
|
||||
typedef std::vector< Value_type > Array_type;
|
||||
typedef std::map< String_type, Value_type > Object_type;
|
||||
typedef typename Object_type::value_type Pair_type;
|
||||
|
||||
static Value_type& add( Object_type& obj, const String_type& name, const Value_type& value )
|
||||
{
|
||||
return obj[ name ] = value;
|
||||
}
|
||||
|
||||
static String_type get_name( const Pair_type& pair )
|
||||
{
|
||||
return pair.first;
|
||||
}
|
||||
|
||||
static Value_type get_value( const Pair_type& pair )
|
||||
{
|
||||
return pair.second;
|
||||
}
|
||||
};
|
||||
|
||||
// typedefs for ASCII
|
||||
|
||||
typedef Config_map< std::string > mConfig;
|
||||
|
||||
typedef mConfig::Value_type mValue;
|
||||
typedef mConfig::Object_type mObject;
|
||||
typedef mConfig::Array_type mArray;
|
||||
|
||||
// typedefs for Unicode
|
||||
|
||||
#ifndef BOOST_NO_STD_WSTRING
|
||||
|
||||
typedef Config_map< std::wstring > wmConfig;
|
||||
|
||||
typedef wmConfig::Value_type wmValue;
|
||||
typedef wmConfig::Object_type wmObject;
|
||||
typedef wmConfig::Array_type wmArray;
|
||||
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// implementation
|
||||
|
||||
template< class Config >
|
||||
const Value_impl< Config > Value_impl< Config >::null;
|
||||
|
||||
template< class Config >
|
||||
Value_impl< Config >::Value_impl()
|
||||
: type_( null_type )
|
||||
, is_uint64_( false )
|
||||
{
|
||||
}
|
||||
|
||||
template< class Config >
|
||||
Value_impl< Config >::Value_impl( const Const_str_ptr value )
|
||||
: type_( str_type )
|
||||
, v_( String_type( value ) )
|
||||
, is_uint64_( false )
|
||||
{
|
||||
}
|
||||
|
||||
template< class Config >
|
||||
Value_impl< Config >::Value_impl( const String_type& value )
|
||||
: type_( str_type )
|
||||
, v_( value )
|
||||
, is_uint64_( false )
|
||||
{
|
||||
}
|
||||
|
||||
template< class Config >
|
||||
Value_impl< Config >::Value_impl( const Object& value )
|
||||
: type_( obj_type )
|
||||
, v_( value )
|
||||
, is_uint64_( false )
|
||||
{
|
||||
}
|
||||
|
||||
template< class Config >
|
||||
Value_impl< Config >::Value_impl( const Array& value )
|
||||
: type_( array_type )
|
||||
, v_( value )
|
||||
, is_uint64_( false )
|
||||
{
|
||||
}
|
||||
|
||||
template< class Config >
|
||||
Value_impl< Config >::Value_impl( bool value )
|
||||
: type_( bool_type )
|
||||
, v_( value )
|
||||
, is_uint64_( false )
|
||||
{
|
||||
}
|
||||
|
||||
template< class Config >
|
||||
Value_impl< Config >::Value_impl( int value )
|
||||
: type_( int_type )
|
||||
, v_( static_cast< boost::int64_t >( value ) )
|
||||
, is_uint64_( false )
|
||||
{
|
||||
}
|
||||
|
||||
template< class Config >
|
||||
Value_impl< Config >::Value_impl( boost::int64_t value )
|
||||
: type_( int_type )
|
||||
, v_( value )
|
||||
, is_uint64_( false )
|
||||
{
|
||||
}
|
||||
|
||||
template< class Config >
|
||||
Value_impl< Config >::Value_impl( boost::uint64_t value )
|
||||
: type_( int_type )
|
||||
, v_( static_cast< boost::int64_t >( value ) )
|
||||
, is_uint64_( true )
|
||||
{
|
||||
}
|
||||
|
||||
template< class Config >
|
||||
Value_impl< Config >::Value_impl( double value )
|
||||
: type_( real_type )
|
||||
, v_( value )
|
||||
, is_uint64_( false )
|
||||
{
|
||||
}
|
||||
|
||||
template< class Config >
|
||||
Value_impl< Config >::Value_impl( const Value_impl< Config >& other )
|
||||
: type_( other.type() )
|
||||
, v_( other.v_ )
|
||||
, is_uint64_( other.is_uint64_ )
|
||||
{
|
||||
}
|
||||
|
||||
template< class Config >
|
||||
Value_impl< Config >& Value_impl< Config >::operator=( const Value_impl& lhs )
|
||||
{
|
||||
Value_impl tmp( lhs );
|
||||
|
||||
std::swap( type_, tmp.type_ );
|
||||
std::swap( v_, tmp.v_ );
|
||||
std::swap( is_uint64_, tmp.is_uint64_ );
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template< class Config >
|
||||
bool Value_impl< Config >::operator==( const Value_impl& lhs ) const
|
||||
{
|
||||
if( this == &lhs ) return true;
|
||||
|
||||
if( type() != lhs.type() ) return false;
|
||||
|
||||
return v_ == lhs.v_;
|
||||
}
|
||||
|
||||
template< class Config >
|
||||
Value_type Value_impl< Config >::type() const
|
||||
{
|
||||
return type_;
|
||||
}
|
||||
|
||||
template< class Config >
|
||||
bool Value_impl< Config >::is_uint64() const
|
||||
{
|
||||
return is_uint64_;
|
||||
}
|
||||
|
||||
template< class Config >
|
||||
bool Value_impl< Config >::is_null() const
|
||||
{
|
||||
return type() == null_type;
|
||||
}
|
||||
|
||||
template< class Config >
|
||||
void Value_impl< Config >::check_type( const Value_type vtype ) const
|
||||
{
|
||||
if( type() != vtype )
|
||||
{
|
||||
std::ostringstream os;
|
||||
|
||||
///// triangles: Tell the types by name instead of by number
|
||||
os << "value is type " << Value_type_name[type()] << ", expected " << Value_type_name[vtype];
|
||||
|
||||
throw std::runtime_error( os.str() );
|
||||
}
|
||||
}
|
||||
|
||||
template< class Config >
|
||||
const typename Config::String_type& Value_impl< Config >::get_str() const
|
||||
{
|
||||
check_type( str_type );
|
||||
|
||||
return *boost::get< String_type >( &v_ );
|
||||
}
|
||||
|
||||
template< class Config >
|
||||
const typename Value_impl< Config >::Object& Value_impl< Config >::get_obj() const
|
||||
{
|
||||
check_type( obj_type );
|
||||
|
||||
return *boost::get< Object >( &v_ );
|
||||
}
|
||||
|
||||
template< class Config >
|
||||
const typename Value_impl< Config >::Array& Value_impl< Config >::get_array() const
|
||||
{
|
||||
check_type( array_type );
|
||||
|
||||
return *boost::get< Array >( &v_ );
|
||||
}
|
||||
|
||||
template< class Config >
|
||||
bool Value_impl< Config >::get_bool() const
|
||||
{
|
||||
check_type( bool_type );
|
||||
|
||||
return boost::get< bool >( v_ );
|
||||
}
|
||||
|
||||
template< class Config >
|
||||
int Value_impl< Config >::get_int() const
|
||||
{
|
||||
check_type( int_type );
|
||||
|
||||
return static_cast< int >( get_int64() );
|
||||
}
|
||||
|
||||
template< class Config >
|
||||
boost::int64_t Value_impl< Config >::get_int64() const
|
||||
{
|
||||
check_type( int_type );
|
||||
|
||||
return boost::get< boost::int64_t >( v_ );
|
||||
}
|
||||
|
||||
template< class Config >
|
||||
boost::uint64_t Value_impl< Config >::get_uint64() const
|
||||
{
|
||||
check_type( int_type );
|
||||
|
||||
return static_cast< boost::uint64_t >( get_int64() );
|
||||
}
|
||||
|
||||
template< class Config >
|
||||
double Value_impl< Config >::get_real() const
|
||||
{
|
||||
if( type() == int_type )
|
||||
{
|
||||
return is_uint64() ? static_cast< double >( get_uint64() )
|
||||
: static_cast< double >( get_int64() );
|
||||
}
|
||||
|
||||
check_type( real_type );
|
||||
|
||||
return boost::get< double >( v_ );
|
||||
}
|
||||
|
||||
template< class Config >
|
||||
typename Value_impl< Config >::Object& Value_impl< Config >::get_obj()
|
||||
{
|
||||
check_type( obj_type );
|
||||
|
||||
return *boost::get< Object >( &v_ );
|
||||
}
|
||||
|
||||
template< class Config >
|
||||
typename Value_impl< Config >::Array& Value_impl< Config >::get_array()
|
||||
{
|
||||
check_type( array_type );
|
||||
|
||||
return *boost::get< Array >( &v_ );
|
||||
}
|
||||
|
||||
template< class Config >
|
||||
Pair_impl< Config >::Pair_impl( const String_type& name, const Value_type& value )
|
||||
: name_( name )
|
||||
, value_( value )
|
||||
{
|
||||
}
|
||||
|
||||
template< class Config >
|
||||
bool Pair_impl< Config >::operator==( const Pair_impl< Config >& lhs ) const
|
||||
{
|
||||
if( this == &lhs ) return true;
|
||||
|
||||
return ( name_ == lhs.name_ ) && ( value_ == lhs.value_ );
|
||||
}
|
||||
|
||||
// converts a C string, ie. 8 bit char array, to a string object
|
||||
//
|
||||
template < class String_type >
|
||||
String_type to_str( const char* c_str )
|
||||
{
|
||||
String_type result;
|
||||
|
||||
for( const char* p = c_str; *p != 0; ++p )
|
||||
{
|
||||
result += *p;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
namespace internal_
|
||||
{
|
||||
template< typename T >
|
||||
struct Type_to_type
|
||||
{
|
||||
};
|
||||
|
||||
template< class Value >
|
||||
int get_value( const Value& value, Type_to_type< int > )
|
||||
{
|
||||
return value.get_int();
|
||||
}
|
||||
|
||||
template< class Value >
|
||||
boost::int64_t get_value( const Value& value, Type_to_type< boost::int64_t > )
|
||||
{
|
||||
return value.get_int64();
|
||||
}
|
||||
|
||||
template< class Value >
|
||||
boost::uint64_t get_value( const Value& value, Type_to_type< boost::uint64_t > )
|
||||
{
|
||||
return value.get_uint64();
|
||||
}
|
||||
|
||||
template< class Value >
|
||||
double get_value( const Value& value, Type_to_type< double > )
|
||||
{
|
||||
return value.get_real();
|
||||
}
|
||||
|
||||
template< class Value >
|
||||
typename Value::String_type get_value( const Value& value, Type_to_type< typename Value::String_type > )
|
||||
{
|
||||
return value.get_str();
|
||||
}
|
||||
|
||||
template< class Value >
|
||||
typename Value::Array get_value( const Value& value, Type_to_type< typename Value::Array > )
|
||||
{
|
||||
return value.get_array();
|
||||
}
|
||||
|
||||
template< class Value >
|
||||
typename Value::Object get_value( const Value& value, Type_to_type< typename Value::Object > )
|
||||
{
|
||||
return value.get_obj();
|
||||
}
|
||||
|
||||
template< class Value >
|
||||
bool get_value( const Value& value, Type_to_type< bool > )
|
||||
{
|
||||
return value.get_bool();
|
||||
}
|
||||
}
|
||||
|
||||
template< class Config >
|
||||
template< typename T >
|
||||
T Value_impl< Config >::get_value() const
|
||||
{
|
||||
return internal_::get_value( *this, internal_::Type_to_type< T >() );
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,95 +0,0 @@
|
||||
// Copyright John W. Wilkinson 2007 - 2009.
|
||||
// Distributed under the MIT License, see accompanying file LICENSE.txt
|
||||
|
||||
// json spirit version 4.03
|
||||
|
||||
#include "json_spirit_writer.h"
|
||||
#include "json_spirit_writer_template.h"
|
||||
|
||||
void json_spirit::write( const Value& value, std::ostream& os )
|
||||
{
|
||||
write_stream( value, os, false );
|
||||
}
|
||||
|
||||
void json_spirit::write_formatted( const Value& value, std::ostream& os )
|
||||
{
|
||||
write_stream( value, os, true );
|
||||
}
|
||||
|
||||
std::string json_spirit::write( const Value& value )
|
||||
{
|
||||
return write_string( value, false );
|
||||
}
|
||||
|
||||
std::string json_spirit::write_formatted( const Value& value )
|
||||
{
|
||||
return write_string( value, true );
|
||||
}
|
||||
|
||||
#ifndef BOOST_NO_STD_WSTRING
|
||||
|
||||
void json_spirit::write( const wValue& value, std::wostream& os )
|
||||
{
|
||||
write_stream( value, os, false );
|
||||
}
|
||||
|
||||
void json_spirit::write_formatted( const wValue& value, std::wostream& os )
|
||||
{
|
||||
write_stream( value, os, true );
|
||||
}
|
||||
|
||||
std::wstring json_spirit::write( const wValue& value )
|
||||
{
|
||||
return write_string( value, false );
|
||||
}
|
||||
|
||||
std::wstring json_spirit::write_formatted( const wValue& value )
|
||||
{
|
||||
return write_string( value, true );
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void json_spirit::write( const mValue& value, std::ostream& os )
|
||||
{
|
||||
write_stream( value, os, false );
|
||||
}
|
||||
|
||||
void json_spirit::write_formatted( const mValue& value, std::ostream& os )
|
||||
{
|
||||
write_stream( value, os, true );
|
||||
}
|
||||
|
||||
std::string json_spirit::write( const mValue& value )
|
||||
{
|
||||
return write_string( value, false );
|
||||
}
|
||||
|
||||
std::string json_spirit::write_formatted( const mValue& value )
|
||||
{
|
||||
return write_string( value, true );
|
||||
}
|
||||
|
||||
#ifndef BOOST_NO_STD_WSTRING
|
||||
|
||||
void json_spirit::write( const wmValue& value, std::wostream& os )
|
||||
{
|
||||
write_stream( value, os, false );
|
||||
}
|
||||
|
||||
void json_spirit::write_formatted( const wmValue& value, std::wostream& os )
|
||||
{
|
||||
write_stream( value, os, true );
|
||||
}
|
||||
|
||||
std::wstring json_spirit::write( const wmValue& value )
|
||||
{
|
||||
return write_string( value, false );
|
||||
}
|
||||
|
||||
std::wstring json_spirit::write_formatted( const wmValue& value )
|
||||
{
|
||||
return write_string( value, true );
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,50 +0,0 @@
|
||||
#ifndef JSON_SPIRIT_WRITER
|
||||
#define JSON_SPIRIT_WRITER
|
||||
|
||||
// Copyright John W. Wilkinson 2007 - 2009.
|
||||
// Distributed under the MIT License, see accompanying file LICENSE.txt
|
||||
|
||||
// json spirit version 4.03
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
|
||||
# pragma once
|
||||
#endif
|
||||
|
||||
#include "json_spirit_value.h"
|
||||
#include <iostream>
|
||||
|
||||
namespace json_spirit
|
||||
{
|
||||
// functions to convert JSON Values to text,
|
||||
// the "formatted" versions add whitespace to format the output nicely
|
||||
|
||||
void write ( const Value& value, std::ostream& os );
|
||||
void write_formatted( const Value& value, std::ostream& os );
|
||||
std::string write ( const Value& value );
|
||||
std::string write_formatted( const Value& value );
|
||||
|
||||
#ifndef BOOST_NO_STD_WSTRING
|
||||
|
||||
void write ( const wValue& value, std::wostream& os );
|
||||
void write_formatted( const wValue& value, std::wostream& os );
|
||||
std::wstring write ( const wValue& value );
|
||||
std::wstring write_formatted( const wValue& value );
|
||||
|
||||
#endif
|
||||
|
||||
void write ( const mValue& value, std::ostream& os );
|
||||
void write_formatted( const mValue& value, std::ostream& os );
|
||||
std::string write ( const mValue& value );
|
||||
std::string write_formatted( const mValue& value );
|
||||
|
||||
#ifndef BOOST_NO_STD_WSTRING
|
||||
|
||||
void write ( const wmValue& value, std::wostream& os );
|
||||
void write_formatted( const wmValue& value, std::wostream& os );
|
||||
std::wstring write ( const wmValue& value );
|
||||
std::wstring write_formatted( const wmValue& value );
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,248 +0,0 @@
|
||||
#ifndef JSON_SPIRIT_WRITER_TEMPLATE
|
||||
#define JSON_SPIRIT_WRITER_TEMPLATE
|
||||
|
||||
// Copyright John W. Wilkinson 2007 - 2009.
|
||||
// Distributed under the MIT License, see accompanying file LICENSE.txt
|
||||
|
||||
// json spirit version 4.03
|
||||
|
||||
#include "json_spirit_value.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
namespace json_spirit
|
||||
{
|
||||
inline char to_hex_char( unsigned int c )
|
||||
{
|
||||
assert( c <= 0xF );
|
||||
|
||||
const char ch = static_cast< char >( c );
|
||||
|
||||
if( ch < 10 ) return '0' + ch;
|
||||
|
||||
return 'A' - 10 + ch;
|
||||
}
|
||||
|
||||
template< class String_type >
|
||||
String_type non_printable_to_string( unsigned int c )
|
||||
{
|
||||
typedef typename String_type::value_type Char_type;
|
||||
|
||||
String_type result( 6, '\\' );
|
||||
|
||||
result[1] = 'u';
|
||||
|
||||
result[ 5 ] = to_hex_char( c & 0x000F ); c >>= 4;
|
||||
result[ 4 ] = to_hex_char( c & 0x000F ); c >>= 4;
|
||||
result[ 3 ] = to_hex_char( c & 0x000F ); c >>= 4;
|
||||
result[ 2 ] = to_hex_char( c & 0x000F );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template< typename Char_type, class String_type >
|
||||
bool add_esc_char( Char_type c, String_type& s )
|
||||
{
|
||||
switch( c )
|
||||
{
|
||||
case '"': s += to_str< String_type >( "\\\"" ); return true;
|
||||
case '\\': s += to_str< String_type >( "\\\\" ); return true;
|
||||
case '\b': s += to_str< String_type >( "\\b" ); return true;
|
||||
case '\f': s += to_str< String_type >( "\\f" ); return true;
|
||||
case '\n': s += to_str< String_type >( "\\n" ); return true;
|
||||
case '\r': s += to_str< String_type >( "\\r" ); return true;
|
||||
case '\t': s += to_str< String_type >( "\\t" ); return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template< class String_type >
|
||||
String_type add_esc_chars( const String_type& s )
|
||||
{
|
||||
typedef typename String_type::const_iterator Iter_type;
|
||||
typedef typename String_type::value_type Char_type;
|
||||
|
||||
String_type result;
|
||||
|
||||
const Iter_type end( s.end() );
|
||||
|
||||
for( Iter_type i = s.begin(); i != end; ++i )
|
||||
{
|
||||
const Char_type c( *i );
|
||||
|
||||
if( add_esc_char( c, result ) ) continue;
|
||||
|
||||
const wint_t unsigned_c( ( c >= 0 ) ? c : 256 + c );
|
||||
|
||||
if( iswprint( unsigned_c ) )
|
||||
{
|
||||
result += c;
|
||||
}
|
||||
else
|
||||
{
|
||||
result += non_printable_to_string< String_type >( unsigned_c );
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// this class generates the JSON text,
|
||||
// it keeps track of the indentation level etc.
|
||||
//
|
||||
template< class Value_type, class Ostream_type >
|
||||
class Generator
|
||||
{
|
||||
typedef typename Value_type::Config_type Config_type;
|
||||
typedef typename Config_type::String_type String_type;
|
||||
typedef typename Config_type::Object_type Object_type;
|
||||
typedef typename Config_type::Array_type Array_type;
|
||||
typedef typename String_type::value_type Char_type;
|
||||
typedef typename Object_type::value_type Obj_member_type;
|
||||
|
||||
public:
|
||||
|
||||
Generator( const Value_type& value, Ostream_type& os, bool pretty )
|
||||
: os_( os )
|
||||
, indentation_level_( 0 )
|
||||
, pretty_( pretty )
|
||||
{
|
||||
output( value );
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void output( const Value_type& value )
|
||||
{
|
||||
switch( value.type() )
|
||||
{
|
||||
case obj_type: output( value.get_obj() ); break;
|
||||
case array_type: output( value.get_array() ); break;
|
||||
case str_type: output( value.get_str() ); break;
|
||||
case bool_type: output( value.get_bool() ); break;
|
||||
case int_type: output_int( value ); break;
|
||||
|
||||
/// triangles: Added std::fixed and changed precision from 16 to 8
|
||||
case real_type: os_ << std::showpoint << std::fixed << std::setprecision(8)
|
||||
<< value.get_real(); break;
|
||||
|
||||
case null_type: os_ << "null"; break;
|
||||
default: assert( false );
|
||||
}
|
||||
}
|
||||
|
||||
void output( const Object_type& obj )
|
||||
{
|
||||
output_array_or_obj( obj, '{', '}' );
|
||||
}
|
||||
|
||||
void output( const Array_type& arr )
|
||||
{
|
||||
output_array_or_obj( arr, '[', ']' );
|
||||
}
|
||||
|
||||
void output( const Obj_member_type& member )
|
||||
{
|
||||
output( Config_type::get_name( member ) ); space();
|
||||
os_ << ':'; space();
|
||||
output( Config_type::get_value( member ) );
|
||||
}
|
||||
|
||||
void output_int( const Value_type& value )
|
||||
{
|
||||
if( value.is_uint64() )
|
||||
{
|
||||
os_ << value.get_uint64();
|
||||
}
|
||||
else
|
||||
{
|
||||
os_ << value.get_int64();
|
||||
}
|
||||
}
|
||||
|
||||
void output( const String_type& s )
|
||||
{
|
||||
os_ << '"' << add_esc_chars( s ) << '"';
|
||||
}
|
||||
|
||||
void output( bool b )
|
||||
{
|
||||
os_ << to_str< String_type >( b ? "true" : "false" );
|
||||
}
|
||||
|
||||
template< class T >
|
||||
void output_array_or_obj( const T& t, Char_type start_char, Char_type end_char )
|
||||
{
|
||||
os_ << start_char; new_line();
|
||||
|
||||
++indentation_level_;
|
||||
|
||||
for( typename T::const_iterator i = t.begin(); i != t.end(); ++i )
|
||||
{
|
||||
indent(); output( *i );
|
||||
|
||||
typename T::const_iterator next = i;
|
||||
|
||||
if( ++next != t.end())
|
||||
{
|
||||
os_ << ',';
|
||||
}
|
||||
|
||||
new_line();
|
||||
}
|
||||
|
||||
--indentation_level_;
|
||||
|
||||
indent(); os_ << end_char;
|
||||
}
|
||||
|
||||
void indent()
|
||||
{
|
||||
if( !pretty_ ) return;
|
||||
|
||||
for( int i = 0; i < indentation_level_; ++i )
|
||||
{
|
||||
os_ << " ";
|
||||
}
|
||||
}
|
||||
|
||||
void space()
|
||||
{
|
||||
if( pretty_ ) os_ << ' ';
|
||||
}
|
||||
|
||||
void new_line()
|
||||
{
|
||||
if( pretty_ ) os_ << '\n';
|
||||
}
|
||||
|
||||
Generator& operator=( const Generator& ); // to prevent "assignment operator could not be generated" warning
|
||||
|
||||
Ostream_type& os_;
|
||||
int indentation_level_;
|
||||
bool pretty_;
|
||||
};
|
||||
|
||||
template< class Value_type, class Ostream_type >
|
||||
void write_stream( const Value_type& value, Ostream_type& os, bool pretty )
|
||||
{
|
||||
Generator< Value_type, Ostream_type >( value, os, pretty );
|
||||
}
|
||||
|
||||
template< class Value_type >
|
||||
typename Value_type::String_type write_string( const Value_type& value, bool pretty )
|
||||
{
|
||||
typedef typename Value_type::String_type::value_type Char_type;
|
||||
|
||||
std::basic_ostringstream< Char_type > os;
|
||||
|
||||
write_stream( value, os, pretty );
|
||||
|
||||
return os.str();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -31,9 +31,13 @@ int64_t GetWeight(int64_t nIntervalBeginning, int64_t nIntervalEnd)
|
||||
if (nAge < 0)
|
||||
return 0;
|
||||
|
||||
// After v5 fork: remove max age cap so coins aged during the freeze can stake
|
||||
// After v5 fork: use soft cap of 7 days instead of hard nStakeMaxAge.
|
||||
// This prevents "stake surprise" where a whale who was offline for weeks
|
||||
// comes back with massively amplified staking power and dominates blocks.
|
||||
// The 7-day cap still allows generous accumulation while limiting abuse.
|
||||
static const int64_t STAKE_AGE_SOFT_CAP = 7 * 24 * 60 * 60; // 7 days
|
||||
if (pindexBest && pindexBest->nHeight >= FORK_HEIGHT_V5)
|
||||
return nAge;
|
||||
return min(nAge, STAKE_AGE_SOFT_CAP);
|
||||
|
||||
return min(nAge, (int64_t)nStakeMaxAge);
|
||||
}
|
||||
@@ -334,9 +338,11 @@ bool CheckStakeKernelHash(unsigned int nBits, const CBlock& blockFrom, unsigned
|
||||
// Now check if proof-of-stake hash meets target protocol
|
||||
if (CBigNum(hashProofOfStake) > bnCoinDayWeight * bnTargetPerCoinDay)
|
||||
{
|
||||
// Guard against null pindexBest during early startup / IBD
|
||||
int nCurrentHeight = pindexBest ? pindexBest->nHeight : 0;
|
||||
|
||||
// triangles fix: accept hash to get blockchain moving again with Pharao release (v 4.0.0.1) for first 10 blocks after release
|
||||
//printf(">>>> pindexBest->nHeight %d\n",pindexBest->nHeight);
|
||||
if (pindexBest->nHeight > CRAPCHAIN_CUTOFF_BLOCK)
|
||||
if (nCurrentHeight > CRAPCHAIN_CUTOFF_BLOCK)
|
||||
{
|
||||
if(fDebug)
|
||||
{
|
||||
@@ -349,8 +355,8 @@ bool CheckStakeKernelHash(unsigned int nBits, const CBlock& blockFrom, unsigned
|
||||
else
|
||||
{
|
||||
//accept hash
|
||||
if (pindexBest->nHeight % 10000 == 0 || pindexBest->nHeight > 2186900)
|
||||
printf(">>>> pindexBest->nHeight %d, Pharao release - hash accepted\n", pindexBest->nHeight);
|
||||
if (nCurrentHeight % 10000 == 0 || nCurrentHeight > 2186900)
|
||||
printf(">>>> pindexBest->nHeight %d, Pharao release - hash accepted\n", nCurrentHeight);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
set(LEVELDB_CORE_SOURCES
|
||||
db/builder.cc
|
||||
db/c.cc
|
||||
db/db_impl.cc
|
||||
db/db_iter.cc
|
||||
db/dbformat.cc
|
||||
db/dumpfile.cc
|
||||
db/filename.cc
|
||||
db/log_reader.cc
|
||||
db/log_writer.cc
|
||||
db/memtable.cc
|
||||
db/repair.cc
|
||||
db/table_cache.cc
|
||||
db/version_edit.cc
|
||||
db/version_set.cc
|
||||
db/write_batch.cc
|
||||
table/block.cc
|
||||
table/block_builder.cc
|
||||
table/filter_block.cc
|
||||
table/format.cc
|
||||
table/iterator.cc
|
||||
table/merger.cc
|
||||
table/table.cc
|
||||
table/table_builder.cc
|
||||
table/two_level_iterator.cc
|
||||
util/arena.cc
|
||||
util/bloom.cc
|
||||
util/cache.cc
|
||||
util/coding.cc
|
||||
util/comparator.cc
|
||||
util/crc32c.cc
|
||||
util/env.cc
|
||||
util/filter_policy.cc
|
||||
util/hash.cc
|
||||
util/histogram.cc
|
||||
util/logging.cc
|
||||
util/options.cc
|
||||
util/status.cc
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
list(APPEND LEVELDB_CORE_SOURCES util/env_windows.cc)
|
||||
else()
|
||||
list(APPEND LEVELDB_CORE_SOURCES util/env_posix.cc)
|
||||
endif()
|
||||
|
||||
add_library(leveldb_lib STATIC ${LEVELDB_CORE_SOURCES})
|
||||
|
||||
target_compile_features(leveldb_lib PUBLIC cxx_std_11)
|
||||
|
||||
target_include_directories(leveldb_lib
|
||||
PUBLIC
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/include"
|
||||
PRIVATE
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
)
|
||||
|
||||
target_compile_definitions(leveldb_lib
|
||||
PRIVATE
|
||||
HAVE_CRC32C=0
|
||||
HAVE_SNAPPY=0
|
||||
)
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
|
||||
target_compile_options(leveldb_lib PRIVATE -fno-builtin-memcmp)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
target_compile_definitions(leveldb_lib PRIVATE
|
||||
_REENTRANT
|
||||
OS_WINDOWS
|
||||
LEVELDB_PLATFORM_WINDOWS
|
||||
WINVER=0x0500
|
||||
__USE_MINGW_ANSI_STDIO=1
|
||||
)
|
||||
target_link_libraries(leveldb_lib PUBLIC shlwapi)
|
||||
elseif(APPLE)
|
||||
target_compile_definitions(leveldb_lib PRIVATE
|
||||
OS_MACOSX
|
||||
LEVELDB_PLATFORM_POSIX
|
||||
)
|
||||
target_link_libraries(leveldb_lib PUBLIC Threads::Threads)
|
||||
else()
|
||||
target_compile_definitions(leveldb_lib PRIVATE
|
||||
_REENTRANT
|
||||
OS_LINUX
|
||||
LEVELDB_PLATFORM_POSIX
|
||||
)
|
||||
target_link_libraries(leveldb_lib PUBLIC Threads::Threads)
|
||||
endif()
|
||||
|
||||
add_library(leveldb_memenv STATIC helpers/memenv/memenv.cc)
|
||||
|
||||
target_compile_features(leveldb_memenv PUBLIC cxx_std_11)
|
||||
|
||||
target_include_directories(leveldb_memenv
|
||||
PUBLIC
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/include"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/helpers"
|
||||
PRIVATE
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
)
|
||||
|
||||
target_compile_definitions(leveldb_memenv
|
||||
PRIVATE
|
||||
HAVE_CRC32C=0
|
||||
HAVE_SNAPPY=0
|
||||
)
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
|
||||
target_compile_options(leveldb_memenv PRIVATE -fno-builtin-memcmp)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
target_compile_definitions(leveldb_memenv PRIVATE
|
||||
_REENTRANT
|
||||
OS_WINDOWS
|
||||
LEVELDB_PLATFORM_WINDOWS
|
||||
WINVER=0x0500
|
||||
__USE_MINGW_ANSI_STDIO=1
|
||||
)
|
||||
elseif(APPLE)
|
||||
target_compile_definitions(leveldb_memenv PRIVATE
|
||||
OS_MACOSX
|
||||
LEVELDB_PLATFORM_POSIX
|
||||
)
|
||||
else()
|
||||
target_compile_definitions(leveldb_memenv PRIVATE
|
||||
_REENTRANT
|
||||
OS_LINUX
|
||||
LEVELDB_PLATFORM_POSIX
|
||||
)
|
||||
endif()
|
||||
|
||||
target_link_libraries(leveldb_memenv PUBLIC leveldb_lib)
|
||||
|
||||
add_library(leveldb_bundled INTERFACE)
|
||||
|
||||
target_link_libraries(leveldb_bundled INTERFACE
|
||||
leveldb_lib
|
||||
leveldb_memenv
|
||||
)
|
||||
|
||||
target_include_directories(leveldb_bundled INTERFACE
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/include"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/helpers"
|
||||
)
|
||||
@@ -11,6 +11,8 @@
|
||||
#include "script.h"
|
||||
#include "scrypt.h"
|
||||
#include "hashblock.h"
|
||||
#include "checkqueue.h"
|
||||
#include "sigcache.h"
|
||||
|
||||
#include <list>
|
||||
|
||||
@@ -25,6 +27,7 @@ class CAddress;
|
||||
class CInv;
|
||||
class CRequestTracker;
|
||||
class CNode;
|
||||
class CScriptCheck;
|
||||
|
||||
static const int CUTOFF_POW_BLOCK = 9000;
|
||||
static const int CRAPCHAIN_CUTOFF_BLOCK = 17691; // pre-Pharao (version 4) blockchain until block 17691
|
||||
@@ -36,7 +39,8 @@ static const unsigned int MAX_BLOCK_SIZE_GEN = MAX_BLOCK_SIZE/2;
|
||||
static const unsigned int MAX_BLOCK_SIGOPS = MAX_BLOCK_SIZE/50;
|
||||
static const unsigned int MAX_ORPHAN_TRANSACTIONS = MAX_BLOCK_SIZE/100;
|
||||
static const unsigned int MAX_ORPHAN_BLOCKS = 750;
|
||||
static const unsigned int MAX_ORPHAN_BLOCKS_IBD = 4000;
|
||||
static const unsigned int MAX_ORPHAN_BLOCKS_IBD = 1500;
|
||||
static const unsigned int MAX_REORG_DEPTH = 100; // reject reorgs deeper than this (finality)
|
||||
static const unsigned int MAX_INV_SZ = 50000;
|
||||
static const int64_t MIN_TX_FEE = (1 * CENT) / 100;
|
||||
static const int64_t MIN_RELAY_TX_FEE = (1 * CENT) / 100;
|
||||
@@ -57,11 +61,15 @@ static const int fHaveUPnP = false;
|
||||
|
||||
static const uint256 hashGenesisBlockOfficial("0x7e7a6e4dd5fe895106fca912dfbacaeaf2a89e76c6a588df8ff96e0e18b96021");
|
||||
static const uint256 hashGenesisBlockTestNet ("0x7e7a6e4dd5fe895106fca912dfbacaeaf2a89e76c6a588df8ff96e0e18b96021");
|
||||
inline int64_t GetMaxTimeDrift(int nHeight) { return (nHeight >= FORK_HEIGHT_V5_4) ? 3 * 60 : 10 * 60; }
|
||||
inline int64_t GetMaxTimeDrift(int nHeight) { return (nHeight >= FORK_HEIGHT_V5_4) ? 90 : 10 * 60; }
|
||||
inline int64_t PastDrift(int64_t nTime, int nHeight) { return nTime - GetMaxTimeDrift(nHeight); }
|
||||
inline int64_t FutureDrift(int64_t nTime, int nHeight) { return nTime + GetMaxTimeDrift(nHeight); }
|
||||
inline int64_t PastDrift(int64_t nTime) { return PastDrift(nTime, nBestHeight); }
|
||||
inline int64_t FutureDrift(int64_t nTime) { return FutureDrift(nTime, nBestHeight); }
|
||||
// Height-less overloads always use post-V5.4 rules (90-second drift).
|
||||
// All nodes are well past FORK_HEIGHT_V5_4; using the global nBestHeight
|
||||
// here previously caused nodes at different heights to disagree on block
|
||||
// validity during the fork transition — a consensus-splitting bug.
|
||||
inline int64_t PastDrift(int64_t nTime) { return PastDrift(nTime, FORK_HEIGHT_V5_4); }
|
||||
inline int64_t FutureDrift(int64_t nTime) { return FutureDrift(nTime, FORK_HEIGHT_V5_4); }
|
||||
|
||||
|
||||
extern CScript COINBASE_FLAGS;
|
||||
@@ -77,6 +85,7 @@ extern uint256 nBestChainTrust;
|
||||
extern uint256 nBestInvalidTrust;
|
||||
extern uint256 hashBestChain;
|
||||
extern CBlockIndex* pindexBest;
|
||||
extern CBlockIndex* pindexFinalized; // auto-checkpoint: deepest finalized block
|
||||
extern unsigned int nTransactionsUpdated;
|
||||
extern uint64_t nLastBlockTx;
|
||||
extern uint64_t nLastBlockSize;
|
||||
@@ -130,6 +139,7 @@ bool IsInitialBlockDownload();
|
||||
std::string GetWarnings(std::string strFor);
|
||||
bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock);
|
||||
uint256 WantedByOrphan(const CBlock* pblockOrphan);
|
||||
unsigned int LimitOrphanBlocks(unsigned int nMaxOrphans);
|
||||
const CBlockIndex* GetLastBlockIndex(const CBlockIndex* pindex, bool fProofOfStake);
|
||||
void StakeMiner(CWallet *pwallet);
|
||||
void ResendWalletTransactions(bool fForce = false);
|
||||
@@ -431,7 +441,51 @@ enum GetMinFee_mode
|
||||
GMF_SEND,
|
||||
};
|
||||
|
||||
typedef std::map<uint256, std::pair<CTxIndex, CTransaction> > MapPrevTx;
|
||||
/** A single unspent transaction output entry in the UTXO database.
|
||||
* Keyed by (txhash, output_index). Erased when spent.
|
||||
*/
|
||||
class CUtxoEntry
|
||||
{
|
||||
public:
|
||||
int64_t nValue; // output value in satoshis
|
||||
int nHeight; // block height where output was created
|
||||
CScript scriptPubKey; // output script (needed for sig verification)
|
||||
bool fCoinBase; // from a coinbase transaction
|
||||
bool fCoinStake; // from a coinstake transaction
|
||||
unsigned int nTxTime; // transaction timestamp (needed for PoS coin age)
|
||||
|
||||
CUtxoEntry()
|
||||
{
|
||||
SetNull();
|
||||
}
|
||||
|
||||
IMPLEMENT_SERIALIZE
|
||||
(
|
||||
READWRITE(nValue);
|
||||
READWRITE(nHeight);
|
||||
READWRITE(scriptPubKey);
|
||||
READWRITE(fCoinBase);
|
||||
READWRITE(fCoinStake);
|
||||
READWRITE(nTxTime);
|
||||
)
|
||||
|
||||
void SetNull()
|
||||
{
|
||||
nValue = -1;
|
||||
nHeight = 0;
|
||||
scriptPubKey.clear();
|
||||
fCoinBase = false;
|
||||
fCoinStake = false;
|
||||
nTxTime = 0;
|
||||
}
|
||||
|
||||
bool IsNull() const
|
||||
{
|
||||
return (nValue == -1);
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::map<COutPoint, CUtxoEntry> MapPrevTx;
|
||||
|
||||
/** The basic transaction that is broadcasted on the network and contained in
|
||||
* blocks. A transaction can contain multiple inputs and outputs.
|
||||
@@ -672,33 +726,30 @@ public:
|
||||
bool ReadFromDisk(COutPoint prevout);
|
||||
bool DisconnectInputs(CTxDB& txdb);
|
||||
|
||||
/** Fetch from memory and/or disk. inputsRet keys are transaction hashes.
|
||||
/** Fetch UTXO entries for all inputs from the UTXO database or mempool.
|
||||
|
||||
@param[in] txdb Transaction database
|
||||
@param[in] mapTestPool List of pending changes to the transaction index database
|
||||
@param[in] fBlock True if being called to add a new best-block to the chain
|
||||
@param[in] fMiner True if being called by CreateNewBlock
|
||||
@param[out] inputsRet Pointers to this transaction's inputs
|
||||
@param[out] fInvalid returns true if transaction is invalid
|
||||
@return Returns true if all inputs are in txdb or mapTestPool
|
||||
@param[in] txdb Transaction database
|
||||
@param[in] mapPendingUtxos UTXOs created by earlier transactions in the same block
|
||||
@param[in] fBlock True if being called to add a new best-block to the chain
|
||||
@param[in] fMiner True if being called by CreateNewBlock
|
||||
@param[out] inputsRet UTXO entries for this transaction's inputs (keyed by COutPoint)
|
||||
@param[out] fInvalid returns true if transaction is invalid
|
||||
@return Returns true if all inputs are found
|
||||
*/
|
||||
bool FetchInputs(CTxDB& txdb, const std::map<uint256, CTxIndex>& mapTestPool,
|
||||
bool FetchInputs(CTxDB& txdb, const MapPrevTx& mapPendingUtxos,
|
||||
bool fBlock, bool fMiner, MapPrevTx& inputsRet, bool& fInvalid);
|
||||
|
||||
/** Sanity check previous transactions, then, if all checks succeed,
|
||||
mark them as spent by this transaction.
|
||||
/** Validate inputs against UTXO entries and verify signatures.
|
||||
|
||||
@param[in] inputs Previous transactions (from FetchInputs)
|
||||
@param[out] mapTestPool Keeps track of inputs that need to be updated on disk
|
||||
@param[in] posThisTx Position of this transaction on disk
|
||||
@param[in] pindexBlock
|
||||
@param[in] fBlock true if called from ConnectBlock
|
||||
@param[in] fMiner true if called from CreateNewBlock
|
||||
@param[in] inputs UTXO entries for inputs (from FetchInputs)
|
||||
@param[in] pindexBlock Block being connected
|
||||
@param[in] fBlock true if called from ConnectBlock
|
||||
@param[in] fMiner true if called from CreateNewBlock
|
||||
@return Returns true if all checks succeed
|
||||
*/
|
||||
bool ConnectInputs(CTxDB& txdb, MapPrevTx inputs,
|
||||
std::map<uint256, CTxIndex>& mapTestPool, const CDiskTxPos& posThisTx,
|
||||
const CBlockIndex* pindexBlock, bool fBlock, bool fMiner);
|
||||
bool ConnectInputs(CTxDB& txdb, const MapPrevTx& inputs,
|
||||
const CBlockIndex* pindexBlock, bool fBlock, bool fMiner,
|
||||
std::vector<CScriptCheck>* pvChecks = NULL);
|
||||
bool ClientConnectInputs();
|
||||
bool CheckTransaction() const;
|
||||
bool AcceptToMemoryPool(CTxDB& txdb, bool fCheckInputs=true, bool* pfMissingInputs=NULL);
|
||||
@@ -771,9 +822,10 @@ public:
|
||||
|
||||
|
||||
|
||||
/** A txdb record that contains the disk location of a transaction and the
|
||||
* locations of transactions that spend its outputs. vSpent is really only
|
||||
* used as a flag, but having the location is very helpful for debugging.
|
||||
/** A txdb record that contains the disk location of a transaction.
|
||||
* Used for getrawtransaction and wallet position lookups.
|
||||
* vSpent is legacy (kept for serialization compat) — spent state is
|
||||
* tracked by the UTXO set (CUtxoEntry) since dbformat=3.
|
||||
*/
|
||||
class CTxIndex
|
||||
{
|
||||
@@ -1364,6 +1416,10 @@ public:
|
||||
uint256 hashPrev;
|
||||
uint256 hashNext;
|
||||
|
||||
// When true, nChainTrust is included in the on-disk serialization.
|
||||
// Set by LoadBlockIndex based on the DB format version before deserializing.
|
||||
static bool fSerializeChainTrust;
|
||||
|
||||
CDiskBlockIndex()
|
||||
{
|
||||
hashPrev = 0;
|
||||
@@ -1411,6 +1467,10 @@ public:
|
||||
READWRITE(nBits);
|
||||
READWRITE(nNonce);
|
||||
READWRITE(blockHash);
|
||||
|
||||
// DB format v2+: persist chain trust to skip expensive recalculation on startup
|
||||
if (fSerializeChainTrust)
|
||||
READWRITE(nChainTrust);
|
||||
)
|
||||
|
||||
uint256 GetBlockHash() const
|
||||
@@ -1503,6 +1563,12 @@ public:
|
||||
return vHave.empty();
|
||||
}
|
||||
|
||||
// Return the first hash in the locator (peer's tip), or 0 if empty
|
||||
uint256 GetTipHash() const
|
||||
{
|
||||
return vHave.empty() ? uint256(0) : vHave[0];
|
||||
}
|
||||
|
||||
void Set(const CBlockIndex* pindex)
|
||||
{
|
||||
vHave.clear();
|
||||
@@ -1622,5 +1688,163 @@ public:
|
||||
};
|
||||
|
||||
extern CTxMemPool mempool;
|
||||
extern CScriptVerifyCache scriptVerifyCache;
|
||||
|
||||
#endif
|
||||
/**
|
||||
* Compact block relay for Tor-only networks.
|
||||
*
|
||||
* Instead of sending a full block, send the header + short transaction IDs.
|
||||
* The receiver reconstructs the block from its mempool. For PoS blocks with
|
||||
* 0-2 transactions (the common case), the coinstake is always prefilled, so
|
||||
* the compact block IS the complete block — no extra round-trip needed.
|
||||
*/
|
||||
|
||||
/** Short transaction ID: first 6 bytes of SipHash(txid) */
|
||||
static inline uint64_t GetShortTxId(const uint256& txhash, uint64_t nonce)
|
||||
{
|
||||
// Simple short ID: XOR txhash prefix with nonce
|
||||
uint64_t id = 0;
|
||||
memcpy(&id, txhash.begin(), 6); // first 6 bytes
|
||||
id ^= nonce;
|
||||
return id & 0xFFFFFFFFFFFFULL; // mask to 48 bits
|
||||
}
|
||||
|
||||
class CCompactBlock
|
||||
{
|
||||
public:
|
||||
// Block header fields
|
||||
int nVersion;
|
||||
uint256 hashPrevBlock;
|
||||
uint256 hashMerkleRoot;
|
||||
unsigned int nTime;
|
||||
unsigned int nBits;
|
||||
unsigned int nNonce;
|
||||
std::vector<unsigned char> vchBlockSig;
|
||||
|
||||
// Compact block data
|
||||
uint64_t nShortIdNonce; // nonce for short ID calculation
|
||||
std::vector<uint64_t> vShortTxIds; // short IDs for non-prefilled txs
|
||||
std::vector<std::pair<uint16_t, CTransaction>> vPrefilledTxn; // index + full tx
|
||||
|
||||
CCompactBlock() : nVersion(0), nTime(0), nBits(0), nNonce(0), nShortIdNonce(0) {}
|
||||
|
||||
// Construct from a full block: prefill coinbase + coinstake, short-ID the rest
|
||||
CCompactBlock(const CBlock& block)
|
||||
{
|
||||
nVersion = block.nVersion;
|
||||
hashPrevBlock = block.hashPrevBlock;
|
||||
hashMerkleRoot = block.hashMerkleRoot;
|
||||
nTime = block.nTime;
|
||||
nBits = block.nBits;
|
||||
nNonce = block.nNonce;
|
||||
vchBlockSig = block.vchBlockSig;
|
||||
nShortIdNonce = GetRand(std::numeric_limits<uint64_t>::max());
|
||||
|
||||
for (uint16_t i = 0; i < block.vtx.size(); i++)
|
||||
{
|
||||
if (i <= 1) {
|
||||
// Always prefill coinbase (idx 0) and coinstake (idx 1)
|
||||
vPrefilledTxn.push_back(std::make_pair(i, block.vtx[i]));
|
||||
} else {
|
||||
vShortTxIds.push_back(GetShortTxId(block.vtx[i].GetHash(), nShortIdNonce));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IMPLEMENT_SERIALIZE
|
||||
(
|
||||
READWRITE(nVersion);
|
||||
READWRITE(hashPrevBlock);
|
||||
READWRITE(hashMerkleRoot);
|
||||
READWRITE(nTime);
|
||||
READWRITE(nBits);
|
||||
READWRITE(nNonce);
|
||||
READWRITE(vchBlockSig);
|
||||
READWRITE(nShortIdNonce);
|
||||
READWRITE(vShortTxIds);
|
||||
READWRITE(vPrefilledTxn);
|
||||
)
|
||||
|
||||
uint256 GetBlockHash() const
|
||||
{
|
||||
CBlock hdr;
|
||||
hdr.nVersion = nVersion;
|
||||
hdr.hashPrevBlock = hashPrevBlock;
|
||||
hdr.hashMerkleRoot = hashMerkleRoot;
|
||||
hdr.nTime = nTime;
|
||||
hdr.nBits = nBits;
|
||||
hdr.nNonce = nNonce;
|
||||
return hdr.GetHash();
|
||||
}
|
||||
};
|
||||
|
||||
class CBlockTxnRequest
|
||||
{
|
||||
public:
|
||||
uint256 blockhash;
|
||||
std::vector<uint16_t> vIndex; // indices of missing transactions
|
||||
|
||||
IMPLEMENT_SERIALIZE
|
||||
(
|
||||
READWRITE(blockhash);
|
||||
READWRITE(vIndex);
|
||||
)
|
||||
};
|
||||
|
||||
class CBlockTxnResponse
|
||||
{
|
||||
public:
|
||||
uint256 blockhash;
|
||||
std::vector<CTransaction> vTxn;
|
||||
|
||||
IMPLEMENT_SERIALIZE
|
||||
(
|
||||
READWRITE(blockhash);
|
||||
READWRITE(vTxn);
|
||||
)
|
||||
};
|
||||
|
||||
/**
|
||||
* Closure representing one script check for parallel verification.
|
||||
* Captures everything needed to call VerifyScript independently.
|
||||
*/
|
||||
class CScriptCheck
|
||||
{
|
||||
private:
|
||||
CScript scriptPubKey;
|
||||
CScript scriptSig;
|
||||
const CTransaction* ptxTo;
|
||||
unsigned int nIn;
|
||||
int nHashType;
|
||||
|
||||
public:
|
||||
CScriptCheck() : ptxTo(NULL), nIn(0), nHashType(0) {}
|
||||
|
||||
CScriptCheck(const CScript& scriptPubKeyIn, const CScript& scriptSigIn,
|
||||
const CTransaction& txToIn, unsigned int nInIn, int nHashTypeIn)
|
||||
: scriptPubKey(scriptPubKeyIn), scriptSig(scriptSigIn),
|
||||
ptxTo(&txToIn), nIn(nInIn), nHashType(nHashTypeIn) {}
|
||||
|
||||
bool operator()()
|
||||
{
|
||||
if (!ptxTo)
|
||||
return false;
|
||||
if (!VerifyScript(scriptSig, scriptPubKey, *ptxTo, nIn, nHashType))
|
||||
return false;
|
||||
scriptVerifyCache.Set(ptxTo->GetHash(), nIn);
|
||||
return true;
|
||||
}
|
||||
|
||||
void swap(CScriptCheck& other)
|
||||
{
|
||||
std::swap(scriptPubKey, other.scriptPubKey);
|
||||
std::swap(scriptSig, other.scriptSig);
|
||||
std::swap(ptxTo, other.ptxTo);
|
||||
std::swap(nIn, other.nIn);
|
||||
std::swap(nHashType, other.nHashType);
|
||||
}
|
||||
};
|
||||
|
||||
extern CCheckQueue<CScriptCheck>* pScriptCheckQueue;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,261 +0,0 @@
|
||||
# Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||
# Distributed under the MIT/X11 software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
#
|
||||
# Windows (MSYS2 MinGW64) makefile for trianglesd headless daemon
|
||||
|
||||
USE_UPNP:=1
|
||||
USE_IPV6:=1
|
||||
|
||||
CXX=g++
|
||||
CC=gcc
|
||||
LINK=$(CXX)
|
||||
|
||||
DEPSDIR=C:/msys64/mingw64
|
||||
|
||||
DEFS=-DBOOST_SPIRIT_THREADSAFE -DBOOST_THREAD_USE_LIB -DBOOST_BIND_GLOBAL_PLACEHOLDERS -DWIN32 -D__NO_SYSTEM_INCLUDES
|
||||
|
||||
DEFS += $(addprefix -I,$(CURDIR) $(CURDIR)/tor $(CURDIR)/obj \
|
||||
$(DEPSDIR)/include)
|
||||
|
||||
LIBS = $(addprefix -L,$(DEPSDIR)/lib)
|
||||
|
||||
# libevent
|
||||
LIBS += -levent
|
||||
|
||||
LMODE = dynamic
|
||||
LMODE2 = dynamic
|
||||
ifdef STATIC
|
||||
LMODE = static
|
||||
ifeq (${STATIC}, all)
|
||||
LMODE2 = static
|
||||
endif
|
||||
endif
|
||||
|
||||
LIBS += \
|
||||
-lboost_filesystem-mt \
|
||||
-lboost_program_options-mt \
|
||||
-lboost_thread-mt \
|
||||
-lboost_chrono-mt \
|
||||
-ldb_cxx \
|
||||
-lssl \
|
||||
-lcrypto \
|
||||
-levent
|
||||
|
||||
ifndef USE_UPNP
|
||||
override USE_UPNP = -
|
||||
endif
|
||||
ifneq (${USE_UPNP}, -)
|
||||
LIBS += -lminiupnpc -liphlpapi
|
||||
DEFS += -DUSE_UPNP=$(USE_UPNP) -DSTATICLIB -DMINIUPNP_STATICLIB
|
||||
endif
|
||||
|
||||
ifneq (${USE_IPV6}, -)
|
||||
DEFS += -DUSE_IPV6=$(USE_IPV6)
|
||||
endif
|
||||
|
||||
# Windows-specific libraries
|
||||
LIBS += \
|
||||
-lws2_32 \
|
||||
-lshlwapi \
|
||||
-lmswsock \
|
||||
-lole32 \
|
||||
-loleaut32 \
|
||||
-luuid \
|
||||
-lgdi32 \
|
||||
-lcrypt32 \
|
||||
-lz \
|
||||
-lpthread
|
||||
|
||||
# Hardening
|
||||
HARDENING=-fno-stack-protector
|
||||
HARDENING+=-fstack-protector-all -Wstack-protector
|
||||
HARDENING+=-D_FORTIFY_SOURCE=2
|
||||
|
||||
DEBUGFLAGS=-g
|
||||
|
||||
xCXXFLAGS=-O2 -std=c++17 -pthread -Wall -Wextra -Wno-ignored-qualifiers -Wformat -Wformat-security -Wno-unused-parameter \
|
||||
-Wno-deprecated-declarations -Wno-reserved-user-defined-literal \
|
||||
-Wa,-mbig-obj \
|
||||
$(DEBUGFLAGS) $(DEFS) $(HARDENING) $(CXXFLAGS)
|
||||
|
||||
xLDFLAGS=-static -static-libgcc -static-libstdc++ $(LDFLAGS)
|
||||
|
||||
OBJS= \
|
||||
obj/fugue.o \
|
||||
obj/hamsi.o \
|
||||
obj/hamsi_helper.o \
|
||||
obj/groestl.o \
|
||||
obj/blake.o \
|
||||
obj/bmw.o \
|
||||
obj/skein.o \
|
||||
obj/keccak.o \
|
||||
obj/shavite.o \
|
||||
obj/jh.o \
|
||||
obj/luffa.o \
|
||||
obj/cubehash.o \
|
||||
obj/echo.o \
|
||||
obj/simd.o \
|
||||
obj/aes_helper.o \
|
||||
obj/alert.o \
|
||||
obj/version.o \
|
||||
obj/checkpoints.o \
|
||||
obj/netbase.o \
|
||||
obj/addrman.o \
|
||||
obj/crypter.o \
|
||||
obj/key.o \
|
||||
obj/db.o \
|
||||
obj/init.o \
|
||||
obj/irc.o \
|
||||
obj/keystore.o \
|
||||
obj/miner.o \
|
||||
obj/main.o \
|
||||
obj/net.o \
|
||||
obj/bootstrap.o \
|
||||
obj/net_bootstrap.o \
|
||||
obj/protocol.o \
|
||||
obj/trianglesrpc.o \
|
||||
obj/rest.o \
|
||||
obj/rpcdump.o \
|
||||
obj/rpcnet.o \
|
||||
obj/rpcmining.o \
|
||||
obj/rpcwallet.o \
|
||||
obj/rpcblockchain.o \
|
||||
obj/rpcrawtransaction.o \
|
||||
obj/rpcsmessage.o \
|
||||
obj/script.o \
|
||||
obj/sync.o \
|
||||
obj/util.o \
|
||||
obj/wallet.o \
|
||||
obj/walletdb.o \
|
||||
obj/noui.o \
|
||||
obj/kernel.o \
|
||||
obj/pbkdf2.o \
|
||||
obj/scrypt.o \
|
||||
obj/scrypt-arm.o \
|
||||
obj/scrypt-x86.o \
|
||||
obj/scrypt-x86_64.o \
|
||||
obj/smessage.o \
|
||||
obj/lz4.o \
|
||||
obj/onion_v3.o \
|
||||
obj/tor_process.o \
|
||||
obj/tor_embed_hooks.o \
|
||||
obj/tor_embedded.o
|
||||
|
||||
all: trianglesd.exe
|
||||
|
||||
LIBS += $(CURDIR)/leveldb/libleveldb.a $(CURDIR)/leveldb/libmemenv.a
|
||||
DEFS += $(addprefix -I,$(CURDIR)/leveldb/include)
|
||||
DEFS += $(addprefix -I,$(CURDIR)/leveldb/helpers)
|
||||
OBJS += obj/txdb-leveldb.o
|
||||
|
||||
leveldb/libleveldb.a:
|
||||
@echo "Building LevelDB ..."; cd leveldb; CC=gcc CXX=g++ TARGET_OS=OS_WINDOWS_CROSSCOMPILE $(MAKE) OPT="-fno-keep-inline-dllexport -march=nocona -msahf -mtune=generic -Wa,-mbig-obj -O2" libleveldb.a libmemenv.a; cd ..;
|
||||
obj/txdb-leveldb.o: leveldb/libleveldb.a
|
||||
|
||||
# auto-generated dependencies:
|
||||
-include obj/*.P
|
||||
|
||||
obj/build.h: FORCE
|
||||
/bin/sh ../share/genbuild.sh obj/build.h
|
||||
version.cpp: obj/build.h
|
||||
DEFS += -DHAVE_BUILD_INFO
|
||||
|
||||
obj/scrypt-x86.o: scrypt-x86.S
|
||||
$(CXX) -c $(xCXXFLAGS) -MMD -o $@ $<
|
||||
|
||||
obj/scrypt-x86_64.o: scrypt-x86_64.S
|
||||
$(CXX) -c $(xCXXFLAGS) -MMD -o $@ $<
|
||||
|
||||
obj/scrypt-arm.o: scrypt-arm.S
|
||||
$(CXX) -c $(xCXXFLAGS) -MMD -o $@ $<
|
||||
|
||||
obj/%.o: %.cpp
|
||||
$(CXX) -c $(xCXXFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $<
|
||||
@cp $(@:%.o=%.d) $(@:%.o=%.P); \
|
||||
sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
|
||||
-e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \
|
||||
rm -f $(@:%.o=%.d)
|
||||
|
||||
obj/%.o: %.c
|
||||
$(CXX) -c $(xCXXFLAGS) -fpermissive -MMD -MF $(@:%.o=%.d) -o $@ $<
|
||||
@cp $(@:%.o=%.d) $(@:%.o=%.P); \
|
||||
sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
|
||||
-e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \
|
||||
rm -f $(@:%.o=%.d)
|
||||
|
||||
obj/%.o: tor/%.c
|
||||
$(CC) -c $(xCXXFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $<
|
||||
@cp $(@:%.o=%.d) $(@:%.o=%.P); \
|
||||
sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
|
||||
-e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \
|
||||
rm -f $(@:%.o=%.d)
|
||||
|
||||
obj/anonymize.o: tor/anonymize.cpp
|
||||
$(CXX) -c $(xCXXFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $<
|
||||
@cp $(@:%.o=%.d) $(@:%.o=%.P); \
|
||||
sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
|
||||
-e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \
|
||||
rm -f $(@:%.o=%.d)
|
||||
|
||||
obj/onion_v3.o: tor/onion_v3.cpp
|
||||
$(CXX) -c $(xCXXFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $<
|
||||
@cp $(@:%.o=%.d) $(@:%.o=%.P); \
|
||||
sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
|
||||
-e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \
|
||||
rm -f $(@:%.o=%.d)
|
||||
|
||||
obj/tor_process.o: tor/tor_process.cpp
|
||||
$(CXX) -c $(xCXXFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $<
|
||||
@cp $(@:%.o=%.d) $(@:%.o=%.P); \
|
||||
sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
|
||||
-e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \
|
||||
rm -f $(@:%.o=%.d)
|
||||
|
||||
obj/lz4.o: lz4/lz4.c
|
||||
$(CXX) -c $(xCXXFLAGS) -fpermissive -MMD -MF $(@:%.o=%.d) -o $@ $<
|
||||
@cp $(@:%.o=%.d) $(@:%.o=%.P); \
|
||||
sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
|
||||
-e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \
|
||||
rm -f $(@:%.o=%.d)
|
||||
|
||||
obj/tor_embed_hooks.o: tor_embed_hooks.cpp
|
||||
$(CXX) -c $(xCXXFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $<
|
||||
@cp $(@:%.o=%.d) $(@:%.o=%.P); \
|
||||
sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
|
||||
-e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \
|
||||
rm -f $(@:%.o=%.d)
|
||||
|
||||
obj/tor_embedded.o: tor/tor_embedded.cpp
|
||||
$(CXX) -c $(xCXXFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $<
|
||||
@cp $(@:%.o=%.d) $(@:%.o=%.P); \
|
||||
sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
|
||||
-e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \
|
||||
rm -f $(@:%.o=%.d)
|
||||
|
||||
obj/bootstrap.o: bootstrap.cpp
|
||||
$(CXX) -c $(xCXXFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $<
|
||||
@cp $(@:%.o=%.d) $(@:%.o=%.P); \
|
||||
sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
|
||||
-e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \
|
||||
rm -f $(@:%.o=%.d)
|
||||
|
||||
obj/net_bootstrap.o: net_bootstrap.cpp
|
||||
$(CXX) -c $(xCXXFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $<
|
||||
@cp $(@:%.o=%.d) $(@:%.o=%.P); \
|
||||
sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
|
||||
-e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \
|
||||
rm -f $(@:%.o=%.d)
|
||||
|
||||
trianglesd.exe: $(OBJS:obj/%=obj/%) obj/anonymize.o
|
||||
$(LINK) $(xCXXFLAGS) -o $@ $^ $(xLDFLAGS) $(LIBS)
|
||||
|
||||
clean:
|
||||
-rm -f trianglesd.exe
|
||||
-rm -f obj/*.o
|
||||
-rm -f obj/*.P
|
||||
-rm -f obj/build.h
|
||||
-rm -f leveldb/libleveldb.a
|
||||
-rm -f leveldb/libmemenv.a
|
||||
|
||||
FORCE:
|
||||
@@ -1,301 +0,0 @@
|
||||
# -*- mode: Makefile; -*-
|
||||
# Copyright (c) 2011 Bitcoin Developers
|
||||
# Distributed under the MIT/X11 software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
# Mac OS X makefile for triangles
|
||||
# Originally by Laszlo Hanyecz (solar@heliacal.net)
|
||||
|
||||
CXX=llvm-g++
|
||||
CC=llvm-gcc
|
||||
DEPSDIR=/opt/local
|
||||
|
||||
INCLUDEPATHS= \
|
||||
-I"$(CURDIR)" \
|
||||
-I"$(CURDIR)/obj" \
|
||||
-I"$(CURDIR)/tor" \
|
||||
-I"$(DEPSDIR)/include" \
|
||||
-I"$(DEPSDIR)/include/db48" \
|
||||
-I"$(DEPSDIR)/include/boost"
|
||||
|
||||
LIBPATHS= \
|
||||
-L"$(DEPSDIR)/lib" \
|
||||
-L"$(DEPSDIR)/lib/db48"
|
||||
|
||||
USE_UPNP:=1
|
||||
USE_IPV6:=1
|
||||
|
||||
LIBS= -dead_strip
|
||||
|
||||
TESTDEFS = -DTEST_DATA_DIR=$(abspath test/data)
|
||||
|
||||
ifdef STATIC
|
||||
# Build STATIC if you are redistributing the bitcoind binary
|
||||
TESTLIBS += \
|
||||
$(DEPSDIR)/lib/libboost_unit_test_framework-mt.a
|
||||
|
||||
LIBS += \
|
||||
$(DEPSDIR)/lib/db48/libdb_cxx-4.8.a \
|
||||
$(DEPSDIR)/lib/libboost_system-mt.a \
|
||||
$(DEPSDIR)/lib/libboost_filesystem-mt.a \
|
||||
$(DEPSDIR)/lib/libboost_program_options-mt.a \
|
||||
$(DEPSDIR)/lib/libboost_thread-mt.a \
|
||||
$(DEPSDIR)/lib/libssl.a \
|
||||
$(DEPSDIR)/lib/libcrypto.a \
|
||||
-lz
|
||||
else
|
||||
TESTLIBS += \
|
||||
-lboost_unit_test_framework-mt
|
||||
LIBS += \
|
||||
-ldb_cxx-4.8 \
|
||||
-lboost_system-mt \
|
||||
-lboost_filesystem-mt \
|
||||
-lboost_program_options-mt \
|
||||
-lboost_thread-mt \
|
||||
-lssl \
|
||||
-lcrypto \
|
||||
-lz \
|
||||
-levent
|
||||
TESTDEFS += -DBOOST_TEST_DYN_LINK
|
||||
endif
|
||||
|
||||
DEFS=-DMAC_OSX -DMSG_NOSIGNAL=0 -DBOOST_SPIRIT_THREADSAFE
|
||||
|
||||
ifdef RELEASE
|
||||
# Compile for maximum compatibility and smallest size.
|
||||
# This requires that dependencies are compiled
|
||||
# the same way.
|
||||
CFLAGS = -mmacosx-version-min=10.5 -arch x86_64 -O3 -msse2
|
||||
else
|
||||
CFLAGS = -g -msse2
|
||||
endif
|
||||
|
||||
# triangles doesn't work because we don't support big-endian
|
||||
CFLAGS += -Wall -Wextra -Wformat -Wformat-security -Wno-unused-parameter \
|
||||
$(DEBUGFLAGS) $(DEFS) $(INCLUDEPATHS)
|
||||
|
||||
OBJS= \
|
||||
obj/address.o \
|
||||
obj/addressmap.o \
|
||||
obj/aes.o \
|
||||
obj/backtrace.o \
|
||||
obj/buffers.o \
|
||||
obj/channel.o \
|
||||
obj/channeltls.o \
|
||||
obj/circpathbias.o \
|
||||
obj/circuitbuild.o \
|
||||
obj/circuitlist.o \
|
||||
obj/circuitmux.o \
|
||||
obj/circuitmux_ewma.o \
|
||||
obj/circuitstats.o \
|
||||
obj/circuituse.o \
|
||||
obj/command.o \
|
||||
obj/compat.o \
|
||||
obj/compat_libevent.o \
|
||||
obj/config.o \
|
||||
obj/config_codedigest.o \
|
||||
obj/confparse.o \
|
||||
obj/connection.o \
|
||||
obj/connection_edge.o \
|
||||
obj/connection_or.o \
|
||||
obj/container.o \
|
||||
obj/control.o \
|
||||
obj/cpuworker.o \
|
||||
obj/crypto.o \
|
||||
obj/crypto_curve25519.o \
|
||||
obj/crypto_format.o \
|
||||
obj/curve25519-donna.o \
|
||||
obj/di_ops.o \
|
||||
obj/directory.o \
|
||||
obj/dirserv.o \
|
||||
obj/dirvote.o \
|
||||
obj/dns.o \
|
||||
obj/dnsserv.o \
|
||||
obj/entrynodes.o \
|
||||
obj/ext_orport.o \
|
||||
obj/fp_pair.o \
|
||||
obj/geoip.o \
|
||||
obj/hibernate.o \
|
||||
obj/log.o \
|
||||
obj/memarea.o \
|
||||
obj/mempool.o \
|
||||
obj/microdesc.o \
|
||||
obj/networkstatus.o \
|
||||
obj/nodelist.o \
|
||||
obj/onion.o \
|
||||
obj/onion_fast.o \
|
||||
obj/onion_main.o \
|
||||
obj/onion_ntor.o \
|
||||
obj/onion_tap.o \
|
||||
obj/policies.o \
|
||||
obj/procmon.o \
|
||||
obj/reasons.o \
|
||||
obj/relay.o \
|
||||
obj/rendclient.o \
|
||||
obj/rendcommon.o \
|
||||
obj/rendmid.o \
|
||||
obj/rendservice.o \
|
||||
obj/rephist.o \
|
||||
obj/replaycache.o \
|
||||
obj/router.o \
|
||||
obj/routerlist.o \
|
||||
obj/routerparse.o \
|
||||
obj/routerset.o \
|
||||
obj/sandbox.o \
|
||||
obj/statefile.o \
|
||||
obj/status.o \
|
||||
obj/tor_util.o \
|
||||
obj/torgzip.o \
|
||||
obj/tortls.o \
|
||||
obj/transports.o \
|
||||
obj/util_codedigest.o \
|
||||
obj/fugue.o \
|
||||
obj/hamsi.o \
|
||||
obj/groestl.o \
|
||||
obj/blake.o \
|
||||
obj/bmw.o \
|
||||
obj/skein.o \
|
||||
obj/keccak.o \
|
||||
obj/shavite.o \
|
||||
obj/jh.o \
|
||||
obj/luffa.o \
|
||||
obj/cubehash.o \
|
||||
obj/echo.o \
|
||||
obj/simd.o \
|
||||
obj/alert.o \
|
||||
obj/version.o \
|
||||
obj/checkpoints.o \
|
||||
obj/netbase.o \
|
||||
obj/addrman.o \
|
||||
obj/crypter.o \
|
||||
obj/key.o \
|
||||
obj/db.o \
|
||||
obj/init.o \
|
||||
obj/irc.o \
|
||||
obj/keystore.o \
|
||||
obj/miner.o \
|
||||
obj/main.o \
|
||||
obj/net.o \
|
||||
obj/protocol.o \
|
||||
obj/trianglesrpc.o \
|
||||
obj/rpcdump.o \
|
||||
obj/rpcnet.o \
|
||||
obj/rpcmining.o \
|
||||
obj/rpcwallet.o \
|
||||
obj/rpcblockchain.o \
|
||||
obj/rpcrawtransaction.o \
|
||||
obj/rpcsmessage.o \
|
||||
obj/script.o \
|
||||
obj/sync.o \
|
||||
obj/util.o \
|
||||
obj/wallet.o \
|
||||
obj/walletdb.o \
|
||||
obj/noui.o \
|
||||
obj/kernel.o \
|
||||
obj/pbkdf2.o \
|
||||
obj/scrypt.o \
|
||||
obj/scrypt-arm.o \
|
||||
obj/scrypt-x86.o \
|
||||
obj/scrypt-x86_64.o \
|
||||
obj/smessage.o
|
||||
ifndef USE_UPNP
|
||||
override USE_UPNP = -
|
||||
endif
|
||||
ifneq (${USE_UPNP}, -)
|
||||
DEFS += -DUSE_UPNP=$(USE_UPNP)
|
||||
ifdef STATIC
|
||||
LIBS += $(DEPSDIR)/lib/libminiupnpc.a
|
||||
else
|
||||
LIBS += -lminiupnpc
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq (${USE_IPV6}, -)
|
||||
DEFS += -DUSE_IPV6=$(USE_IPV6)
|
||||
endif
|
||||
|
||||
all: trianglesd
|
||||
|
||||
test check: test_triangles FORCE
|
||||
./test_triangles
|
||||
|
||||
LIBS += $(CURDIR)/leveldb/libleveldb.a $(CURDIR)/leveldb/libmemenv.a
|
||||
DEFS += $(addprefix -I,$(CURDIR)/leveldb/include)
|
||||
DEFS += $(addprefix -I,$(CURDIR)/leveldb/helpers)
|
||||
OBJS += obj/txdb-leveldb.o
|
||||
leveldb/libleveldb.a:
|
||||
@echo "Building LevelDB ..."; cd leveldb; make libleveldb.a libmemenv.a; cd ..;
|
||||
obj/txdb-leveldb.o: leveldb/libleveldb.a
|
||||
|
||||
# auto-generated dependencies:
|
||||
-include obj/*.P
|
||||
-include obj-test/*.P
|
||||
|
||||
obj/build.h: FORCE
|
||||
/bin/sh ../share/genbuild.sh obj/build.h
|
||||
version.cpp: obj/build.h
|
||||
DEFS += -DHAVE_BUILD_INFO
|
||||
|
||||
obj/%.o: %.cpp
|
||||
$(CXX) -c $(CFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $<
|
||||
@cp $(@:%.o=%.d) $(@:%.o=%.P); \
|
||||
sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
|
||||
-e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \
|
||||
rm -f $(@:%.o=%.d)
|
||||
|
||||
obj/%.o: %.c
|
||||
$(CC) -c $(CFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $<
|
||||
@cp $(@:%.o=%.d) $(@:%.o=%.P); \
|
||||
sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
|
||||
-e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \
|
||||
rm -f $(@:%.o=%.d)
|
||||
|
||||
obj/%.o: tor/%.c
|
||||
$(CC) -c $(CFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $<
|
||||
@cp $(@:%.o=%.d) $(@:%.o=%.P); \
|
||||
sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
|
||||
-e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \
|
||||
rm -f $(@:%.o=%.d)
|
||||
|
||||
obj/anonymize.o: tor/anonymize.cpp
|
||||
$(CXX) -c $(CFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $<
|
||||
@cp $(@:%.o=%.d) $(@:%.o=%.P); \
|
||||
sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
|
||||
-e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \
|
||||
rm -f $(@:%.o=%.d)
|
||||
|
||||
obj/scrypt-x86.o: scrypt-x86.S
|
||||
$(CXX) -c $(xCXXFLAGS) -MMD -o $@ $<
|
||||
|
||||
obj/scrypt-x86_64.o: scrypt-x86_64.S
|
||||
$(CXX) -c $(xCXXFLAGS) -MMD -o $@ $<
|
||||
|
||||
obj/scrypt-arm.o: scrypt-arm.S
|
||||
$(CXX) -c $(xCXXFLAGS) -MMD -o $@ $<
|
||||
|
||||
trianglesd: $(OBJS:obj/%=obj/%) obj/anonymize.o
|
||||
$(CXX) $(CFLAGS) -o $@ $(LIBPATHS) $^ $(LIBS)
|
||||
|
||||
TESTOBJS := $(patsubst test/%.cpp,obj-test/%.o,$(wildcard test/*.cpp))
|
||||
|
||||
obj-test/%.o: test/%.cpp
|
||||
$(CXX) -c $(TESTDEFS) $(CFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $<
|
||||
@cp $(@:%.o=%.d) $(@:%.o=%.P); \
|
||||
sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
|
||||
-e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \
|
||||
rm -f $(@:%.o=%.d)
|
||||
|
||||
test_triangles: $(TESTOBJS) $(filter-out obj/init.o,$(OBJS:obj/%=obj/%))
|
||||
$(CXX) $(CFLAGS) -o $@ $(LIBPATHS) $^ $(LIBS) $(TESTLIBS)
|
||||
|
||||
clean:
|
||||
-rm -f trianglesd test_triangles
|
||||
-rm -f obj/*.o
|
||||
-rm -f obj-test/*.o
|
||||
-rm -f obj/*.P
|
||||
-rm -f obj-test/*.P
|
||||
-rm -f obj/build.h
|
||||
-rm -f leveldb/libleveldb.a
|
||||
-rm -f leveldb/libmemenv.a
|
||||
|
||||
FORCE:
|
||||
@@ -1,329 +0,0 @@
|
||||
# Copyright (c) 2009-2010 Satoshi Nakamoto
|
||||
# Distributed under the MIT/X11 software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
USE_UPNP:=0
|
||||
USE_IPV6:=1
|
||||
|
||||
LINK:=$(CXX)
|
||||
ARCH:=$(shell lscpu | head -n 1 | awk '{print $$2}')
|
||||
|
||||
DEFS=-DBOOST_SPIRIT_THREADSAFE
|
||||
|
||||
DEFS += $(addprefix -I,$(CURDIR) $(CURDIR)/tor $(CURDIR)/obj $(BOOST_INCLUDE_PATH) $(BDB_INCLUDE_PATH) $(OPENSSL_INCLUDE_PATH))
|
||||
LIBS = $(addprefix -L,$(BOOST_LIB_PATH) $(BDB_LIB_PATH) $(OPENSSL_LIB_PATH))
|
||||
|
||||
# libevent
|
||||
EVENTDIR = /usr/local
|
||||
DEFS += $(addprefix -I,$(EVENTDIR)/include)
|
||||
LIBS += -levent $(addprefix -L,$(EVENTDIR)/lib)
|
||||
|
||||
TESTDEFS = -DTEST_DATA_DIR=$(abspath test/data)
|
||||
|
||||
LMODE = dynamic
|
||||
LMODE2 = dynamic
|
||||
ifdef STATIC
|
||||
LMODE = static
|
||||
ifeq (${STATIC}, all)
|
||||
LMODE2 = static
|
||||
endif
|
||||
else
|
||||
TESTDEFS += -DBOOST_TEST_DYN_LINK
|
||||
endif
|
||||
|
||||
# for boost 1.37, add -mt to the boost libraries
|
||||
LIBS += \
|
||||
-Wl,-B$(LMODE) \
|
||||
-l boost_system$(BOOST_LIB_SUFFIX) \
|
||||
-l boost_filesystem$(BOOST_LIB_SUFFIX) \
|
||||
-l boost_program_options$(BOOST_LIB_SUFFIX) \
|
||||
-l boost_thread$(BOOST_LIB_SUFFIX) \
|
||||
-l boost_chrono$(BOOST_LIB_SUFFIX) \
|
||||
-l db_cxx$(BDB_LIB_SUFFIX) \
|
||||
-l ssl \
|
||||
-l crypto
|
||||
|
||||
ifndef USE_UPNP
|
||||
override USE_UPNP = -
|
||||
endif
|
||||
ifneq (${USE_UPNP}, -)
|
||||
LIBS += -l miniupnpc
|
||||
DEFS += -DUSE_UPNP=$(USE_UPNP)
|
||||
endif
|
||||
|
||||
ifneq (${USE_IPV6}, -)
|
||||
DEFS += -DUSE_IPV6=$(USE_IPV6)
|
||||
endif
|
||||
|
||||
LIBS+= \
|
||||
-Wl,-B$(LMODE2) \
|
||||
-l z \
|
||||
-l dl \
|
||||
-l pthread
|
||||
|
||||
|
||||
# Hardening
|
||||
# Make some classes of vulnerabilities unexploitable in case one is discovered.
|
||||
#
|
||||
# This is a workaround for Ubuntu bug #691722, the default -fstack-protector causes
|
||||
# -fstack-protector-all to be ignored unless -fno-stack-protector is used first.
|
||||
# see: https://bugs.launchpad.net/ubuntu/+source/gcc-4.5/+bug/691722
|
||||
HARDENING=-fno-stack-protector
|
||||
|
||||
# Stack Canaries
|
||||
# Put numbers at the beginning of each stack frame and check that they are the same.
|
||||
# If a stack buffer if overflowed, it writes over the canary number and then on return
|
||||
# when that number is checked, it won't be the same and the program will exit with
|
||||
# a "Stack smashing detected" error instead of being exploited.
|
||||
HARDENING+=-fstack-protector-all -Wstack-protector
|
||||
|
||||
# Make some important things such as the global offset table read only as soon as
|
||||
# the dynamic linker is finished building it. This will prevent overwriting of addresses
|
||||
# which would later be jumped to.
|
||||
LDHARDENING+=-Wl,-z,relro -Wl,-z,now
|
||||
|
||||
# Build position independent code to take advantage of Address Space Layout Randomization
|
||||
# offered by some kernels.
|
||||
# see doc/build-unix.txt for more information.
|
||||
ifdef PIE
|
||||
HARDENING+=-fPIE
|
||||
LDHARDENING+=-pie
|
||||
endif
|
||||
|
||||
# -D_FORTIFY_SOURCE=2 does some checking for potentially exploitable code patterns in
|
||||
# the source such overflowing a statically defined buffer.
|
||||
HARDENING+=-D_FORTIFY_SOURCE=2
|
||||
#
|
||||
|
||||
|
||||
DEBUGFLAGS=-g
|
||||
|
||||
|
||||
ifeq (${ARCH}, i686)
|
||||
EXT_OPTIONS=-msse2
|
||||
endif
|
||||
|
||||
|
||||
# CXXFLAGS can be specified on the make command line, so we use xCXXFLAGS that only
|
||||
# adds some defaults in front. Unfortunately, CXXFLAGS=... $(CXXFLAGS) does not work.
|
||||
xCXXFLAGS=-O2 -std=c++17 $(EXT_OPTIONS) -pthread -Wall -Wextra -Wno-ignored-qualifiers -Wformat -Wformat-security -Wno-unused-parameter \
|
||||
$(DEBUGFLAGS) $(DEFS) $(HARDENING) $(CXXFLAGS)
|
||||
|
||||
# LDFLAGS can be specified on the make command line, so we use xLDFLAGS that only
|
||||
# adds some defaults in front. Unfortunately, LDFLAGS=... $(LDFLAGS) does not work.
|
||||
xLDFLAGS=$(LDHARDENING) $(LDFLAGS)
|
||||
|
||||
# Old embedded Tor v2 client removed - incompatible with OpenSSL 3.x
|
||||
# Tor v3 onion services handled by onion_v3.cpp via external Tor/SOCKS5
|
||||
OBJS= \
|
||||
obj/fugue.o \
|
||||
obj/hamsi.o \
|
||||
obj/hamsi_helper.o \
|
||||
obj/groestl.o \
|
||||
obj/blake.o \
|
||||
obj/bmw.o \
|
||||
obj/skein.o \
|
||||
obj/keccak.o \
|
||||
obj/shavite.o \
|
||||
obj/jh.o \
|
||||
obj/luffa.o \
|
||||
obj/cubehash.o \
|
||||
obj/echo.o \
|
||||
obj/simd.o \
|
||||
obj/aes_helper.o \
|
||||
obj/alert.o \
|
||||
obj/version.o \
|
||||
obj/checkpoints.o \
|
||||
obj/netbase.o \
|
||||
obj/addrman.o \
|
||||
obj/crypter.o \
|
||||
obj/key.o \
|
||||
obj/db.o \
|
||||
obj/init.o \
|
||||
obj/irc.o \
|
||||
obj/keystore.o \
|
||||
obj/miner.o \
|
||||
obj/main.o \
|
||||
obj/net.o \
|
||||
obj/bootstrap.o \
|
||||
obj/net_bootstrap.o \
|
||||
obj/protocol.o \
|
||||
obj/trianglesrpc.o \
|
||||
obj/rest.o \
|
||||
obj/rpcdump.o \
|
||||
obj/rpcnet.o \
|
||||
obj/rpcmining.o \
|
||||
obj/rpcwallet.o \
|
||||
obj/rpcblockchain.o \
|
||||
obj/rpcrawtransaction.o \
|
||||
obj/rpcsmessage.o \
|
||||
obj/script.o \
|
||||
obj/sync.o \
|
||||
obj/util.o \
|
||||
obj/wallet.o \
|
||||
obj/walletdb.o \
|
||||
obj/noui.o \
|
||||
obj/kernel.o \
|
||||
obj/pbkdf2.o \
|
||||
obj/scrypt.o \
|
||||
obj/scrypt-arm.o \
|
||||
obj/scrypt-x86.o \
|
||||
obj/scrypt-x86_64.o \
|
||||
obj/smessage.o \
|
||||
obj/lz4.o \
|
||||
obj/onion_v3.o \
|
||||
obj/tor_process.o \
|
||||
obj/tor_embed_hooks.o \
|
||||
obj/tor_embedded.o
|
||||
|
||||
# Embedded Tor support (optional)
|
||||
# Build with: make -f makefile.unix USE_TOR_EMBEDDED=1 TOR_SOURCE_ROOT=/path/to/tor-src
|
||||
# Requires libtor.a built from official Tor source (see CODEX-TOR-GUIDE.md)
|
||||
TOR_SOURCE_ROOT ?= tor/tor-src
|
||||
TOR_INCLUDE_PATH ?= $(TOR_SOURCE_ROOT)/src/feature/api
|
||||
TOR_LIB_PATH ?= $(TOR_SOURCE_ROOT)
|
||||
TOR_EMBEDDED_LIBS ?= -ltor
|
||||
ifdef USE_TOR_EMBEDDED
|
||||
DEFS += -DENABLE_TOR_EMBEDDED
|
||||
DEFS += $(addprefix -I,$(TOR_INCLUDE_PATH))
|
||||
LIBS += $(addprefix -L,$(TOR_LIB_PATH))
|
||||
LIBS += -Wl,--start-group $(TOR_EMBEDDED_LIBS) -Wl,--end-group
|
||||
LIBS += -llzma -lzstd -lm -lpthread
|
||||
endif
|
||||
|
||||
# ZMQ support (optional)
|
||||
# Build with: make -f makefile.unix USE_ZMQ=1
|
||||
ifdef USE_ZMQ
|
||||
DEFS += -DENABLE_ZMQ
|
||||
LIBS += -lzmq
|
||||
OBJS += obj/zmqpublishnotifier.o
|
||||
endif
|
||||
|
||||
all: trianglesd
|
||||
|
||||
obj:
|
||||
@mkdir -p obj
|
||||
|
||||
obj-test:
|
||||
@mkdir -p obj-test
|
||||
|
||||
test check: test_triangles FORCE
|
||||
./test_triangles
|
||||
|
||||
LIBS += $(CURDIR)/leveldb/libleveldb.a $(CURDIR)/leveldb/libmemenv.a
|
||||
DEFS += $(addprefix -I,$(CURDIR)/leveldb/include)
|
||||
DEFS += $(addprefix -I,$(CURDIR)/leveldb/helpers)
|
||||
OBJS += obj/txdb-leveldb.o
|
||||
leveldb/libleveldb.a:
|
||||
@echo "Building LevelDB ..."; cd leveldb; make libleveldb.a libmemenv.a; cd ..;
|
||||
obj/txdb-leveldb.o: leveldb/libleveldb.a
|
||||
|
||||
# auto-generated dependencies:
|
||||
-include obj/*.P
|
||||
-include obj-test/*.P
|
||||
|
||||
obj/build.h: FORCE
|
||||
/bin/sh ../share/genbuild.sh obj/build.h
|
||||
version.cpp: obj/build.h
|
||||
DEFS += -DHAVE_BUILD_INFO
|
||||
|
||||
obj/scrypt-x86.o: scrypt-x86.S
|
||||
$(CXX) -c $(xCXXFLAGS) -MMD -o $@ $<
|
||||
|
||||
obj/scrypt-x86_64.o: scrypt-x86_64.S
|
||||
$(CXX) -c $(xCXXFLAGS) -MMD -o $@ $<
|
||||
|
||||
obj/scrypt-arm.o: scrypt-arm.S
|
||||
$(CXX) -c $(xCXXFLAGS) -MMD -o $@ $<
|
||||
|
||||
obj/%.o: %.cpp | obj
|
||||
$(CXX) -c $(xCXXFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $<
|
||||
@cp $(@:%.o=%.d) $(@:%.o=%.P); \
|
||||
sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
|
||||
-e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \
|
||||
rm -f $(@:%.o=%.d)
|
||||
|
||||
obj/%.o: %.c | obj
|
||||
$(CXX) -c $(xCXXFLAGS) -fpermissive -MMD -MF $(@:%.o=%.d) -o $@ $<
|
||||
@cp $(@:%.o=%.d) $(@:%.o=%.P); \
|
||||
sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
|
||||
-e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \
|
||||
rm -f $(@:%.o=%.d)
|
||||
|
||||
obj/lz4.o: lz4/lz4.c
|
||||
$(CXX) -c $(xCXXFLAGS) -fpermissive -MMD -MF $(@:%.o=%.d) -o $@ $<
|
||||
@cp $(@:%.o=%.d) $(@:%.o=%.P); \
|
||||
sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//'\
|
||||
-e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \
|
||||
rm -f $(@:%.o=%.d)
|
||||
|
||||
obj/tor_embed_hooks.o: tor_embed_hooks.cpp
|
||||
$(CXX) -c $(xCXXFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $<
|
||||
@cp $(@:%.o=%.d) $(@:%.o=%.P); \
|
||||
sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
|
||||
-e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \
|
||||
rm -f $(@:%.o=%.d)
|
||||
|
||||
obj/onion_v3.o: tor/onion_v3.cpp
|
||||
$(CXX) -c $(xCXXFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $<
|
||||
@cp $(@:%.o=%.d) $(@:%.o=%.P); \
|
||||
sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
|
||||
-e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \
|
||||
rm -f $(@:%.o=%.d)
|
||||
|
||||
obj/tor_process.o: tor/tor_process.cpp
|
||||
$(CXX) -c $(xCXXFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $<
|
||||
@cp $(@:%.o=%.d) $(@:%.o=%.P); \
|
||||
sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
|
||||
-e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \
|
||||
rm -f $(@:%.o=%.d)
|
||||
|
||||
obj/tor_embedded.o: tor/tor_embedded.cpp
|
||||
$(CXX) -c $(xCXXFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $<
|
||||
@cp $(@:%.o=%.d) $(@:%.o=%.P); \
|
||||
sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
|
||||
-e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \
|
||||
rm -f $(@:%.o=%.d)
|
||||
|
||||
obj/bootstrap.o: bootstrap.cpp
|
||||
$(CXX) -c $(xCXXFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $<
|
||||
@cp $(@:%.o=%.d) $(@:%.o=%.P); \
|
||||
sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
|
||||
-e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \
|
||||
rm -f $(@:%.o=%.d)
|
||||
|
||||
obj/net_bootstrap.o: net_bootstrap.cpp
|
||||
$(CXX) -c $(xCXXFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $<
|
||||
@cp $(@:%.o=%.d) $(@:%.o=%.P); \
|
||||
sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
|
||||
-e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \
|
||||
rm -f $(@:%.o=%.d)
|
||||
|
||||
trianglesd: $(OBJS:obj/%=obj/%)
|
||||
$(LINK) $(xCXXFLAGS) -o $@ $^ $(xLDFLAGS) $(LIBS)
|
||||
|
||||
# miner_tests.cpp references CreateNewBlock() which was never ported from Bitcoin
|
||||
TESTOBJS := $(filter-out obj-test/miner_tests.o, \
|
||||
$(patsubst test/%.cpp,obj-test/%.o,$(wildcard test/*.cpp)))
|
||||
|
||||
obj-test/%.o: test/%.cpp | obj-test
|
||||
$(CXX) -c $(TESTDEFS) $(xCXXFLAGS) -MMD -MF $(@:%.o=%.d) -o $@ $<
|
||||
@cp $(@:%.o=%.d) $(@:%.o=%.P); \
|
||||
sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
|
||||
-e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \
|
||||
rm -f $(@:%.o=%.d)
|
||||
|
||||
test_triangles: $(TESTOBJS) $(filter-out obj/init.o,$(OBJS:obj/%=obj/%))
|
||||
$(LINK) $(xCXXFLAGS) -o $@ $(LIBPATHS) $^ -Wl,-B$(LMODE) -lboost_unit_test_framework $(xLDFLAGS) $(LIBS)
|
||||
|
||||
clean:
|
||||
-rm -f trianglesd test_triangles
|
||||
-rm -f obj/*.o
|
||||
-rm -f obj-test/*.o
|
||||
-rm -f obj/*.P
|
||||
-rm -f obj-test/*.P
|
||||
-rm -f obj/build.h
|
||||
-rm -f leveldb/libleveldb.a
|
||||
-rm -f leveldb/libmemenv.a
|
||||
|
||||
FORCE:
|
||||
@@ -17,41 +17,8 @@ using namespace std;
|
||||
|
||||
//
|
||||
|
||||
int static FormatHashBlocks(void* pbuffer, unsigned int len)
|
||||
{
|
||||
unsigned char* pdata = (unsigned char*)pbuffer;
|
||||
unsigned int blocks = 1 + ((len + 8) / 64);
|
||||
unsigned char* pend = pdata + 64 * blocks;
|
||||
memset(pdata + len, 0, 64 * blocks - len);
|
||||
pdata[len] = 0x80;
|
||||
unsigned int bits = len * 8;
|
||||
pend[-1] = (bits >> 0) & 0xff;
|
||||
pend[-2] = (bits >> 8) & 0xff;
|
||||
pend[-3] = (bits >> 16) & 0xff;
|
||||
pend[-4] = (bits >> 24) & 0xff;
|
||||
return blocks;
|
||||
}
|
||||
|
||||
static const unsigned int pSHA256InitState[8] =
|
||||
{0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19};
|
||||
|
||||
void SHA256Transform(void* pstate, void* pinput, const void* pinit)
|
||||
{
|
||||
SHA256_CTX ctx;
|
||||
unsigned char data[64];
|
||||
|
||||
SHA256_Init(&ctx);
|
||||
|
||||
for (int i = 0; i < 16; i++)
|
||||
((uint32_t*)data)[i] = ByteReverse(((uint32_t*)pinput)[i]);
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
ctx.h[i] = ((uint32_t*)pinit)[i];
|
||||
|
||||
SHA256_Update(&ctx, data, sizeof(data));
|
||||
for (int i = 0; i < 8; i++)
|
||||
((uint32_t*)pstate)[i] = ctx.h[i];
|
||||
}
|
||||
// PoW mining helpers (SHA256Transform, FormatHashBlocks, FormatHashBuffers,
|
||||
// IncrementExtraNonce, CheckWork) removed - PoW ended at block 9000.
|
||||
|
||||
// Some explaining would be appreciated
|
||||
class COrphan
|
||||
@@ -247,7 +214,6 @@ CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake, int64_t* pFees)
|
||||
}
|
||||
|
||||
// Collect transactions into block
|
||||
map<uint256, CTxIndex> mapTestPool;
|
||||
uint64_t nBlockSize = 1000;
|
||||
uint64_t nBlockTx = 0;
|
||||
int nBlockSigOps = 100;
|
||||
@@ -299,10 +265,10 @@ CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake, int64_t* pFees)
|
||||
|
||||
// Connecting shouldn't fail due to dependency on other memory pool transactions
|
||||
// because we're already processing them in order of dependency
|
||||
map<uint256, CTxIndex> mapTestPoolTmp(mapTestPool);
|
||||
MapPrevTx mapInputs;
|
||||
MapPrevTx mapEmpty;
|
||||
bool fInvalid;
|
||||
if (!tx.FetchInputs(txdb, mapTestPoolTmp, false, true, mapInputs, fInvalid))
|
||||
if (!tx.FetchInputs(txdb, mapEmpty, false, true, mapInputs, fInvalid))
|
||||
continue;
|
||||
|
||||
int64_t nTxFees = tx.GetValueIn(mapInputs)-tx.GetValueOut();
|
||||
@@ -313,10 +279,8 @@ CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake, int64_t* pFees)
|
||||
if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
|
||||
continue;
|
||||
|
||||
if (!tx.ConnectInputs(txdb, mapInputs, mapTestPoolTmp, CDiskTxPos(1,1,1), pindexPrev, false, true))
|
||||
if (!tx.ConnectInputs(txdb, mapInputs, pindexPrev, false, true))
|
||||
continue;
|
||||
mapTestPoolTmp[tx.GetHash()] = CTxIndex(CDiskTxPos(1,1,1), tx.vout.size());
|
||||
swap(mapTestPool, mapTestPoolTmp);
|
||||
|
||||
// Added
|
||||
pblock->vtx.push_back(tx);
|
||||
@@ -375,110 +339,6 @@ CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake, int64_t* pFees)
|
||||
}
|
||||
|
||||
|
||||
void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce)
|
||||
{
|
||||
// Update nExtraNonce
|
||||
static uint256 hashPrevBlock;
|
||||
if (hashPrevBlock != pblock->hashPrevBlock)
|
||||
{
|
||||
nExtraNonce = 0;
|
||||
hashPrevBlock = pblock->hashPrevBlock;
|
||||
}
|
||||
++nExtraNonce;
|
||||
|
||||
unsigned int nHeight = pindexPrev->nHeight+1; // Height first in coinbase required for block.version=2
|
||||
pblock->vtx[0].vin[0].scriptSig = (CScript() << nHeight << CBigNum(nExtraNonce)) + COINBASE_FLAGS;
|
||||
assert(pblock->vtx[0].vin[0].scriptSig.size() <= 100);
|
||||
|
||||
pblock->hashMerkleRoot = pblock->BuildMerkleTree();
|
||||
}
|
||||
|
||||
|
||||
void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash1)
|
||||
{
|
||||
//
|
||||
// Pre-build hash buffers
|
||||
//
|
||||
struct
|
||||
{
|
||||
struct unnamed2
|
||||
{
|
||||
int nVersion;
|
||||
uint256 hashPrevBlock;
|
||||
uint256 hashMerkleRoot;
|
||||
unsigned int nTime;
|
||||
unsigned int nBits;
|
||||
unsigned int nNonce;
|
||||
}
|
||||
block;
|
||||
unsigned char pchPadding0[64];
|
||||
uint256 hash1;
|
||||
unsigned char pchPadding1[64];
|
||||
}
|
||||
tmp;
|
||||
memset(&tmp, 0, sizeof(tmp));
|
||||
|
||||
tmp.block.nVersion = pblock->nVersion;
|
||||
tmp.block.hashPrevBlock = pblock->hashPrevBlock;
|
||||
tmp.block.hashMerkleRoot = pblock->hashMerkleRoot;
|
||||
tmp.block.nTime = pblock->nTime;
|
||||
tmp.block.nBits = pblock->nBits;
|
||||
tmp.block.nNonce = pblock->nNonce;
|
||||
|
||||
FormatHashBlocks(&tmp.block, sizeof(tmp.block));
|
||||
FormatHashBlocks(&tmp.hash1, sizeof(tmp.hash1));
|
||||
|
||||
// Byte swap all the input buffer
|
||||
for (unsigned int i = 0; i < sizeof(tmp)/4; i++)
|
||||
((unsigned int*)&tmp)[i] = ByteReverse(((unsigned int*)&tmp)[i]);
|
||||
|
||||
// Precalc the first half of the first hash, which stays constant
|
||||
SHA256Transform(pmidstate, &tmp.block, pSHA256InitState);
|
||||
|
||||
memcpy(pdata, &tmp.block, 128);
|
||||
memcpy(phash1, &tmp.hash1, 64);
|
||||
}
|
||||
|
||||
|
||||
bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey)
|
||||
{
|
||||
uint256 hash = pblock->GetHash();
|
||||
uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();
|
||||
|
||||
if(!pblock->IsProofOfWork())
|
||||
return error("CheckWork() : %s is not a proof-of-work block", hash.GetHex().c_str());
|
||||
|
||||
if (hash > hashTarget && pblock->IsProofOfWork())
|
||||
return error("CheckWork() : proof-of-work not meeting target");
|
||||
|
||||
//// debug print
|
||||
printf("CheckWork() : new proof-of-work block found \n hash: %s \ntarget: %s\n", hash.GetHex().c_str(), hashTarget.GetHex().c_str());
|
||||
pblock->print();
|
||||
printf("generated %s\n", FormatMoney(pblock->vtx[0].vout[0].nValue).c_str());
|
||||
|
||||
// Found a solution
|
||||
{
|
||||
LOCK(cs_main);
|
||||
if (pblock->hashPrevBlock != hashBestChain)
|
||||
return error("CheckWork() : generated block is stale");
|
||||
|
||||
// Remove key from key pool
|
||||
reservekey.KeepKey();
|
||||
|
||||
// Track how many getdata requests this block gets
|
||||
{
|
||||
LOCK(wallet.cs_wallet);
|
||||
wallet.mapRequestCount[hash] = 0;
|
||||
}
|
||||
|
||||
// Process this block the same as if we had received it from another node
|
||||
if (!ProcessBlock(NULL, pblock))
|
||||
return error("CheckWork() : ProcessBlock, block not accepted");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CheckStake(CBlock* pblock, CWallet& wallet)
|
||||
{
|
||||
uint256 proofHash = 0, hashTarget = 0;
|
||||
@@ -487,8 +347,10 @@ bool CheckStake(CBlock* pblock, CWallet& wallet)
|
||||
if(!pblock->IsProofOfStake())
|
||||
return error("CheckStake() : %s is not a proof-of-stake block", hash.GetHex().c_str());
|
||||
|
||||
if (pblock->vtx.size() < 2)
|
||||
return error("CheckStake() : block has no coinstake transaction");
|
||||
|
||||
// verify hash target and signature of coinstake tx
|
||||
//bool fIsInitialDownload = IsInitialBlockDownload();
|
||||
if (!CheckProofOfStake(pblock->vtx[1], pblock->nBits, proofHash, hashTarget))
|
||||
return error("CheckStake() : proof-of-stake checking failed");
|
||||
|
||||
@@ -551,7 +413,7 @@ void StakeMiner(CWallet *pwallet)
|
||||
if (fTryToSync)
|
||||
{
|
||||
fTryToSync = false;
|
||||
if (vNodes.size() < 1 || nBestHeight < GetNumBlocksOfPeers())
|
||||
if (vNodes.size() < 2 || nBestHeight < GetNumBlocksOfPeers())
|
||||
{
|
||||
MilliSleep(60000);
|
||||
continue;
|
||||
@@ -574,16 +436,30 @@ void StakeMiner(CWallet *pwallet)
|
||||
int64_t nFees;
|
||||
unique_ptr<CBlock> pblock(CreateNewBlock(pwallet, true, &nFees));
|
||||
if (!pblock.get())
|
||||
return;
|
||||
{
|
||||
MilliSleep(5000);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try to sign the block
|
||||
if (pblock->SignBlock(*pwallet, nFees))
|
||||
{
|
||||
printf("StakeMiner(): A proof-of-stake block has been found! %s\n", pblock->GetHash().ToString().c_str());
|
||||
SetThreadPriority(THREAD_PRIORITY_NORMAL);
|
||||
CheckStake(pblock.get(), *pwallet);
|
||||
bool fAccepted = CheckStake(pblock.get(), *pwallet);
|
||||
SetThreadPriority(THREAD_PRIORITY_LOWEST);
|
||||
MilliSleep(500);
|
||||
if (fAccepted)
|
||||
{
|
||||
MilliSleep(500);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Block was orphaned or rejected — apply a cooldown to reduce
|
||||
// fork oscillation. Without this, the staker immediately retries
|
||||
// with a different timestamp, potentially creating competing forks.
|
||||
printf("StakeMiner(): block not accepted, cooldown 30s\n");
|
||||
MilliSleep(30000);
|
||||
}
|
||||
}
|
||||
else
|
||||
MilliSleep(500);
|
||||
|
||||
@@ -12,21 +12,9 @@
|
||||
/* Generate a new block, without valid proof-of-work */
|
||||
CBlock* CreateNewBlock(CWallet* pwallet, bool fProofOfStake=false, int64_t* pFees = 0);
|
||||
|
||||
/** Modify the extranonce in a block */
|
||||
void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce);
|
||||
|
||||
/** Do mining precalculation */
|
||||
void FormatHashBuffers(CBlock* pblock, char* pmidstate, char* pdata, char* phash1);
|
||||
|
||||
/** Check mined proof-of-work block */
|
||||
bool CheckWork(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey);
|
||||
|
||||
/** Check mined proof-of-stake block */
|
||||
bool CheckStake(CBlock* pblock, CWallet& wallet);
|
||||
|
||||
/** Base sha256 mining transform */
|
||||
void SHA256Transform(void* pstate, void* pinput, const void* pinit);
|
||||
|
||||
#endif // TRIANGLES_MINER_H
|
||||
|
||||
|
||||
|
||||
@@ -13,7 +13,9 @@
|
||||
#include "ui_interface.h"
|
||||
#include "onionseed.h"
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#ifdef WIN32
|
||||
@@ -35,7 +37,7 @@ extern "C" {
|
||||
// int tor_main(int argc, char *argv[]);
|
||||
}
|
||||
|
||||
static const int MAX_OUTBOUND_CONNECTIONS = 16;
|
||||
static const int MAX_OUTBOUND_CONNECTIONS = 8; // reduced from 16 for Tor-only small networks
|
||||
|
||||
void ThreadMessageHandler2(void* parg);
|
||||
void ThreadSocketHandler2(void* parg);
|
||||
@@ -45,7 +47,7 @@ void ThreadOpenAddedConnections2(void* parg);
|
||||
void ThreadMapPort2(void* parg);
|
||||
#endif
|
||||
void ThreadHTTPSeedFetch(void* parg);
|
||||
void ThreadHTTPSeedFetch2(void* parg);
|
||||
bool ThreadHTTPSeedFetch2(void* parg);
|
||||
bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false);
|
||||
|
||||
|
||||
@@ -60,8 +62,11 @@ struct LocalServiceInfo {
|
||||
bool fClient = false;
|
||||
//bool fDiscover = true;
|
||||
|
||||
#ifdef USE_UPNP
|
||||
bool fUseUPnP = GetBoolArg("-upnp", USE_UPNP);
|
||||
//bool fUseUPnP = false;
|
||||
#else
|
||||
bool fUseUPnP = false;
|
||||
#endif
|
||||
uint64_t nLocalServices = (fClient ? 0 : NODE_NETWORK);
|
||||
static CCriticalSection cs_mapLocalHost;
|
||||
static map<CNetAddr, LocalServiceInfo> mapLocalHost;
|
||||
@@ -490,6 +495,14 @@ CNode* FindNode(const CService& addr)
|
||||
|
||||
CNode* ConnectNode(CAddress addrConnect, const char *pszDest)
|
||||
{
|
||||
// TOR-NATIVE: Reject all non-.onion addresses
|
||||
std::string addrStr = pszDest ? std::string(pszDest) : addrConnect.ToStringIP();
|
||||
if (addrStr.find(".onion") == std::string::npos) {
|
||||
if (fDebug)
|
||||
printf("ConnectNode(): REJECTED non-onion address: %s (Tor-native mode)\n", addrStr.c_str());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (pszDest == NULL) {
|
||||
if (IsLocal(addrConnect))
|
||||
return NULL;
|
||||
@@ -610,6 +623,32 @@ bool CNode::IsBanned(CNetAddr ip)
|
||||
return fResult;
|
||||
}
|
||||
|
||||
bool CNode::Ban(CNetAddr ip, int64_t banTime)
|
||||
{
|
||||
if (ip.IsLocal())
|
||||
return false;
|
||||
|
||||
LOCK(cs_setBanned);
|
||||
std::map<CNetAddr, int64_t>::iterator it = setBanned.find(ip);
|
||||
if (it != setBanned.end() && it->second >= banTime)
|
||||
return false;
|
||||
|
||||
setBanned[ip] = banTime;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CNode::Unban(CNetAddr ip)
|
||||
{
|
||||
LOCK(cs_setBanned);
|
||||
return setBanned.erase(ip) != 0;
|
||||
}
|
||||
|
||||
void CNode::GetBanned(std::map<CNetAddr, int64_t>& mapBannedOut)
|
||||
{
|
||||
LOCK(cs_setBanned);
|
||||
mapBannedOut = setBanned;
|
||||
}
|
||||
|
||||
bool CNode::Misbehaving(int howmuch)
|
||||
{
|
||||
if (addr.IsLocal())
|
||||
@@ -651,6 +690,9 @@ void CNode::copyStats(CNodeStats &stats)
|
||||
X(fInbound);
|
||||
X(nStartingHeight);
|
||||
X(nMisbehavior);
|
||||
X(nPingUsecTime);
|
||||
X(nBlocksDelivered);
|
||||
X(nAvgBlockLatencyUs);
|
||||
}
|
||||
#undef X
|
||||
|
||||
@@ -990,10 +1032,6 @@ void ThreadSocketHandler2(void* parg)
|
||||
if (nErr != WSAEWOULDBLOCK)
|
||||
printf("socket error accept failed: %d\n", nErr);
|
||||
}
|
||||
else if (nInbound >= GetArg("-maxconnections", 125) - MAX_OUTBOUND_CONNECTIONS)
|
||||
{
|
||||
closesocket(hSocket);
|
||||
}
|
||||
else if (CNode::IsBanned(addr))
|
||||
{
|
||||
printf("connection from %s dropped (banned)\n", addr.ToString().c_str());
|
||||
@@ -1001,12 +1039,36 @@ void ThreadSocketHandler2(void* parg)
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("accepted connection %s\n", addr.ToString().c_str());
|
||||
CNode* pnode = new CNode(hSocket, addr, "", true);
|
||||
pnode->AddRef();
|
||||
{
|
||||
LOCK(cs_vNodes);
|
||||
vNodes.push_back(pnode);
|
||||
int nMaxInbound = GetArg("-maxconnections", 125) - MAX_OUTBOUND_CONNECTIONS;
|
||||
bool fAccept = (nInbound < nMaxInbound);
|
||||
|
||||
// Reserve 2 extra inbound slots for known seed nodes
|
||||
if (!fAccept) {
|
||||
bool fIsSeed = false;
|
||||
static const char *(*strOnionSeedCheck)[1] = fTestNet ? strTestNetOnionSeed : strMainNetOnionSeed;
|
||||
std::string incomingAddr = addr.ToStringIP();
|
||||
for (unsigned int si = 0; strOnionSeedCheck[si][0] != NULL; si++) {
|
||||
if (incomingAddr.find(strOnionSeedCheck[si][0]) != std::string::npos) {
|
||||
fIsSeed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (fIsSeed && nInbound < nMaxInbound + 2) {
|
||||
fAccept = true;
|
||||
printf("accepted seed node %s (reserved slot)\n", addr.ToString().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if (fAccept) {
|
||||
printf("accepted connection %s\n", addr.ToString().c_str());
|
||||
CNode* pnode = new CNode(hSocket, addr, "", true);
|
||||
pnode->AddRef();
|
||||
{
|
||||
LOCK(cs_vNodes);
|
||||
vNodes.push_back(pnode);
|
||||
}
|
||||
} else {
|
||||
closesocket(hSocket);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1098,14 +1160,14 @@ void ThreadSocketHandler2(void* parg)
|
||||
printf("socket no message in first 60 seconds, %d %d\n", pnode->nLastRecv != 0, pnode->nLastSend != 0);
|
||||
pnode->fDisconnect = true;
|
||||
}
|
||||
else if (GetTime() - pnode->nLastSend > 90*60 && GetTime() - pnode->nLastSendEmpty > 90*60)
|
||||
else if (GetTime() - pnode->nLastSend > 10*60 && GetTime() - pnode->nLastSendEmpty > 10*60)
|
||||
{
|
||||
printf("socket not sending\n");
|
||||
printf("socket not sending (10min timeout)\n");
|
||||
pnode->fDisconnect = true;
|
||||
}
|
||||
else if (GetTime() - pnode->nLastRecv > 90*60)
|
||||
else if (GetTime() - pnode->nLastRecv > 10*60)
|
||||
{
|
||||
printf("socket inactivity timeout\n");
|
||||
printf("socket inactivity timeout (10min)\n");
|
||||
pnode->fDisconnect = true;
|
||||
}
|
||||
}
|
||||
@@ -1342,40 +1404,119 @@ void ThreadOnionSeed(void* parg)
|
||||
|
||||
|
||||
|
||||
// Load hardcoded .onion seeds and queue them for immediate direct connection
|
||||
static const char *(*strOnionSeed)[1] = fTestNet ? strTestNetOnionSeed : strMainNetOnionSeed;
|
||||
|
||||
|
||||
int found = 0;
|
||||
|
||||
printf("Loading addresses from .onion seeds\n");
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
for (unsigned int seed_idx = 0; strOnionSeed[seed_idx][0] != NULL; seed_idx++) {
|
||||
CNetAddr parsed;
|
||||
if (
|
||||
!parsed.SetSpecial(
|
||||
strOnionSeed[seed_idx][0]
|
||||
)
|
||||
) {
|
||||
if (!parsed.SetSpecial(strOnionSeed[seed_idx][0]))
|
||||
throw runtime_error("ThreadOnionSeed() : invalid .onion seed");
|
||||
}
|
||||
|
||||
int nOneDay = 24*3600;
|
||||
CAddress addr = CAddress(CService(parsed, GetDefaultPort()));
|
||||
addr.nTime = GetTime() - 3*nOneDay - GetRand(4*nOneDay); // use a random age between 3 and 7 days old
|
||||
|
||||
found++;
|
||||
addr.nTime = GetTime() - 3*nOneDay - GetRand(4*nOneDay);
|
||||
addrman.Add(addr, parsed);
|
||||
|
||||
|
||||
|
||||
|
||||
// Queue for immediate direct connection (OneShot) — don't wait for
|
||||
// addrman selection which deprioritizes stale timestamps
|
||||
std::string oneShotAddr = std::string(strOnionSeed[seed_idx][0])
|
||||
+ ":" + std::to_string(GetDefaultPort());
|
||||
AddOneShot(oneShotAddr);
|
||||
found++;
|
||||
}
|
||||
|
||||
printf("%d addresses found from .onion seeds\n", found);
|
||||
printf("%d addresses from hardcoded .onion seeds (queued as OneShot)\n", found);
|
||||
|
||||
// Wait for Tor to establish circuits before attempting HTTPS seed fetch.
|
||||
// The hardcoded OneShot connections can race ahead meanwhile.
|
||||
printf("ThreadOnionSeed: waiting 20s for Tor circuits before HTTPS seed fetch...\n");
|
||||
for (int i = 0; i < 20 && !fShutdown; i++)
|
||||
MilliSleep(1000);
|
||||
|
||||
// Fetch dynamic seeds with retry — up to 4 attempts with increasing backoff.
|
||||
// This is the primary discovery mechanism — seeds.cryptographic-triangles.org
|
||||
{
|
||||
bool ok = false;
|
||||
int delays[] = {0, 30, 60, 120};
|
||||
for (int attempt = 0; attempt < 4 && !ok && !fShutdown; attempt++) {
|
||||
if (attempt > 0) {
|
||||
printf("ThreadOnionSeed: HTTPS seed fetch retry %d in %ds...\n", attempt, delays[attempt]);
|
||||
for (int i = 0; i < delays[attempt] && !fShutdown; i++)
|
||||
MilliSleep(1000);
|
||||
}
|
||||
if (!fShutdown)
|
||||
ok = ThreadHTTPSeedFetch2(NULL);
|
||||
}
|
||||
if (!ok && !fShutdown)
|
||||
printf("ThreadOnionSeed: all HTTPS seed fetch attempts failed\n");
|
||||
}
|
||||
|
||||
printf("ThreadOnionSeed: initial seeding complete\n");
|
||||
|
||||
// Periodic re-seeding for isolated or under-connected nodes.
|
||||
// EMERGENCY MODE: When 0 outbound peers, check every 15 seconds
|
||||
// NORMAL MODE: Check every 2 minutes, re-seed when < 2 outbound peers
|
||||
int64_t nLastReseed = GetTime();
|
||||
bool bFirstReseed = true;
|
||||
while (!fShutdown) {
|
||||
// Count outbound peers to determine check interval
|
||||
int nOutbound = 0;
|
||||
{
|
||||
LOCK(cs_vNodes);
|
||||
for (CNode* pnode : vNodes)
|
||||
if (!pnode->fInbound)
|
||||
nOutbound++;
|
||||
}
|
||||
|
||||
// Emergency mode: 0 peers = check every 15 seconds
|
||||
// Low mode: 1 peer = check every 30 seconds
|
||||
// Normal: 2+ peers = check every 2 minutes
|
||||
int nSleepSeconds = (nOutbound == 0) ? 15 : (nOutbound < 2) ? 30 : 120;
|
||||
for (int i = 0; i < nSleepSeconds && !fShutdown; i++)
|
||||
MilliSleep(1000);
|
||||
|
||||
if (fShutdown) break;
|
||||
|
||||
// Recount after sleep
|
||||
nOutbound = 0;
|
||||
{
|
||||
LOCK(cs_vNodes);
|
||||
for (CNode* pnode : vNodes)
|
||||
if (!pnode->fInbound)
|
||||
nOutbound++;
|
||||
}
|
||||
|
||||
// Emergency (0 peers): no cooldown, reseed immediately
|
||||
// Low (1 peer): 60 second cooldown
|
||||
// Normal (<2): 5 min first, 15 min subsequent
|
||||
int64_t nCooldown;
|
||||
if (nOutbound == 0)
|
||||
nCooldown = 0; // immediate
|
||||
else if (nOutbound < 2)
|
||||
nCooldown = bFirstReseed ? 60 : 5 * 60;
|
||||
else
|
||||
nCooldown = bFirstReseed ? 5 * 60 : 15 * 60;
|
||||
|
||||
if (nOutbound < 2 && GetTime() - nLastReseed > nCooldown) {
|
||||
if (nOutbound == 0)
|
||||
printf("ThreadOnionSeed: EMERGENCY - 0 outbound peers, re-seeding immediately!\n");
|
||||
else
|
||||
printf("ThreadOnionSeed: low outbound peers (%d), re-seeding...\n", nOutbound);
|
||||
|
||||
ThreadHTTPSeedFetch2(NULL);
|
||||
|
||||
// Re-queue hardcoded seeds for direct connection
|
||||
for (unsigned int seed_idx = 0; strOnionSeed[seed_idx][0] != NULL; seed_idx++) {
|
||||
std::string oneShotAddr = std::string(strOnionSeed[seed_idx][0])
|
||||
+ ":" + std::to_string(GetDefaultPort());
|
||||
AddOneShot(oneShotAddr);
|
||||
}
|
||||
|
||||
nLastReseed = GetTime();
|
||||
bFirstReseed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1433,11 +1574,11 @@ void ThreadDumpAddress(void* parg)
|
||||
printf("ThreadDumpAddress exited\n");
|
||||
}
|
||||
|
||||
void ThreadHTTPSeedFetch2(void* parg)
|
||||
bool ThreadHTTPSeedFetch2(void* parg)
|
||||
{
|
||||
static const char* DEFAULT_SEED_URL_HOST = "seeds.cryptographic-triangles.org";
|
||||
static const char* DEFAULT_SEED_URL_PATH = "/seeds.txt";
|
||||
static const int HTTP_PORT = 80;
|
||||
static const int HTTPS_PORT = 443;
|
||||
|
||||
std::string seedHost = GetArg("-seedurl", DEFAULT_SEED_URL_HOST);
|
||||
std::string seedPath = DEFAULT_SEED_URL_PATH;
|
||||
@@ -1449,70 +1590,123 @@ void ThreadHTTPSeedFetch2(void* parg)
|
||||
seedHost = seedHost.substr(0, slashPos);
|
||||
}
|
||||
|
||||
printf("Fetching seed list from http://%s%s ...\n", seedHost.c_str(), seedPath.c_str());
|
||||
printf("Fetching seed list from https://%s%s (via Tor)...\n", seedHost.c_str(), seedPath.c_str());
|
||||
|
||||
SSL_CTX* ctx = NULL;
|
||||
SSL* ssl = NULL;
|
||||
SOCKET hSocket = INVALID_SOCKET;
|
||||
|
||||
try {
|
||||
boost::asio::io_context io_context;
|
||||
boost::asio::ip::tcp::resolver resolver(io_context);
|
||||
// Connect through Tor SOCKS proxy using existing proxy-aware socket infrastructure
|
||||
CService addrResolved;
|
||||
std::string connectDest = seedHost + ":" + std::to_string(HTTPS_PORT);
|
||||
|
||||
boost::system::error_code resolve_ec;
|
||||
auto endpoints = resolver.resolve(seedHost, std::to_string(HTTP_PORT), resolve_ec);
|
||||
if (resolve_ec) {
|
||||
printf("HTTP seed fetch: cannot resolve %s (%s)\n", seedHost.c_str(), resolve_ec.message().c_str());
|
||||
return;
|
||||
if (!ConnectSocketByName(addrResolved, hSocket, connectDest.c_str(), HTTPS_PORT, nConnectTimeout)) {
|
||||
printf("HTTPS seed fetch: cannot connect to %s through Tor proxy\n", seedHost.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
boost::asio::ip::tcp::socket socket(io_context);
|
||||
boost::asio::connect(socket, endpoints);
|
||||
// Set up TLS over the connected socket
|
||||
ctx = SSL_CTX_new(TLS_client_method());
|
||||
if (!ctx) {
|
||||
printf("HTTPS seed fetch: SSL_CTX_new failed\n");
|
||||
closesocket(hSocket);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Use system default CA certificates for verification
|
||||
SSL_CTX_set_default_verify_paths(ctx);
|
||||
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
|
||||
|
||||
ssl = SSL_new(ctx);
|
||||
if (!ssl) {
|
||||
printf("HTTPS seed fetch: SSL_new failed\n");
|
||||
SSL_CTX_free(ctx);
|
||||
closesocket(hSocket);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set SNI hostname (required for Caddy/Let's Encrypt)
|
||||
SSL_set_tlsext_host_name(ssl, seedHost.c_str());
|
||||
SSL_set_fd(ssl, (int)hSocket);
|
||||
|
||||
int ret = SSL_connect(ssl);
|
||||
if (ret != 1) {
|
||||
int sslErr = SSL_get_error(ssl, ret);
|
||||
unsigned long errCode = ERR_get_error();
|
||||
char errBuf[256];
|
||||
ERR_error_string_n(errCode, errBuf, sizeof(errBuf));
|
||||
printf("HTTPS seed fetch: TLS handshake failed (ssl_err=%d): %s\n", sslErr, errBuf);
|
||||
SSL_free(ssl);
|
||||
SSL_CTX_free(ctx);
|
||||
closesocket(hSocket);
|
||||
return false;
|
||||
}
|
||||
|
||||
printf("HTTPS seed fetch: TLS connection established to %s\n", seedHost.c_str());
|
||||
|
||||
// Send HTTP request over TLS
|
||||
std::string request =
|
||||
"GET " + seedPath + " HTTP/1.1\r\n"
|
||||
"Host: " + seedHost + "\r\n"
|
||||
"Connection: close\r\n"
|
||||
"User-Agent: Triangles\r\n"
|
||||
"\r\n";
|
||||
boost::asio::write(socket, boost::asio::buffer(request));
|
||||
|
||||
// Read response
|
||||
boost::asio::streambuf response_buf;
|
||||
boost::asio::read_until(socket, response_buf, "\r\n\r\n");
|
||||
|
||||
std::istream response_stream(&response_buf);
|
||||
std::string http_version;
|
||||
unsigned int status_code = 0;
|
||||
response_stream >> http_version >> status_code;
|
||||
std::string status_message;
|
||||
std::getline(response_stream, status_message);
|
||||
|
||||
if (status_code != 200) {
|
||||
printf("HTTP seed fetch: got status %u from %s\n", status_code, seedHost.c_str());
|
||||
return;
|
||||
int nSent = 0;
|
||||
int nLen = request.size();
|
||||
while (nSent < nLen) {
|
||||
int nBytes = SSL_write(ssl, request.c_str() + nSent, nLen - nSent);
|
||||
if (nBytes <= 0) {
|
||||
printf("HTTPS seed fetch: SSL_write failed\n");
|
||||
SSL_shutdown(ssl);
|
||||
SSL_free(ssl);
|
||||
SSL_CTX_free(ctx);
|
||||
closesocket(hSocket);
|
||||
return false;
|
||||
}
|
||||
nSent += nBytes;
|
||||
}
|
||||
|
||||
// Skip remaining headers
|
||||
std::string header_line;
|
||||
while (std::getline(response_stream, header_line) && header_line != "\r") {}
|
||||
|
||||
// Read body (remainder in buffer + rest from socket)
|
||||
std::string body;
|
||||
|
||||
// First, grab anything already buffered past the headers
|
||||
if (response_buf.size() > 0) {
|
||||
std::istream body_stream(&response_buf);
|
||||
std::ostringstream oss;
|
||||
oss << body_stream.rdbuf();
|
||||
body = oss.str();
|
||||
// Read response over TLS
|
||||
std::string response;
|
||||
char buf[4096];
|
||||
while (true) {
|
||||
int nBytes = SSL_read(ssl, buf, sizeof(buf));
|
||||
if (nBytes <= 0)
|
||||
break;
|
||||
response.append(buf, nBytes);
|
||||
}
|
||||
|
||||
// Read rest until EOF
|
||||
boost::system::error_code ec;
|
||||
while (boost::asio::read(socket, response_buf, boost::asio::transfer_at_least(1), ec)) {
|
||||
std::istream s(&response_buf);
|
||||
std::ostringstream oss;
|
||||
oss << s.rdbuf();
|
||||
body += oss.str();
|
||||
SSL_shutdown(ssl);
|
||||
SSL_free(ssl);
|
||||
SSL_CTX_free(ctx);
|
||||
closesocket(hSocket);
|
||||
ssl = NULL;
|
||||
ctx = NULL;
|
||||
hSocket = INVALID_SOCKET;
|
||||
|
||||
if (response.empty()) {
|
||||
printf("HTTPS seed fetch: empty response from %s\n", seedHost.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse HTTP response - find end of headers
|
||||
size_t headerEnd = response.find("\r\n\r\n");
|
||||
if (headerEnd == std::string::npos) {
|
||||
printf("HTTPS seed fetch: malformed response (no header terminator)\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check status code
|
||||
std::string statusLine = response.substr(0, response.find("\r\n"));
|
||||
if (statusLine.find("200") == std::string::npos) {
|
||||
printf("HTTPS seed fetch: %s from %s\n", statusLine.c_str(), seedHost.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string body = response.substr(headerEnd + 4);
|
||||
|
||||
// Parse one address per line: "address:port" or just "address"
|
||||
int found = 0;
|
||||
std::istringstream lines(body);
|
||||
@@ -1520,7 +1714,7 @@ void ThreadHTTPSeedFetch2(void* parg)
|
||||
while (std::getline(lines, line))
|
||||
{
|
||||
if (fShutdown)
|
||||
return;
|
||||
return false;
|
||||
|
||||
// Trim whitespace and carriage returns
|
||||
while (!line.empty() && (line.back() == '\r' || line.back() == ' ' || line.back() == '\t'))
|
||||
@@ -1541,12 +1735,8 @@ void ThreadHTTPSeedFetch2(void* parg)
|
||||
port = atoi(addrStr.substr(onionPos + 7).c_str());
|
||||
addrStr = addrStr.substr(0, onionPos + 6); // keep ".onion"
|
||||
} else if (addrStr.find(".onion") == std::string::npos) {
|
||||
// Clearnet address - find last colon for port
|
||||
size_t colonPos = addrStr.rfind(':');
|
||||
if (colonPos != std::string::npos) {
|
||||
port = atoi(addrStr.substr(colonPos + 1).c_str());
|
||||
addrStr = addrStr.substr(0, colonPos);
|
||||
}
|
||||
// Tor-native: skip non-.onion addresses
|
||||
continue;
|
||||
}
|
||||
|
||||
if (port <= 0 || port > 65535)
|
||||
@@ -1564,15 +1754,25 @@ void ThreadHTTPSeedFetch2(void* parg)
|
||||
if (resolved) {
|
||||
CAddress addr(CService(parsed, port));
|
||||
addr.nTime = GetTime() - 3*24*60*60; // 3 days ago
|
||||
addrman.Add(addr, CNetAddr("http-seed", true));
|
||||
addrman.Add(addr, CNetAddr("https-seed", true));
|
||||
// Queue the first 8 seeds for immediate direct connection
|
||||
if (found < 8) {
|
||||
std::string oneShotAddr = addrStr + ":" + std::to_string(port);
|
||||
AddOneShot(oneShotAddr);
|
||||
}
|
||||
found++;
|
||||
}
|
||||
}
|
||||
|
||||
printf("%d addresses found from HTTP seed list (%s)\n", found, seedHost.c_str());
|
||||
printf("%d addresses found from HTTPS seed list (%s)\n", found, seedHost.c_str());
|
||||
return found > 0;
|
||||
|
||||
} catch (std::exception& e) {
|
||||
printf("HTTP seed fetch failed: %s\n", e.what());
|
||||
printf("HTTPS seed fetch failed: %s\n", e.what());
|
||||
if (ssl) { SSL_shutdown(ssl); SSL_free(ssl); }
|
||||
if (ctx) SSL_CTX_free(ctx);
|
||||
if (hSocket != INVALID_SOCKET) closesocket(hSocket);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1639,18 +1839,35 @@ void static ThreadStakeMiner(void* parg)
|
||||
{
|
||||
printf("ThreadStakeMiner started\n");
|
||||
CWallet* pwallet = (CWallet*)parg;
|
||||
try
|
||||
int nConsecutiveErrors = 0;
|
||||
while (!fShutdown)
|
||||
{
|
||||
vnThreadsRunning[THREAD_STAKE_MINER]++;
|
||||
StakeMiner(pwallet);
|
||||
vnThreadsRunning[THREAD_STAKE_MINER]--;
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
vnThreadsRunning[THREAD_STAKE_MINER]--;
|
||||
PrintException(&e, "ThreadStakeMiner()");
|
||||
} catch (...) {
|
||||
vnThreadsRunning[THREAD_STAKE_MINER]--;
|
||||
PrintException(NULL, "ThreadStakeMiner()");
|
||||
try
|
||||
{
|
||||
vnThreadsRunning[THREAD_STAKE_MINER]++;
|
||||
StakeMiner(pwallet);
|
||||
vnThreadsRunning[THREAD_STAKE_MINER]--;
|
||||
break; // normal exit
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
vnThreadsRunning[THREAD_STAKE_MINER]--;
|
||||
nConsecutiveErrors++;
|
||||
printf("ThreadStakeMiner() exception: %s (attempt %d)\n", e.what(), nConsecutiveErrors);
|
||||
if (nConsecutiveErrors >= 10) {
|
||||
printf("ThreadStakeMiner() too many consecutive errors, giving up\n");
|
||||
break;
|
||||
}
|
||||
MilliSleep(5000); // wait 5 seconds before retrying
|
||||
} catch (...) {
|
||||
vnThreadsRunning[THREAD_STAKE_MINER]--;
|
||||
nConsecutiveErrors++;
|
||||
printf("ThreadStakeMiner() unknown exception (attempt %d)\n", nConsecutiveErrors);
|
||||
if (nConsecutiveErrors >= 10) {
|
||||
printf("ThreadStakeMiner() too many consecutive errors, giving up\n");
|
||||
break;
|
||||
}
|
||||
MilliSleep(5000);
|
||||
}
|
||||
}
|
||||
printf("ThreadStakeMiner exiting, %d threads remaining\n", vnThreadsRunning[THREAD_STAKE_MINER]);
|
||||
}
|
||||
@@ -2175,8 +2392,11 @@ void StartNode(void* parg)
|
||||
RenameThread("Triangles-start");
|
||||
|
||||
if (semOutbound == NULL) {
|
||||
// initialize semaphore
|
||||
int nMaxOutbound = min(MAX_OUTBOUND_CONNECTIONS, (int)GetArg("-maxconnections", 125));
|
||||
// initialize semaphore — use -maxoutbound if specified, else default
|
||||
int nMaxOutbound = (int)GetArg("-maxoutbound", MAX_OUTBOUND_CONNECTIONS);
|
||||
nMaxOutbound = min(nMaxOutbound, (int)GetArg("-maxconnections", 125));
|
||||
nMaxOutbound = max(nMaxOutbound, 1); // at least 1 outbound
|
||||
printf("Max outbound connections: %d\n", nMaxOutbound);
|
||||
semOutbound = new CSemaphore(nMaxOutbound);
|
||||
}
|
||||
|
||||
@@ -2203,8 +2423,11 @@ void StartNode(void* parg)
|
||||
if (fUseUPnP)
|
||||
MapPort();
|
||||
|
||||
// HTTP seed list fetch (replaces DNS seeds)
|
||||
if (GetBoolArg("-noseedurl", false))
|
||||
// HTTP seed list fetch — only as a standalone thread if onion seeding is disabled,
|
||||
// since ThreadOnionSeed already calls ThreadHTTPSeedFetch2 internally.
|
||||
if (GetBoolArg("-onionseed", true))
|
||||
printf("HTTP seed fetch handled by onion seed thread\n");
|
||||
else if (GetBoolArg("-noseedurl", false))
|
||||
printf("HTTP seed fetch disabled\n");
|
||||
else if (!NewThread(ThreadHTTPSeedFetch, NULL))
|
||||
printf("Error: NewThread(ThreadHTTPSeedFetch) failed\n");
|
||||
@@ -2243,9 +2466,13 @@ bool StopNode()
|
||||
fShutdown = true;
|
||||
nTransactionsUpdated++;
|
||||
int64_t nStart = GetTime();
|
||||
if (semOutbound)
|
||||
for (int i=0; i<MAX_OUTBOUND_CONNECTIONS; i++)
|
||||
if (semOutbound) {
|
||||
int nMaxOutbound = (int)GetArg("-maxoutbound", MAX_OUTBOUND_CONNECTIONS);
|
||||
nMaxOutbound = min(nMaxOutbound, (int)GetArg("-maxconnections", 125));
|
||||
nMaxOutbound = max(nMaxOutbound, 1);
|
||||
for (int i=0; i<nMaxOutbound; i++)
|
||||
semOutbound->post();
|
||||
}
|
||||
do
|
||||
{
|
||||
int nThreadsRunning = 0;
|
||||
|
||||
@@ -26,7 +26,7 @@ extern int nBestHeight;
|
||||
|
||||
|
||||
|
||||
inline unsigned int ReceiveFloodSize() { return 100 * 1024 * 1024; } // 100 MB
|
||||
inline unsigned int ReceiveFloodSize() { return 50 * 1024 * 1024; } // 50 MB (reduced for Tor-only network)
|
||||
inline unsigned int SendBufferSize() { return 32 * 1024 * 1024; } // 32 MB
|
||||
|
||||
void AddOneShot(std::string strDest);
|
||||
@@ -146,6 +146,9 @@ public:
|
||||
bool fInbound;
|
||||
int nStartingHeight;
|
||||
int nMisbehavior;
|
||||
int64_t nPingUsecTime;
|
||||
int nBlocksDelivered;
|
||||
int64_t nAvgBlockLatencyUs;
|
||||
};
|
||||
|
||||
|
||||
@@ -252,6 +255,8 @@ public:
|
||||
bool fNetworkNode;
|
||||
bool fSuccessfullyConnected;
|
||||
bool fDisconnect;
|
||||
bool fPreferHeaders; // peer requested block announcements via headers (sendheaders)
|
||||
bool fSendCmpct; // peer supports compact block relay (sendcmpct)
|
||||
CSemaphoreGrant grantOutbound;
|
||||
int nRefCount;
|
||||
protected:
|
||||
@@ -271,6 +276,17 @@ public:
|
||||
CBlockIndex* pindexLastGetHeadersBegin;
|
||||
uint256 hashLastGetHeadersEnd;
|
||||
int nStartingHeight;
|
||||
int64_t nLastTipCheck; // last time we asked this peer for chain tip
|
||||
int64_t nAvgBlockLatencyUs; // rolling average block delivery latency (microseconds)
|
||||
int nBlocksDelivered; // count of blocks delivered by this peer
|
||||
int nBestKnownHeight; // highest block height known to this peer (updated from inv/block msgs)
|
||||
int nIncompatibleGetblocks; // count of getblocks with no common blocks (fork detection)
|
||||
|
||||
// BIP 31 ping/pong latency tracking
|
||||
uint64_t nPingNonceSent; // nonce of last ping sent (0 = no outstanding ping)
|
||||
int64_t nPingUsecStart; // microsecond timestamp when last ping was sent
|
||||
int64_t nPingUsecTime; // last measured round-trip time (microseconds), 0 = unknown
|
||||
int nPingRetryCount; // consecutive pings without pong response
|
||||
|
||||
// flood relay
|
||||
std::vector<CAddress> vAddrToSend;
|
||||
@@ -308,6 +324,8 @@ public:
|
||||
fNetworkNode = false;
|
||||
fSuccessfullyConnected = false;
|
||||
fDisconnect = false;
|
||||
fPreferHeaders = false;
|
||||
fSendCmpct = false;
|
||||
nRefCount = 0;
|
||||
nSendSize = 0;
|
||||
nSendOffset = 0;
|
||||
@@ -317,6 +335,15 @@ public:
|
||||
pindexLastGetHeadersBegin = 0;
|
||||
hashLastGetHeadersEnd = 0;
|
||||
nStartingHeight = -1;
|
||||
nLastTipCheck = 0;
|
||||
nAvgBlockLatencyUs = 0;
|
||||
nBlocksDelivered = 0;
|
||||
nBestKnownHeight = -1;
|
||||
nIncompatibleGetblocks = 0;
|
||||
nPingNonceSent = 0;
|
||||
nPingUsecStart = 0;
|
||||
nPingUsecTime = 0;
|
||||
nPingRetryCount = 0;
|
||||
fGetAddr = false;
|
||||
nMisbehavior = 0;
|
||||
hashCheckpointKnown = 0;
|
||||
@@ -732,6 +759,9 @@ public:
|
||||
// new code.
|
||||
static void ClearBanned(); // needed for unit testing
|
||||
static bool IsBanned(CNetAddr ip);
|
||||
static bool Ban(CNetAddr ip, int64_t banTime);
|
||||
static bool Unban(CNetAddr ip);
|
||||
static void GetBanned(std::map<CNetAddr, int64_t>& mapBannedOut);
|
||||
bool Misbehaving(int howmuch); // 1 == a little, 100 == a lot
|
||||
void copyStats(CNodeStats &stats);
|
||||
};
|
||||
|
||||
@@ -1,256 +1,44 @@
|
||||
// Copyright (c) 2024 Triangles developers
|
||||
// Network Bootstrap Implementation for Triangles v5.0.0.0
|
||||
// Network Bootstrap Implementation for Triangles
|
||||
// Distributed under the MIT/X11 software license
|
||||
|
||||
#include "net_bootstrap.h"
|
||||
#include "main.h"
|
||||
#include "net.h"
|
||||
#include "util.h"
|
||||
#include "init.h"
|
||||
#include "tor/onion_v3.h"
|
||||
|
||||
namespace NetBootstrap {
|
||||
|
||||
// Forward declarations
|
||||
bool InitializeLegacyBootstrap();
|
||||
bool InitializeMixedBootstrap();
|
||||
bool InitializeTorOnlyBootstrap();
|
||||
|
||||
// Global variable for tracking network activity
|
||||
int64_t nTimeBestReceived = 0;
|
||||
|
||||
BootstrapMode GetBootstrapMode() {
|
||||
// Determine bootstrap mode based on configuration
|
||||
bool torEnabled = GetBoolArg("-tor", false) || GetBoolArg("-proxy", false);
|
||||
bool onlyTor = GetBoolArg("-onlynet", false) && GetArg("-onlynet", "") == "tor";
|
||||
|
||||
if (onlyTor) {
|
||||
printf("Network Bootstrap: Tor-only mode selected\n");
|
||||
return BOOTSTRAP_TOR_ONLY;
|
||||
} else if (torEnabled) {
|
||||
printf("Network Bootstrap: Mixed Tor + clearnet mode selected\n");
|
||||
return BOOTSTRAP_TOR_MIXED;
|
||||
} else {
|
||||
printf("Network Bootstrap: Legacy clearnet mode selected\n");
|
||||
return BOOTSTRAP_LEGACY;
|
||||
}
|
||||
}
|
||||
|
||||
bool InitializeNetworkBootstrap(BootstrapMode mode) {
|
||||
printf("Initializing network bootstrap (mode: %d)...\n", mode);
|
||||
|
||||
try {
|
||||
switch (mode) {
|
||||
case BOOTSTRAP_LEGACY:
|
||||
return InitializeLegacyBootstrap();
|
||||
|
||||
case BOOTSTRAP_TOR_MIXED:
|
||||
return InitializeMixedBootstrap();
|
||||
|
||||
case BOOTSTRAP_TOR_ONLY:
|
||||
return InitializeTorOnlyBootstrap();
|
||||
|
||||
default:
|
||||
printf("ERROR: Unknown bootstrap mode: %d\n", mode);
|
||||
return false;
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
printf("ERROR: Exception in network bootstrap: %s\n", e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool InitializeLegacyBootstrap() {
|
||||
printf("Initializing legacy network bootstrap for old wallet compatibility...\n");
|
||||
|
||||
// Use DNS seeds and hardcoded IP addresses
|
||||
// This ensures old wallets can still connect
|
||||
|
||||
// Add DNS seed nodes
|
||||
for (int i = 0; strDNSSeed[i] != NULL; i++) {
|
||||
printf("Adding DNS seed: %s\n", strDNSSeed[i]);
|
||||
// DNS resolution will be handled by the existing network code
|
||||
}
|
||||
|
||||
// Add hardcoded IP seed nodes
|
||||
for (int i = 0; i < ARRAYLEN(pnSeed); i++) {
|
||||
if (pnSeed[i] != 0) {
|
||||
printf("Adding IP seed: 0x%08x\n", pnSeed[i]);
|
||||
// IP seeds will be processed by existing network code
|
||||
}
|
||||
}
|
||||
|
||||
printf("Legacy bootstrap initialized successfully\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InitializeMixedBootstrap() {
|
||||
printf("Initializing mixed Tor + clearnet bootstrap...\n");
|
||||
|
||||
// Initialize legacy bootstrap first
|
||||
if (!InitializeLegacyBootstrap()) {
|
||||
printf("ERROR: Failed to initialize legacy bootstrap\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Initialize Tor v3 functionality
|
||||
if (!InitTorV3()) {
|
||||
printf("WARNING: Failed to initialize Tor v3, continuing with clearnet only\n");
|
||||
return true; // Don't fail completely, just continue without Tor
|
||||
}
|
||||
|
||||
// Get Tor manager instance
|
||||
CTorV3Manager* torManager = CTorV3Manager::GetInstance();
|
||||
if (!torManager) {
|
||||
printf("WARNING: Could not get Tor manager instance\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Connect to Tor seed nodes
|
||||
std::vector<std::string> torSeeds = torManager->GetKnownSeederNodes();
|
||||
printf("Found %d Tor seed nodes\n", (int)torSeeds.size());
|
||||
|
||||
for (const std::string& seed : torSeeds) {
|
||||
printf("Attempting to connect to Tor seed: %s\n", seed.c_str());
|
||||
torManager->ConnectToSeederNode(seed);
|
||||
}
|
||||
|
||||
printf("Mixed bootstrap initialized successfully\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InitializeTorOnlyBootstrap() {
|
||||
printf("Initializing Tor-only bootstrap...\n");
|
||||
|
||||
// Initialize Tor v3 functionality
|
||||
if (!InitTorV3()) {
|
||||
printf("ERROR: Failed to initialize Tor v3 for Tor-only mode\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get Tor manager instance
|
||||
CTorV3Manager* torManager = CTorV3Manager::GetInstance();
|
||||
if (!torManager) {
|
||||
printf("ERROR: Could not get Tor manager instance for Tor-only mode\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enable Tor
|
||||
torManager->SetTorEnabled(true);
|
||||
|
||||
// Create hidden service if configured
|
||||
if (GetBoolArg("-torhiddenservice", false)) {
|
||||
int hiddenServicePort = GetArg("-torhiddenserviceport", NetworkConfig::TOR_HIDDEN_SERVICE_PORT);
|
||||
if (!torManager->CreateWalletHiddenService(hiddenServicePort)) {
|
||||
printf("WARNING: Failed to create hidden service\n");
|
||||
} else {
|
||||
printf("Created hidden service on port %d\n", hiddenServicePort);
|
||||
|
||||
// Register as seeder if enabled
|
||||
if (GetBoolArg("-torseeder", false)) {
|
||||
std::string onionAddress = torManager->GetWalletOnionAddress();
|
||||
if (!onionAddress.empty()) {
|
||||
torManager->RegisterAsSeederNode(onionAddress, hiddenServicePort);
|
||||
printf("Registered as Tor seeder: %s:%d\n", onionAddress.c_str(), hiddenServicePort);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Connect to Tor seed nodes
|
||||
std::vector<std::string> torSeeds = torManager->GetKnownSeederNodes();
|
||||
printf("Found %d Tor seed nodes for Tor-only mode\n", (int)torSeeds.size());
|
||||
|
||||
if (torSeeds.empty()) {
|
||||
printf("WARNING: No Tor seed nodes available for Tor-only mode\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
int connected = 0;
|
||||
for (const std::string& seed : torSeeds) {
|
||||
printf("Attempting to connect to Tor seed: %s\n", seed.c_str());
|
||||
if (torManager->ConnectToSeederNode(seed)) {
|
||||
connected++;
|
||||
}
|
||||
}
|
||||
|
||||
if (connected == 0) {
|
||||
printf("ERROR: Failed to connect to any Tor seed nodes\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
printf("Tor-only bootstrap initialized successfully (%d connections)\n", connected);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsOldWalletCompatible(int protocolVersion) {
|
||||
return protocolVersion >= NetworkConfig::MIN_PROTOCOL_VERSION_OLD_WALLET;
|
||||
}
|
||||
|
||||
std::vector<std::string> GetCompatibleSeedNodes(int protocolVersion) {
|
||||
std::vector<std::string> seeds;
|
||||
|
||||
if (protocolVersion >= NetworkConfig::CURRENT_PROTOCOL_VERSION) {
|
||||
// Modern wallet - can use all seed types
|
||||
|
||||
// Add Tor v3 seeds
|
||||
CTorV3Manager* torManager = CTorV3Manager::GetInstance();
|
||||
if (torManager) {
|
||||
std::vector<std::string> torSeeds = torManager->GetKnownSeederNodes();
|
||||
seeds.insert(seeds.end(), torSeeds.begin(), torSeeds.end());
|
||||
}
|
||||
|
||||
// Add DNS seeds
|
||||
for (int i = 0; strDNSSeed[i] != NULL; i++) {
|
||||
seeds.push_back(std::string(strDNSSeed[i]));
|
||||
}
|
||||
|
||||
} else if (IsOldWalletCompatible(protocolVersion)) {
|
||||
// Old but compatible wallet - use DNS seeds only
|
||||
for (int i = 0; strDNSSeed[i] != NULL; i++) {
|
||||
seeds.push_back(std::string(strDNSSeed[i]));
|
||||
}
|
||||
}
|
||||
|
||||
return seeds;
|
||||
}
|
||||
|
||||
NetworkHealth GetNetworkHealth() {
|
||||
NetworkHealth health;
|
||||
health.connectedPeers = 0;
|
||||
health.torPeers = 0;
|
||||
health.clearnetPeers = 0;
|
||||
health.isBootstrapped = false;
|
||||
health.isSyncing = false;
|
||||
health.lastBlockTime = 0;
|
||||
|
||||
|
||||
// Count connected peers
|
||||
{
|
||||
LOCK(cs_vNodes);
|
||||
health.connectedPeers = vNodes.size();
|
||||
|
||||
// Count Tor vs clearnet peers
|
||||
|
||||
for (CNode* pnode : vNodes) {
|
||||
std::string addr = pnode->addr.ToString();
|
||||
if (addr.find(".onion") != std::string::npos) {
|
||||
health.torPeers++;
|
||||
} else {
|
||||
health.clearnetPeers++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we're bootstrapped (have at least 3 connections)
|
||||
|
||||
// Bootstrapped if at least 3 connections
|
||||
health.isBootstrapped = (health.connectedPeers >= 3);
|
||||
|
||||
// Check sync status
|
||||
extern int nBestHeight;
|
||||
extern int64_t nTimeBestReceived;
|
||||
|
||||
health.isSyncing = (nTimeBestReceived > 0 &&
|
||||
GetTime() - nTimeBestReceived < 3600); // Synced within last hour
|
||||
health.lastBlockTime = nTimeBestReceived;
|
||||
|
||||
|
||||
// Sync status (nTimeBestReceived declared in main.h)
|
||||
health.isSyncing = (::nTimeBestReceived > 0 &&
|
||||
GetTime() - ::nTimeBestReceived < 3600);
|
||||
health.lastBlockTime = ::nTimeBestReceived;
|
||||
|
||||
return health;
|
||||
}
|
||||
|
||||
} // namespace NetBootstrap
|
||||
} // namespace NetBootstrap
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (c) 2024 Triangles developers
|
||||
// Network Bootstrap Configuration for Triangles v5.0.0.0
|
||||
// Network Bootstrap Configuration for Triangles
|
||||
// Distributed under the MIT/X11 software license
|
||||
|
||||
#ifndef TRIANGLES_NET_BOOTSTRAP_H
|
||||
@@ -9,82 +9,45 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// Network bootstrap configuration for preserving old wallets
|
||||
// while enabling modern Tor v3 functionality
|
||||
// Tor-native network bootstrap configuration.
|
||||
// All connections route through embedded Tor. Clearnet is disabled.
|
||||
|
||||
namespace NetBootstrap {
|
||||
|
||||
// DNS seed nodes for initial bootstrap (clearnet fallback)
|
||||
// Unused attribute prevents warnings
|
||||
static const char* strDNSSeed[] __attribute__((unused)) = {
|
||||
"seed1.cryptographic-triangles.org",
|
||||
"seed2.cryptographic-triangles.org",
|
||||
"seed3.cryptographic-triangles.org",
|
||||
"backup-seed.cryptographic-triangles.org",
|
||||
NULL
|
||||
};
|
||||
|
||||
// Hardcoded seeds removed - peer discovery is now fully dynamic via HTTP seed list.
|
||||
// See: seeds.cryptographic-triangles.org
|
||||
static const unsigned int pnSeed[] __attribute__((unused)) = {
|
||||
};
|
||||
|
||||
// Network protocol compatibility settings
|
||||
struct NetworkConfig {
|
||||
// Preserve compatibility with old wallets
|
||||
static const int MIN_PROTOCOL_VERSION_OLD_WALLET = 70200;
|
||||
static const int CURRENT_PROTOCOL_VERSION = 70205;
|
||||
|
||||
// Network ports (preserved from existing configuration)
|
||||
|
||||
// Network ports
|
||||
static const int DEFAULT_PORT_MAINNET = 24112;
|
||||
static const int DEFAULT_PORT_TESTNET = 24111;
|
||||
static const int DEFAULT_RPC_PORT_MAINNET = 19112;
|
||||
static const int DEFAULT_RPC_PORT_TESTNET = 19111;
|
||||
|
||||
// Connection limits for different node types
|
||||
static const int MAX_CONNECTIONS_DEFAULT = 125;
|
||||
static const int MAX_CONNECTIONS_TOR_ONLY = 64;
|
||||
|
||||
// Connection limits
|
||||
static const int MAX_CONNECTIONS_DEFAULT = 64;
|
||||
static const int MAX_OUTBOUND_CONNECTIONS = 8;
|
||||
|
||||
// Tor-specific configuration
|
||||
|
||||
// Tor configuration
|
||||
static const int TOR_HIDDEN_SERVICE_PORT = 24112;
|
||||
static constexpr const char* TOR_PROXY_DEFAULT = "127.0.0.1:9050";
|
||||
|
||||
// Network timeouts and retry settings
|
||||
|
||||
// Timeouts
|
||||
static const int CONNECTION_TIMEOUT_SECONDS = 30;
|
||||
static const int PEER_DISCOVERY_INTERVAL_SECONDS = 3600; // 1 hour
|
||||
static const int SEEDER_ANNOUNCEMENT_INTERVAL_SECONDS = 1800; // 30 minutes
|
||||
static const int PEER_DISCOVERY_INTERVAL_SECONDS = 3600;
|
||||
};
|
||||
|
||||
// Bootstrap sequence for different wallet types
|
||||
enum BootstrapMode {
|
||||
BOOTSTRAP_LEGACY, // Old wallets without Tor
|
||||
BOOTSTRAP_TOR_MIXED, // Mixed clearnet + Tor
|
||||
BOOTSTRAP_TOR_ONLY // Tor-only mode
|
||||
};
|
||||
|
||||
// Get bootstrap configuration based on wallet capabilities
|
||||
BootstrapMode GetBootstrapMode();
|
||||
|
||||
// Initialize network bootstrap based on configuration
|
||||
bool InitializeNetworkBootstrap(BootstrapMode mode);
|
||||
|
||||
// Backward compatibility helpers
|
||||
bool IsOldWalletCompatible(int protocolVersion);
|
||||
std::vector<std::string> GetCompatibleSeedNodes(int protocolVersion);
|
||||
|
||||
// Network health monitoring
|
||||
struct NetworkHealth {
|
||||
int connectedPeers;
|
||||
int torPeers;
|
||||
int clearnetPeers;
|
||||
bool isBootstrapped;
|
||||
bool isSyncing;
|
||||
int64_t lastBlockTime;
|
||||
};
|
||||
|
||||
|
||||
NetworkHealth GetNetworkHealth();
|
||||
|
||||
|
||||
} // namespace NetBootstrap
|
||||
|
||||
#endif // TRIANGLES_NET_BOOTSTRAP_H
|
||||
#endif // TRIANGLES_NET_BOOTSTRAP_H
|
||||
|
||||
@@ -2,10 +2,15 @@
|
||||
#ifndef TRIANGLES_ONIONSEED_H
|
||||
#define TRIANGLES_ONIONSEED_H
|
||||
|
||||
// Onion seeds are now fetched dynamically via HTTP seed list.
|
||||
// No hardcoded onion addresses - they go stale when Tor services restart.
|
||||
// See: seeds.cryptographic-triangles.org
|
||||
// Hardcoded onion seed nodes for initial peer discovery.
|
||||
// Also fetched dynamically via https://seeds.cryptographic-triangles.org/seeds.txt
|
||||
static const char *strMainNetOnionSeed[][1] = {
|
||||
{"6ygpphp2qsucwvhwefv6h6ehvk6zjf7b7zdp4ggkzjjwe76cg6jwm7id.onion"},
|
||||
{"jbpfhe7zw3qm67wy3j2ayysp3mnrjobopthnko3b3sgahqtecblwqmid.onion"},
|
||||
{"uddaxjbo3lh2zskg7w6gwln4ty5cel7q4c5jbx7fdtv6zf2j47gdlyad.onion"},
|
||||
{"el5sirhhleecuctpeeprelzubpqmoqivvra3rzlwbjttinxa4fq3wnid.onion"},
|
||||
{"sj5dhybnlp3v4y5niyc5unrnd6s43lyx5ibup7rolyosjbi2u2hsbvyd.onion"},
|
||||
{"i3kr5meha7se4ns3wss3h7v46m6uksfzv4wrohdqxpj6n35wyo2bvlid.onion"},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
|
||||
@@ -1383,7 +1383,7 @@ QLabel {
|
||||
color: #f26522;
|
||||
}</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_6" stretch="0,1,0,1,0,0,0,0,0,0">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_6" stretch="0,0,0,1,0,1,0,0,0,0,0,0,0">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
@@ -1406,7 +1406,46 @@ QLabel {
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>70</width>
|
||||
<width>10</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_onion">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>8</pointsize>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="cursor">
|
||||
<cursorShape>PointingHandCursor</cursorShape>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Click to copy .onion address</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="textInteractionFlags">
|
||||
<set>Qt::NoTextInteraction</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_onion">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>10</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
@@ -1514,6 +1553,23 @@ QProgressBar::chunk {
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_v3">
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>9</pointsize>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Tor V3 hidden service status</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string notr="true">V3</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_connections">
|
||||
<property name="text">
|
||||
|
||||
@@ -1506,6 +1506,27 @@ QCheckBox::indicator:checked {
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="showOnionAddress">
|
||||
<property name="toolTip">
|
||||
<string>Whether to show this wallet's .onion address in the status bar.</string>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">QCheckBox::indicator {
|
||||
border:1px solid #f26522;
|
||||
background-color: #000;
|
||||
}
|
||||
|
||||
QCheckBox::indicator:checked {
|
||||
image:url(:/icons/checkbox);
|
||||
padding: 1px;
|
||||
}</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Show .onion address in status &bar</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_Display">
|
||||
<property name="orientation">
|
||||
|
||||
@@ -225,9 +225,22 @@ bool IntroDialog::pickDataDirectory()
|
||||
return false;
|
||||
}
|
||||
|
||||
// Offer bootstrap download on each startup (unless user checked "don't ask again")
|
||||
// Auto-bootstrap: if no blockchain data exists, download automatically.
|
||||
// If data exists, offer optional re-download (unless user checked "don't ask again").
|
||||
fs::path dataDirPath(dataDir.toStdString());
|
||||
if (!settings.value("bootstrapDontAsk", false).toBool())
|
||||
bool needsBootstrap = Bootstrap::NeedsBootstrap(dataDirPath);
|
||||
bool userWantsBootstrap = false;
|
||||
|
||||
if (needsBootstrap)
|
||||
{
|
||||
// No blockchain data — bootstrap automatically, just inform the user
|
||||
QMessageBox::information(0, "Triangles",
|
||||
"No blockchain data found.\n\n"
|
||||
"Downloading the latest blockchain snapshot automatically.\n"
|
||||
"This will only take a few minutes.");
|
||||
userWantsBootstrap = true;
|
||||
}
|
||||
else if (!settings.value("bootstrapDontAsk", false).toBool())
|
||||
{
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle("Triangles");
|
||||
@@ -247,7 +260,10 @@ bool IntroDialog::pickDataDirectory()
|
||||
if (dontAskBox->isChecked())
|
||||
settings.setValue("bootstrapDontAsk", true);
|
||||
|
||||
if (ret == QMessageBox::Yes)
|
||||
userWantsBootstrap = (ret == QMessageBox::Yes);
|
||||
}
|
||||
|
||||
if (userWantsBootstrap)
|
||||
{
|
||||
std::string host = Bootstrap::DEFAULT_HOST;
|
||||
std::string strError;
|
||||
@@ -276,12 +292,6 @@ bool IntroDialog::pickDataDirectory()
|
||||
};
|
||||
|
||||
bool success = Bootstrap::DownloadBootstrap(host, dataDirPath, progressFn, strError);
|
||||
if (!success) {
|
||||
host = Bootstrap::FALLBACK_HOST;
|
||||
progress.setValue(0);
|
||||
success = Bootstrap::DownloadBootstrap(host, dataDirPath, progressFn, strError);
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
QMessageBox::warning(0, "Triangles",
|
||||
QString("Could not download blockchain snapshot:\n%1\n\n"
|
||||
@@ -291,7 +301,6 @@ bool IntroDialog::pickDataDirectory()
|
||||
progress.setValue(100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
#include "ui_interface.h"
|
||||
#include "base58.h"
|
||||
#include "json_spirit.h"
|
||||
#include "json/json_compat.h"
|
||||
|
||||
#include <QSet>
|
||||
#include <QTimer>
|
||||
@@ -265,8 +265,9 @@ public:
|
||||
|
||||
private:
|
||||
// Get the json value
|
||||
const json_spirit::mValue & find_value(json_spirit::mObject & obj, const char * key)
|
||||
const json_spirit::Value & find_json_value(json_spirit::mObject & obj, const char * key)
|
||||
{
|
||||
static const json_spirit::Value null_val;
|
||||
std::string newKey = key;
|
||||
|
||||
json_spirit::mObject::const_iterator i = obj.find(newKey);
|
||||
@@ -274,12 +275,12 @@ private:
|
||||
if(i != obj.end() && i->first == newKey)
|
||||
return i->second;
|
||||
else
|
||||
return json_spirit::mValue::null;
|
||||
return null_val;
|
||||
}
|
||||
|
||||
const std::string get_value(json_spirit::mObject & obj, const char * key)
|
||||
{
|
||||
json_spirit::mValue val = find_value(obj, key);
|
||||
const json_spirit::Value& val = find_json_value(obj, key);
|
||||
|
||||
if(val.is_null())
|
||||
return "";
|
||||
@@ -291,7 +292,7 @@ private:
|
||||
void handleMessageEntry(const MessageTableEntry & message, const bool append)
|
||||
{
|
||||
addMessageEntry(message, append);
|
||||
json_spirit::mValue mVal;
|
||||
json_spirit::Value mVal;
|
||||
json_spirit::read(message.message.toStdString(), mVal);
|
||||
|
||||
if(mVal.is_null())
|
||||
@@ -300,8 +301,8 @@ private:
|
||||
return;
|
||||
}
|
||||
|
||||
json_spirit::mObject mObj(mVal.get_obj());
|
||||
json_spirit::mValue mvType = find_value(mObj, "type");
|
||||
json_spirit::mObject mObj(mVal.get_map_obj());
|
||||
json_spirit::Value mvType = find_json_value(mObj, "type");
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -206,6 +206,7 @@ void OptionsDialog::setMapper()
|
||||
mapper->addMapping(ui->unit, OptionsModel::DisplayUnit);
|
||||
mapper->addMapping(ui->displayAddresses, OptionsModel::DisplayAddresses);
|
||||
mapper->addMapping(ui->coinControlFeatures, OptionsModel::CoinControlFeatures);
|
||||
mapper->addMapping(ui->showOnionAddress, OptionsModel::ShowOnionAddress);
|
||||
}
|
||||
|
||||
void OptionsDialog::enableApplyButton()
|
||||
|
||||
@@ -51,6 +51,7 @@ void OptionsModel::Init()
|
||||
fMinimizeToTray = settings.value("fMinimizeToTray", false).toBool();
|
||||
fMinimizeOnClose = settings.value("fMinimizeOnClose", false).toBool();
|
||||
fCoinControlFeatures = settings.value("fCoinControlFeatures", false).toBool();
|
||||
fShowOnionAddress = settings.value("fShowOnionAddress", true).toBool();
|
||||
nTransactionFee = settings.value("nTransactionFee").toLongLong();
|
||||
nReserveBalance = settings.value("nReserveBalance").toLongLong();
|
||||
language = settings.value("language", "").toString();
|
||||
@@ -126,6 +127,8 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const
|
||||
return settings.value("language", "");
|
||||
case CoinControlFeatures:
|
||||
return QVariant(fCoinControlFeatures);
|
||||
case ShowOnionAddress:
|
||||
return QVariant(fShowOnionAddress);
|
||||
default:
|
||||
return QVariant();
|
||||
}
|
||||
@@ -226,6 +229,12 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in
|
||||
emit coinControlFeaturesChanged(fCoinControlFeatures);
|
||||
}
|
||||
break;
|
||||
case ShowOnionAddress: {
|
||||
fShowOnionAddress = value.toBool();
|
||||
settings.setValue("fShowOnionAddress", fShowOnionAddress);
|
||||
emit showOnionAddressChanged(fShowOnionAddress);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -269,3 +278,8 @@ bool OptionsModel::getDisplayAddresses()
|
||||
{
|
||||
return bDisplayAddresses;
|
||||
}
|
||||
|
||||
bool OptionsModel::getShowOnionAddress()
|
||||
{
|
||||
return fShowOnionAddress;
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ public:
|
||||
DetachDatabases, // bool
|
||||
Language, // QString
|
||||
CoinControlFeatures, // bool
|
||||
ShowOnionAddress, // bool
|
||||
OptionIDRowCount,
|
||||
};
|
||||
|
||||
@@ -52,6 +53,7 @@ public:
|
||||
int getDisplayUnit();
|
||||
bool getDisplayAddresses();
|
||||
bool getCoinControlFeatures();
|
||||
bool getShowOnionAddress();
|
||||
QString getLanguage() { return language; }
|
||||
|
||||
private:
|
||||
@@ -60,6 +62,7 @@ private:
|
||||
bool fMinimizeToTray;
|
||||
bool fMinimizeOnClose;
|
||||
bool fCoinControlFeatures;
|
||||
bool fShowOnionAddress;
|
||||
QString language;
|
||||
|
||||
signals:
|
||||
@@ -67,6 +70,7 @@ signals:
|
||||
void transactionFeeChanged(qint64);
|
||||
void reserveBalanceChanged(qint64);
|
||||
void coinControlFeaturesChanged(bool);
|
||||
void showOnionAddressChanged(bool);
|
||||
};
|
||||
|
||||
#endif // OPTIONSMODEL_H
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include <QAbstractItemDelegate>
|
||||
#include <QPainter>
|
||||
#include <QTimer>
|
||||
|
||||
#define DECORATION_SIZE 64
|
||||
#define NUM_ITEMS 5
|
||||
@@ -88,6 +89,7 @@ public:
|
||||
OverviewPage::OverviewPage(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
ui(new Ui::OverviewPage),
|
||||
clientModel(0),
|
||||
currentBalance(-1),
|
||||
currentStake(0),
|
||||
currentUnconfirmedBalance(-1),
|
||||
@@ -112,6 +114,7 @@ OverviewPage::OverviewPage(QWidget *parent) :
|
||||
|
||||
// start with displaying the "out of sync" warnings
|
||||
showOutOfSyncWarning(true);
|
||||
|
||||
}
|
||||
|
||||
void OverviewPage::handleTransactionClicked(const QModelIndex &index)
|
||||
@@ -125,6 +128,11 @@ OverviewPage::~OverviewPage()
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void OverviewPage::setClientModel(ClientModel *clientModel)
|
||||
{
|
||||
this->clientModel = clientModel;
|
||||
}
|
||||
|
||||
void OverviewPage::setBalance(qint64 balance, qint64 stake, qint64 unconfirmedBalance, qint64 immatureBalance)
|
||||
{
|
||||
if (!model || !model->getOptionsModel())
|
||||
@@ -213,3 +221,4 @@ void OverviewPage::showOutOfSyncWarning(bool fShow)
|
||||
ui->labelWalletStatus->setVisible(fShow);
|
||||
ui->labelTransactionsStatus->setVisible(fShow);
|
||||
}
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 361 KiB After Width: | Height: | Size: 115 KiB |
|
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 20 KiB |
@@ -15,9 +15,11 @@
|
||||
|
||||
#include "coincontrol.h"
|
||||
#include "coincontroldialog.h"
|
||||
#include "tor/onion_v3.h"
|
||||
|
||||
#include <QMessageBox>
|
||||
#include <QLocale>
|
||||
#include <QApplication>
|
||||
#include <QTextDocument>
|
||||
#include <QScrollBar>
|
||||
#include <QClipboard>
|
||||
@@ -145,6 +147,37 @@ void SendCoinsDialog::on_sendButton_clicked()
|
||||
return;
|
||||
}
|
||||
|
||||
// Resolve any .onion addresses to TRI addresses
|
||||
CTorV3Manager* torMgr = CTorV3Manager::GetInstance();
|
||||
for(int i = 0; i < recipients.size(); ++i)
|
||||
{
|
||||
std::string addr = recipients[i].address.toStdString();
|
||||
if (addr.size() == 62 && addr.substr(addr.size() - 6) == ".onion")
|
||||
{
|
||||
std::string triAddr;
|
||||
if (torMgr && torMgr->LookupCachedOnionAddress(addr, triAddr))
|
||||
{
|
||||
// Found in cache — substitute
|
||||
recipients[i].address = QString::fromStdString(triAddr);
|
||||
if (recipients[i].label.isEmpty())
|
||||
recipients[i].label = QString::fromStdString(addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not cached — initiate async resolution and tell user to wait
|
||||
if (torMgr)
|
||||
torMgr->ResolveOnionAddress(addr, triAddr);
|
||||
|
||||
QMessageBox::information(this, tr("Resolving .onion Address"),
|
||||
tr("Connecting to %1 to resolve their TRI address.\n\n"
|
||||
"Please wait a moment and try again.")
|
||||
.arg(recipients[i].address));
|
||||
fNewRecipientAllowed = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Format confirmation message
|
||||
QStringList formatted;
|
||||
foreach(const SendCoinsRecipient &rcp, recipients)
|
||||
|
||||
@@ -23,7 +23,7 @@ SendCoinsEntry::SendCoinsEntry(QWidget *parent) :
|
||||
#if QT_VERSION >= 0x040700
|
||||
/* Do not move this to the XML file, Qt before 4.7 will choke on it */
|
||||
ui->addAsLabel->setPlaceholderText(tr("Enter a label for this address to add it to your address book"));
|
||||
ui->payTo->setPlaceholderText(tr("Enter a TRI address (e.g. TXc7mPCNFFpinDonuSH5PNVY9S8nBcvGQm)"));
|
||||
ui->payTo->setPlaceholderText(tr("Enter a TRI address or .onion address"));
|
||||
ui->narration->setPlaceholderText(tr("Enter a short note to send with payment. This feature will be enabled later."));
|
||||
ui->narration->setEnabled(false);
|
||||
ui->narration->setVisible(false);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "sendmessagesentry.h"
|
||||
#include "dialog_move_handler.h"
|
||||
#include "guiutil.h"
|
||||
#include "tor/onion_v3.h"
|
||||
|
||||
#include <QMessageBox>
|
||||
#include <QLocale>
|
||||
@@ -155,6 +156,35 @@ void SendMessagesDialog::on_sendButton_clicked()
|
||||
if(!valid || recipients.isEmpty())
|
||||
return;
|
||||
|
||||
// Resolve any .onion addresses to TRI addresses
|
||||
CTorV3Manager* torMgr = CTorV3Manager::GetInstance();
|
||||
for(int i = 0; i < recipients.size(); ++i)
|
||||
{
|
||||
std::string addr = recipients[i].address.toStdString();
|
||||
if (addr.size() == 62 && addr.substr(addr.size() - 6) == ".onion")
|
||||
{
|
||||
std::string triAddr;
|
||||
if (torMgr && torMgr->LookupCachedOnionAddress(addr, triAddr))
|
||||
{
|
||||
recipients[i].address = QString::fromStdString(triAddr);
|
||||
if (recipients[i].label.isEmpty())
|
||||
recipients[i].label = QString::fromStdString(addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (torMgr)
|
||||
torMgr->ResolveOnionAddress(addr, triAddr);
|
||||
|
||||
QMessageBox::information(this, tr("Resolving .onion Address"),
|
||||
tr("Connecting to %1 to resolve their TRI address.\n\n"
|
||||
"Please wait a moment and try again.")
|
||||
.arg(recipients[i].address));
|
||||
fNewRecipientAllowed = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Format confirmation message
|
||||
QStringList formatted;
|
||||
foreach(const SendMessagesRecipient &rcp, recipients)
|
||||
|
||||
@@ -25,7 +25,7 @@ SendMessagesEntry::SendMessagesEntry(QWidget *parent) :
|
||||
#if QT_VERSION >= 0x040700
|
||||
/* Do not move this to the XML file, Qt before 4.7 will choke on it */
|
||||
ui->addAsLabel->setPlaceholderText(tr("Enter a label for this address to add it to your address book"));
|
||||
ui->sendTo->setPlaceholderText(tr("Enter a TRI address (e.g. TXc7mPCNFFpinDonuSH5PNVY9S8nBcvGQm)"));
|
||||
ui->sendTo->setPlaceholderText(tr("Enter a TRI address or .onion address"));
|
||||
ui->publicKey->setPlaceholderText(tr("Enter the public key for the address above, it is not in the blockchain"));
|
||||
ui->messageText->setErrorText(tr("You cannot send a blank message!"));
|
||||
#endif
|
||||
|
||||
@@ -50,11 +50,27 @@ QValidator::State TrianglesAddressValidator::validate(QString &input, int &pos)
|
||||
|
||||
// Validation
|
||||
QValidator::State state = QValidator::Acceptable;
|
||||
|
||||
// Allow .onion addresses (base32 lowercase + digits 2-7 + period)
|
||||
bool isOnion = input.endsWith(".onion");
|
||||
|
||||
for(int idx=0; idx<input.size(); ++idx)
|
||||
{
|
||||
int ch = input.at(idx).unicode();
|
||||
|
||||
if(((ch >= '0' && ch<='9') ||
|
||||
if (isOnion)
|
||||
{
|
||||
// V3 onion: base32 (a-z, 2-7) + ".onion" suffix
|
||||
if ((ch >= 'a' && ch <= 'z') || (ch >= '2' && ch <= '7') || ch == '.')
|
||||
{
|
||||
// Valid onion character
|
||||
}
|
||||
else
|
||||
{
|
||||
state = QValidator::Invalid;
|
||||
}
|
||||
}
|
||||
else if(((ch >= '0' && ch<='9') ||
|
||||
(ch >= 'a' && ch<='z') ||
|
||||
(ch >= 'A' && ch<='Z')) &&
|
||||
ch != 'l' && ch != 'I' && ch != '0' && ch != 'O')
|
||||
|
||||
@@ -40,6 +40,8 @@
|
||||
#include "util.h"
|
||||
//#include "message_box_dialog.h"
|
||||
#include "wallet.h"
|
||||
#include "tor/tor_embedded.h"
|
||||
#include "tor/onion_v3.h"
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
#include "macdockiconhandler.h"
|
||||
@@ -66,6 +68,8 @@
|
||||
#include <QFileDialog>
|
||||
#include <QStandardPaths>
|
||||
#include <QTimer>
|
||||
#include <QClipboard>
|
||||
#include <QToolTip>
|
||||
#include <QDragEnterEvent>
|
||||
#if QT_VERSION < 0x050000
|
||||
#include <QUrl>
|
||||
@@ -336,6 +340,21 @@ TrianglesGUI::TrianglesGUI(bool fIsTestnet, QWidget *parent):
|
||||
updateStakingIcon();
|
||||
}
|
||||
|
||||
// Onion address in status bar (hidden until populated, click to copy)
|
||||
labelOnionAddress = ui->label_onion;
|
||||
labelOnionAddress->setVisible(false);
|
||||
labelOnionAddress->setCursor(Qt::PointingHandCursor);
|
||||
labelOnionAddress->installEventFilter(this);
|
||||
|
||||
// V3 indicator next to staking icon (hidden until onion is active)
|
||||
labelV3Icon = ui->label_v3;
|
||||
labelV3Icon->setVisible(false);
|
||||
|
||||
QTimer *timerOnion = new QTimer(this);
|
||||
connect(timerOnion, SIGNAL(timeout()), this, SLOT(updateOnionAddress()));
|
||||
timerOnion->start(5000);
|
||||
updateOnionAddress();
|
||||
|
||||
QTimer *timerShutdown = new QTimer(this);
|
||||
connect(timerShutdown, SIGNAL(timeout()), this, SLOT(detectShutdown()));
|
||||
timerShutdown->start(200);
|
||||
@@ -569,6 +588,9 @@ void TrianglesGUI::setClientModel(ClientModel *clientModel)
|
||||
rpcConsole->setClientModel(clientModel);
|
||||
addressBookPage->setOptionsModel(clientModel->getOptionsModel());
|
||||
receiveCoinsPage->setOptionsModel(clientModel->getOptionsModel());
|
||||
|
||||
connect(clientModel->getOptionsModel(), SIGNAL(showOnionAddressChanged(bool)), this, SLOT(updateOnionAddress()));
|
||||
updateOnionAddress();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1289,6 +1311,16 @@ bool TrianglesGUI::eventFilter(QObject *object, QEvent *event)
|
||||
}
|
||||
if (object == ui->pushButton_Overview && event->type() == QEvent::MouseButtonPress)
|
||||
gotoOverviewPage();
|
||||
if (object == labelOnionAddress && event->type() == QEvent::MouseButtonPress)
|
||||
{
|
||||
QString addr = labelOnionAddress->text();
|
||||
if (!addr.isEmpty())
|
||||
{
|
||||
QApplication::clipboard()->setText(addr);
|
||||
QToolTip::showText(QCursor::pos(), tr("Copied!"), labelOnionAddress);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return QMainWindow::eventFilter(object, event);
|
||||
}
|
||||
|
||||
@@ -1715,6 +1747,42 @@ void TrianglesGUI::detectShutdown()
|
||||
QMetaObject::invokeMethod(QCoreApplication::instance(), "quit", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void TrianglesGUI::updateOnionAddress()
|
||||
{
|
||||
std::string onionAddress = CTorV3Manager::GetInstance()->GetWalletOnionAddress();
|
||||
if (onionAddress.empty())
|
||||
onionAddress = CTorEmbedded::GetInstance()->GetOnionAddress();
|
||||
|
||||
bool hasOnion = !onionAddress.empty();
|
||||
|
||||
// V3 indicator — always visible when onion is active, independent of the address toggle
|
||||
if (hasOnion) {
|
||||
labelV3Icon->setStyleSheet("color: #00ff00; font-weight: bold;");
|
||||
labelV3Icon->setToolTip(tr("V3 Tor enabled"));
|
||||
labelV3Icon->setVisible(true);
|
||||
} else {
|
||||
labelV3Icon->setStyleSheet("color: #555555; font-weight: bold;");
|
||||
labelV3Icon->setToolTip(tr("V3 Tor not active"));
|
||||
labelV3Icon->setVisible(true);
|
||||
}
|
||||
|
||||
// Onion address text — respects user preference
|
||||
if (clientModel && clientModel->getOptionsModel() &&
|
||||
!clientModel->getOptionsModel()->getShowOnionAddress()) {
|
||||
labelOnionAddress->setVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hasOnion) {
|
||||
labelOnionAddress->setVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
labelOnionAddress->setText(QString::fromStdString(onionAddress));
|
||||
labelOnionAddress->setToolTip(tr("This wallet's Tor .onion address. Selectable — right-click to copy."));
|
||||
labelOnionAddress->setVisible(true);
|
||||
}
|
||||
|
||||
|
||||
void TrianglesGUI::on_bHelp_clicked()
|
||||
{
|
||||
|
||||
@@ -109,6 +109,8 @@ private:
|
||||
QLabel *labelStakingIcon;
|
||||
QLabel *labelConnectionsIcon;
|
||||
QLabel *labelBlocksIcon;
|
||||
QLabel *labelOnionAddress;
|
||||
QLabel *labelV3Icon;
|
||||
QLabel *progressBarLabel;
|
||||
QProgressBar *progressBar;
|
||||
|
||||
@@ -174,6 +176,7 @@ public slots:
|
||||
void setEncryptionStatus(int status);
|
||||
void setWalletTransactionSyncState(bool syncing);
|
||||
void setWalletTransactionSyncProgress(bool syncing, int pendingNotifications);
|
||||
void updateOnionAddress();
|
||||
|
||||
/** Notify the user of an error in the network or transaction handling code. */
|
||||
void error(const QString &title, const QString &message, bool modal);
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "walletdb.h" // for BackupWallet
|
||||
#include "base58.h"
|
||||
#include "main.h"
|
||||
#include "tor/onion_v3.h"
|
||||
|
||||
#include <QSet>
|
||||
#include <QTimer>
|
||||
@@ -281,8 +282,11 @@ void WalletModel::updateAddressBook(const QString &address, const QString &label
|
||||
bool WalletModel::validateAddress(const QString &address)
|
||||
{
|
||||
std::string sAddr = address.toStdString();
|
||||
|
||||
|
||||
|
||||
// Accept V3 .onion addresses (56-char base32 + ".onion" = 62 chars)
|
||||
if (sAddr.size() == 62 && sAddr.substr(sAddr.size() - 6) == ".onion")
|
||||
return CTorV3Service::ValidateOnionAddress(sAddr);
|
||||
|
||||
CTrianglesAddress addressParsed(sAddr);
|
||||
return addressParsed.IsValid();
|
||||
}
|
||||
@@ -385,7 +389,7 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(const QList<SendCoinsRecipie
|
||||
for (it = mapStealthNarr.begin(); it != mapStealthNarr.end(); ++it)
|
||||
{
|
||||
char key[64];
|
||||
if (snprintf(key, sizeof(key), "n_%u") < 1)
|
||||
if (snprintf(key, sizeof(key), "n_%u", it->first) < 1)
|
||||
{
|
||||
printf("CreateStealthTransaction(): Error creating narration key.");
|
||||
continue;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "addressindex.h"
|
||||
#include "txdb.h"
|
||||
#include "base58.h"
|
||||
#include "utxosnapshot.h"
|
||||
|
||||
using namespace json_spirit;
|
||||
using namespace std;
|
||||
@@ -28,6 +29,9 @@ double GetDifficulty(const CBlockIndex* blockindex)
|
||||
blockindex = GetLastBlockIndex(pindexBest, false);
|
||||
}
|
||||
|
||||
if (blockindex == NULL)
|
||||
return 1.0;
|
||||
|
||||
int nShift = (blockindex->nBits >> 24) & 0xff;
|
||||
|
||||
double dDiff =
|
||||
@@ -360,6 +364,49 @@ Value gettxoutsetinfo(const Array& params, bool fHelp)
|
||||
return obj;
|
||||
}
|
||||
|
||||
Value recalculatesupply(const Array& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() != 0)
|
||||
throw runtime_error(
|
||||
"recalculatesupply\n"
|
||||
"Recalculates the money supply by summing all unspent transaction outputs.\n"
|
||||
"Updates the stored money supply value at the chain tip and persists it to disk.\n"
|
||||
"Returns the old and new supply values for comparison.\n"
|
||||
"\nWARNING: This modifies blockchain index state. Only use if money supply is incorrect.");
|
||||
|
||||
if (!pindexBest)
|
||||
throw runtime_error("recalculatesupply: no best block");
|
||||
|
||||
int nUtxoCount = 0;
|
||||
CTxDB txdb;
|
||||
int64_t nCalculatedSupply = txdb.SumUtxoValues(nUtxoCount);
|
||||
int64_t nOldSupply = pindexBest->nMoneySupply;
|
||||
int64_t nDifference = nCalculatedSupply - nOldSupply;
|
||||
|
||||
// Sanity check: difference should be reasonable (not millions of TRI)
|
||||
// Max supply is 2,222,222 TRI, so any difference > 1M TRI is suspicious
|
||||
if (abs64(nDifference) > 1000000 * COIN)
|
||||
throw runtime_error(strprintf(
|
||||
"recalculatesupply: calculated supply differs by %s TRI - this is abnormal, refusing to update",
|
||||
FormatMoney(abs64(nDifference)).c_str()));
|
||||
|
||||
// Update the chain tip's money supply
|
||||
pindexBest->nMoneySupply = nCalculatedSupply;
|
||||
|
||||
// Persist to LevelDB
|
||||
CTxDB txdbWrite;
|
||||
if (!txdbWrite.WriteBlockIndex(CDiskBlockIndex(pindexBest)))
|
||||
throw runtime_error("recalculatesupply: failed to write updated block index");
|
||||
|
||||
Object result;
|
||||
result.push_back(Pair("height", (int)nBestHeight));
|
||||
result.push_back(Pair("old_supply", ValueFromAmount(nOldSupply)));
|
||||
result.push_back(Pair("new_supply", ValueFromAmount(nCalculatedSupply)));
|
||||
result.push_back(Pair("difference", ValueFromAmount(nDifference)));
|
||||
result.push_back(Pair("utxo_count", nUtxoCount));
|
||||
return result;
|
||||
}
|
||||
|
||||
// triangles: get information of sync-checkpoint
|
||||
Value getcheckpoint(const Array& params, bool fHelp)
|
||||
{
|
||||
@@ -416,6 +463,48 @@ Value getblockchaininfo(const Array& params, bool fHelp)
|
||||
return obj;
|
||||
}
|
||||
|
||||
Value gencheckpoints(const Array& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() > 1)
|
||||
throw runtime_error(
|
||||
"gencheckpoints [interval]\n"
|
||||
"Generates hardcoded checkpoint entries for checkpoints.cpp.\n"
|
||||
"Outputs C++ map entries for every <interval> blocks (default 5000)\n"
|
||||
"from genesis to current tip, ready to paste into the source code.");
|
||||
|
||||
int nInterval = 5000;
|
||||
if (params.size() > 0)
|
||||
nInterval = params[0].get_int();
|
||||
if (nInterval < 1)
|
||||
throw runtime_error("Interval must be >= 1");
|
||||
|
||||
std::string result;
|
||||
result += "// Generated by gencheckpoints RPC at height " + std::to_string(nBestHeight) + "\n";
|
||||
result += "static MapCheckpoints mapCheckpoints = {\n";
|
||||
|
||||
// Always include genesis
|
||||
CBlockIndex* pindex = mapBlockIndex[hashBestChain];
|
||||
while (pindex->pprev)
|
||||
pindex = pindex->pprev;
|
||||
|
||||
bool first = true;
|
||||
while (pindex)
|
||||
{
|
||||
if (pindex->nHeight % nInterval == 0 || pindex->nHeight == nBestHeight)
|
||||
{
|
||||
if (!first)
|
||||
result += ",\n";
|
||||
result += " {" + std::to_string(pindex->nHeight) + ", uint256(\"0x"
|
||||
+ pindex->GetBlockHash().GetHex() + "\")}";
|
||||
first = false;
|
||||
}
|
||||
pindex = pindex->pnext;
|
||||
}
|
||||
result += "\n};\n";
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Address index RPC commands
|
||||
// ============================================================================
|
||||
@@ -598,3 +687,212 @@ Value getaddresstxids(const Array& params, bool fHelp)
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Value getchaintips(const Array& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() != 0)
|
||||
throw runtime_error(
|
||||
"getchaintips\n"
|
||||
"Return information about all known tips in the block tree,\n"
|
||||
"including the main chain as well as orphaned branches.\n"
|
||||
"Essential for diagnosing chain forks.");
|
||||
|
||||
// Collect all block indices that are tips (nothing points to them as pprev)
|
||||
set<CBlockIndex*> setTips;
|
||||
|
||||
{
|
||||
LOCK(cs_main);
|
||||
for (const auto& item : mapBlockIndex)
|
||||
setTips.insert(item.second);
|
||||
|
||||
for (const auto& item : mapBlockIndex) {
|
||||
if (item.second->pprev)
|
||||
setTips.erase(item.second->pprev);
|
||||
}
|
||||
}
|
||||
|
||||
Array res;
|
||||
LOCK(cs_main);
|
||||
for (CBlockIndex* tip : setTips)
|
||||
{
|
||||
Object obj;
|
||||
obj.push_back(Pair("height", tip->nHeight));
|
||||
obj.push_back(Pair("hash", tip->GetBlockHash().GetHex()));
|
||||
obj.push_back(Pair("chaintrust", tip->nChainTrust.GetHex()));
|
||||
|
||||
int branchLen = 0;
|
||||
CBlockIndex* pWalk = tip;
|
||||
while (pWalk && !pWalk->IsInMainChain()) {
|
||||
branchLen++;
|
||||
pWalk = pWalk->pprev;
|
||||
}
|
||||
|
||||
string status;
|
||||
if (tip == pindexBest)
|
||||
status = "active";
|
||||
else if (branchLen > 0)
|
||||
status = "valid-fork";
|
||||
else
|
||||
status = "unknown";
|
||||
|
||||
obj.push_back(Pair("branchlen", branchLen));
|
||||
obj.push_back(Pair("status", status));
|
||||
|
||||
if (pWalk && !tip->IsInMainChain())
|
||||
obj.push_back(Pair("forkpoint", pWalk->GetBlockHash().GetHex()));
|
||||
|
||||
res.push_back(obj);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
Value invalidateblock(const Array& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() != 1)
|
||||
throw runtime_error(
|
||||
"invalidateblock <hash>\n"
|
||||
"Permanently marks a block as invalid and rewinds the chain.\n"
|
||||
"This forces the node to reorganize to the parent chain.\n"
|
||||
"Use reconsiderblock to undo.");
|
||||
|
||||
string strHash = params[0].get_str();
|
||||
uint256 hash(strHash);
|
||||
|
||||
LOCK(cs_main);
|
||||
|
||||
if (mapBlockIndex.count(hash) == 0)
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
|
||||
|
||||
CBlockIndex* pindex = mapBlockIndex[hash];
|
||||
|
||||
if (pindex->IsInMainChain())
|
||||
{
|
||||
CTxDB txdb;
|
||||
if (!txdb.TxnBegin())
|
||||
throw runtime_error("Failed to begin transaction.");
|
||||
|
||||
CBlockIndex* pindexWalk = pindexBest;
|
||||
|
||||
// Disconnect blocks from best back to (but not including) pindex's parent
|
||||
while (pindexWalk && pindexWalk != pindex->pprev)
|
||||
{
|
||||
CBlock block;
|
||||
if (!block.ReadFromDisk(pindexWalk))
|
||||
throw runtime_error("Failed to read block from disk during invalidation.");
|
||||
|
||||
if (!block.DisconnectBlock(txdb, pindexWalk))
|
||||
throw runtime_error("Failed to disconnect block during invalidation.");
|
||||
|
||||
// Remove disconnected PoS blocks from setStakeSeen
|
||||
if (pindexWalk->IsProofOfStake())
|
||||
{
|
||||
extern set<pair<COutPoint, unsigned int> > setStakeSeen;
|
||||
setStakeSeen.erase(make_pair(pindexWalk->prevoutStake, pindexWalk->nStakeTime));
|
||||
}
|
||||
|
||||
pindexWalk->pprev->pnext = NULL;
|
||||
pindexWalk = pindexWalk->pprev;
|
||||
}
|
||||
|
||||
// Update best block to the fork point
|
||||
if (pindex->pprev) {
|
||||
pindexBest = pindex->pprev;
|
||||
extern uint256 nBestChainTrust;
|
||||
nBestChainTrust = pindexBest->nChainTrust;
|
||||
nBestHeight = pindexBest->nHeight;
|
||||
txdb.WriteHashBestChain(pindexBest->GetBlockHash());
|
||||
if (!txdb.TxnCommit())
|
||||
throw runtime_error("Failed to commit transaction.");
|
||||
printf("invalidateblock: rewound chain to height %d hash %s\n",
|
||||
pindexBest->nHeight, pindexBest->GetBlockHash().ToString().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
return Value::null;
|
||||
}
|
||||
|
||||
Value reconsiderblock(const Array& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() != 1)
|
||||
throw runtime_error(
|
||||
"reconsiderblock <hash>\n"
|
||||
"Reconsiders a previously invalidated block for activation.\n"
|
||||
"If it has more chain trust than current best, triggers a reorg.");
|
||||
|
||||
string strHash = params[0].get_str();
|
||||
uint256 hash(strHash);
|
||||
|
||||
LOCK(cs_main);
|
||||
|
||||
if (mapBlockIndex.count(hash) == 0)
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found");
|
||||
|
||||
CBlockIndex* pindex = mapBlockIndex[hash];
|
||||
|
||||
extern uint256 nBestChainTrust;
|
||||
if (pindex->nChainTrust > nBestChainTrust)
|
||||
{
|
||||
CBlock block;
|
||||
if (!block.ReadFromDisk(pindex))
|
||||
throw runtime_error("Failed to read block from disk.");
|
||||
|
||||
CTxDB txdb;
|
||||
block.SetBestChain(txdb, pindex);
|
||||
printf("reconsiderblock: reconsidered block %s at height %d, new best height=%d\n",
|
||||
hash.ToString().c_str(), pindex->nHeight, nBestHeight);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("reconsiderblock: block %s does not have more trust than current best\n",
|
||||
hash.ToString().c_str());
|
||||
}
|
||||
|
||||
return Value::null;
|
||||
}
|
||||
|
||||
Value dumputxoset(const Array& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() < 1 || params.size() > 2)
|
||||
throw runtime_error(
|
||||
"dumputxoset <filename> [nheaders]\n"
|
||||
"Dumps the current UTXO set and recent block headers to a binary snapshot file.\n"
|
||||
"The snapshot can be used by new nodes to skip initial block download.\n"
|
||||
"\nArguments:\n"
|
||||
"1. filename (string, required) Destination file path\n"
|
||||
"2. nheaders (int, optional, default=2000) Number of block headers to include\n"
|
||||
"\nResult:\n"
|
||||
"{\n"
|
||||
" \"filename\": \"...\",\n"
|
||||
" \"height\": n,\n"
|
||||
" \"blockhash\": \"...\",\n"
|
||||
" \"file_size\": n\n"
|
||||
"}");
|
||||
|
||||
string filename = params[0].get_str();
|
||||
unsigned int nHeaders = UTXO_SNAPSHOT_DEFAULT_HEADERS;
|
||||
if (params.size() > 1)
|
||||
nHeaders = params[1].get_int();
|
||||
|
||||
if (nHeaders < 100)
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "nheaders must be at least 100");
|
||||
|
||||
boost::filesystem::path destPath(filename);
|
||||
std::string strError;
|
||||
|
||||
if (!UtxoSnapshot::DumpSnapshot(destPath, nHeaders, strError))
|
||||
throw runtime_error("dumputxoset failed: " + strError);
|
||||
|
||||
// Get file size
|
||||
int64_t nFileSize = 0;
|
||||
if (boost::filesystem::exists(destPath))
|
||||
nFileSize = (int64_t)boost::filesystem::file_size(destPath);
|
||||
|
||||
Object result;
|
||||
result.push_back(Pair("filename", filename));
|
||||
result.push_back(Pair("height", nBestHeight));
|
||||
result.push_back(Pair("blockhash", hashBestChain.GetHex()));
|
||||
result.push_back(Pair("file_size", nFileSize));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -78,7 +78,8 @@ Value getstakinginfo(const Array& params, bool fHelp)
|
||||
if (fHelp || params.size() != 0)
|
||||
throw runtime_error(
|
||||
"getstakinginfo\n"
|
||||
"Returns an object containing staking-related information.");
|
||||
"Returns an object containing staking-related information.\n"
|
||||
"Includes diagnostic details about why staking may be disabled.");
|
||||
|
||||
uint64_t nMinWeight = 0, nMaxWeight = 0, nWeight = 0;
|
||||
pwalletMain->GetStakeWeight(*pwalletMain, nMinWeight, nMaxWeight, nWeight);
|
||||
@@ -87,6 +88,26 @@ Value getstakinginfo(const Array& params, bool fHelp)
|
||||
bool staking = nLastCoinStakeSearchInterval && nWeight;
|
||||
int nExpectedTime = staking ? (nTargetSpacing * nNetworkWeight / nWeight) : -1;
|
||||
|
||||
// Diagnostic: determine why staking might be disabled
|
||||
Array stakingDisabledReasons;
|
||||
if (!GetBoolArg("-staking", true))
|
||||
stakingDisabledReasons.push_back("staking disabled via -staking=0 flag");
|
||||
|
||||
if (pwalletMain->IsLocked())
|
||||
stakingDisabledReasons.push_back("wallet is locked (use walletpassphrase <pw> <timeout> true)");
|
||||
|
||||
if (vNodes.empty())
|
||||
stakingDisabledReasons.push_back("no network connections (need at least 1 peer)");
|
||||
|
||||
if (IsInitialBlockDownload())
|
||||
stakingDisabledReasons.push_back("initial block download in progress");
|
||||
|
||||
if (nWeight == 0)
|
||||
stakingDisabledReasons.push_back("no mature coins available (coins need 520 confirmations)");
|
||||
|
||||
if (!staking && nLastCoinStakeSearchInterval == 0)
|
||||
stakingDisabledReasons.push_back("stake miner thread not running");
|
||||
|
||||
Object obj;
|
||||
|
||||
obj.push_back(Pair("enabled", GetBoolArg("-staking", true)));
|
||||
@@ -105,441 +126,23 @@ Value getstakinginfo(const Array& params, bool fHelp)
|
||||
|
||||
obj.push_back(Pair("expectedtime", nExpectedTime));
|
||||
|
||||
// Add detailed diagnostics
|
||||
obj.push_back(Pair("walletlocked", pwalletMain->IsLocked()));
|
||||
obj.push_back(Pair("walletunlockedforstakingonly", fWalletUnlockStakingOnly));
|
||||
obj.push_back(Pair("connections", (int)vNodes.size()));
|
||||
obj.push_back(Pair("initialblockdownload", IsInitialBlockDownload()));
|
||||
obj.push_back(Pair("maturecoins", nWeight > 0));
|
||||
|
||||
if (!stakingDisabledReasons.empty())
|
||||
obj.push_back(Pair("staking_disabled_reasons", stakingDisabledReasons));
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
Value getworkex(const Array& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() > 2)
|
||||
throw runtime_error(
|
||||
"getworkex [data, coinbase]\n"
|
||||
"If [data, coinbase] is not specified, returns extended work data.\n"
|
||||
);
|
||||
|
||||
if (vNodes.empty())
|
||||
throw JSONRPCError(-9, "Triangles is not connected!");
|
||||
|
||||
if (IsInitialBlockDownload())
|
||||
throw JSONRPCError(-10, "Triangles is downloading blocks...");
|
||||
|
||||
if (pindexBest->nHeight >= CUTOFF_POW_BLOCK)
|
||||
throw JSONRPCError(RPC_MISC_ERROR, "No more PoW blocks");
|
||||
|
||||
typedef map<uint256, pair<CBlock*, CScript> > mapNewBlock_t;
|
||||
static mapNewBlock_t mapNewBlock;
|
||||
static vector<CBlock*> vNewBlock;
|
||||
static CReserveKey reservekey(pwalletMain);
|
||||
|
||||
if (params.size() == 0)
|
||||
{
|
||||
// Update block
|
||||
static unsigned int nTransactionsUpdatedLast;
|
||||
static CBlockIndex* pindexPrev;
|
||||
static int64_t nStart;
|
||||
static CBlock* pblock;
|
||||
if (pindexPrev != pindexBest ||
|
||||
(nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60))
|
||||
{
|
||||
if (pindexPrev != pindexBest)
|
||||
{
|
||||
// Deallocate old blocks since they're obsolete now
|
||||
mapNewBlock.clear();
|
||||
for (CBlock* pblock : vNewBlock)
|
||||
delete pblock;
|
||||
vNewBlock.clear();
|
||||
}
|
||||
nTransactionsUpdatedLast = nTransactionsUpdated;
|
||||
pindexPrev = pindexBest;
|
||||
nStart = GetTime();
|
||||
|
||||
// Create new block
|
||||
pblock = CreateNewBlock(pwalletMain);
|
||||
if (!pblock)
|
||||
throw JSONRPCError(-7, "Out of memory");
|
||||
vNewBlock.push_back(pblock);
|
||||
}
|
||||
|
||||
// Update nTime
|
||||
pblock->nTime = max(pindexPrev->GetPastTimeLimit()+1, GetAdjustedTime());
|
||||
pblock->nNonce = 0;
|
||||
|
||||
// Update nExtraNonce
|
||||
static unsigned int nExtraNonce = 0;
|
||||
IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
|
||||
|
||||
// Save
|
||||
mapNewBlock[pblock->hashMerkleRoot] = make_pair(pblock, pblock->vtx[0].vin[0].scriptSig);
|
||||
|
||||
// Prebuild hash buffers
|
||||
char pmidstate[32];
|
||||
char pdata[128];
|
||||
char phash1[64];
|
||||
FormatHashBuffers(pblock, pmidstate, pdata, phash1);
|
||||
|
||||
uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();
|
||||
|
||||
CTransaction coinbaseTx = pblock->vtx[0];
|
||||
std::vector<uint256> merkle = pblock->GetMerkleBranch(0);
|
||||
|
||||
Object result;
|
||||
result.push_back(Pair("data", HexStr(BEGIN(pdata), END(pdata))));
|
||||
result.push_back(Pair("target", HexStr(BEGIN(hashTarget), END(hashTarget))));
|
||||
|
||||
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ssTx << coinbaseTx;
|
||||
result.push_back(Pair("coinbase", HexStr(ssTx.begin(), ssTx.end())));
|
||||
|
||||
Array merkle_arr;
|
||||
|
||||
for (uint256 merkleh : merkle) {
|
||||
merkle_arr.push_back(HexStr(BEGIN(merkleh), END(merkleh)));
|
||||
}
|
||||
|
||||
result.push_back(Pair("merkle", merkle_arr));
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Parse parameters
|
||||
vector<unsigned char> vchData = ParseHex(params[0].get_str());
|
||||
vector<unsigned char> coinbase;
|
||||
|
||||
if(params.size() == 2)
|
||||
coinbase = ParseHex(params[1].get_str());
|
||||
|
||||
if (vchData.size() != 128)
|
||||
throw JSONRPCError(-8, "Invalid parameter");
|
||||
|
||||
CBlock* pdata = (CBlock*)&vchData[0];
|
||||
|
||||
// Byte reverse
|
||||
for (int i = 0; i < 128/4; i++)
|
||||
((unsigned int*)pdata)[i] = ByteReverse(((unsigned int*)pdata)[i]);
|
||||
|
||||
// Get saved block
|
||||
if (!mapNewBlock.count(pdata->hashMerkleRoot))
|
||||
return false;
|
||||
CBlock* pblock = mapNewBlock[pdata->hashMerkleRoot].first;
|
||||
|
||||
pblock->nTime = pdata->nTime;
|
||||
pblock->nNonce = pdata->nNonce;
|
||||
|
||||
if(coinbase.size() == 0)
|
||||
pblock->vtx[0].vin[0].scriptSig = mapNewBlock[pdata->hashMerkleRoot].second;
|
||||
else
|
||||
// Deserialize custom coinbase transaction from miner
|
||||
CDataStream(coinbase, SER_NETWORK, PROTOCOL_VERSION) >> pblock->vtx[0];
|
||||
|
||||
pblock->hashMerkleRoot = pblock->BuildMerkleTree();
|
||||
|
||||
return CheckWork(pblock, *pwalletMain, reservekey);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Value getwork(const Array& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() > 1)
|
||||
throw runtime_error(
|
||||
"getwork [data]\n"
|
||||
"If [data] is not specified, returns formatted hash data to work on:\n"
|
||||
" \"midstate\" : precomputed hash state after hashing the first half of the data (DEPRECATED)\n" // deprecated
|
||||
" \"data\" : block data\n"
|
||||
" \"hash1\" : formatted hash buffer for second hash (DEPRECATED)\n" // deprecated
|
||||
" \"target\" : little endian hash target\n"
|
||||
"If [data] is specified, tries to solve the block and returns true if it was successful.");
|
||||
|
||||
if (vNodes.empty())
|
||||
throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Triangles is not connected!");
|
||||
|
||||
if (IsInitialBlockDownload())
|
||||
throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Triangles is downloading blocks...");
|
||||
|
||||
if (pindexBest->nHeight >= CUTOFF_POW_BLOCK)
|
||||
throw JSONRPCError(RPC_MISC_ERROR, "No more PoW blocks");
|
||||
|
||||
typedef map<uint256, pair<CBlock*, CScript> > mapNewBlock_t;
|
||||
// NOTE: Thread safety issue - static variables accessed by multiple RPC threads
|
||||
// without mutex protection. Low priority since PoW ended at block 9000 and
|
||||
// getwork is rarely used. Consider adding std::mutex if usage increases.
|
||||
static mapNewBlock_t mapNewBlock;
|
||||
static vector<CBlock*> vNewBlock;
|
||||
static CReserveKey reservekey(pwalletMain);
|
||||
|
||||
if (params.size() == 0)
|
||||
{
|
||||
// Update block
|
||||
static unsigned int nTransactionsUpdatedLast;
|
||||
static CBlockIndex* pindexPrev;
|
||||
static int64_t nStart;
|
||||
static CBlock* pblock;
|
||||
if (pindexPrev != pindexBest ||
|
||||
(nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 60))
|
||||
{
|
||||
if (pindexPrev != pindexBest)
|
||||
{
|
||||
// Deallocate old blocks since they're obsolete now
|
||||
mapNewBlock.clear();
|
||||
for (CBlock* pblock : vNewBlock)
|
||||
delete pblock;
|
||||
vNewBlock.clear();
|
||||
}
|
||||
|
||||
// Clear pindexPrev so future getworks make a new block, despite any failures from here on
|
||||
pindexPrev = NULL;
|
||||
|
||||
// Store the pindexBest used before CreateNewBlock, to avoid races
|
||||
nTransactionsUpdatedLast = nTransactionsUpdated;
|
||||
CBlockIndex* pindexPrevNew = pindexBest;
|
||||
nStart = GetTime();
|
||||
|
||||
// Create new block
|
||||
pblock = CreateNewBlock(pwalletMain);
|
||||
if (!pblock)
|
||||
throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory");
|
||||
vNewBlock.push_back(pblock);
|
||||
|
||||
// Need to update only after we know CreateNewBlock succeeded
|
||||
pindexPrev = pindexPrevNew;
|
||||
}
|
||||
|
||||
// Update nTime
|
||||
pblock->UpdateTime(pindexPrev);
|
||||
pblock->nNonce = 0;
|
||||
|
||||
// Update nExtraNonce
|
||||
static unsigned int nExtraNonce = 0;
|
||||
IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
|
||||
|
||||
// Save
|
||||
mapNewBlock[pblock->hashMerkleRoot] = make_pair(pblock, pblock->vtx[0].vin[0].scriptSig);
|
||||
|
||||
// Pre-build hash buffers
|
||||
char pmidstate[32];
|
||||
char pdata[128];
|
||||
char phash1[64];
|
||||
FormatHashBuffers(pblock, pmidstate, pdata, phash1);
|
||||
|
||||
uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();
|
||||
|
||||
Object result;
|
||||
result.push_back(Pair("midstate", HexStr(BEGIN(pmidstate), END(pmidstate)))); // deprecated
|
||||
result.push_back(Pair("data", HexStr(BEGIN(pdata), END(pdata))));
|
||||
result.push_back(Pair("hash1", HexStr(BEGIN(phash1), END(phash1)))); // deprecated
|
||||
result.push_back(Pair("target", HexStr(BEGIN(hashTarget), END(hashTarget))));
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Parse parameters
|
||||
vector<unsigned char> vchData = ParseHex(params[0].get_str());
|
||||
if (vchData.size() != 128)
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter");
|
||||
CBlock* pdata = (CBlock*)&vchData[0];
|
||||
|
||||
// Byte reverse
|
||||
for (int i = 0; i < 128/4; i++)
|
||||
((unsigned int*)pdata)[i] = ByteReverse(((unsigned int*)pdata)[i]);
|
||||
|
||||
// Get saved block
|
||||
if (!mapNewBlock.count(pdata->hashMerkleRoot))
|
||||
return false;
|
||||
CBlock* pblock = mapNewBlock[pdata->hashMerkleRoot].first;
|
||||
|
||||
pblock->nTime = pdata->nTime;
|
||||
pblock->nNonce = pdata->nNonce;
|
||||
pblock->vtx[0].vin[0].scriptSig = mapNewBlock[pdata->hashMerkleRoot].second;
|
||||
pblock->hashMerkleRoot = pblock->BuildMerkleTree();
|
||||
|
||||
return CheckWork(pblock, *pwalletMain, reservekey);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Value getblocktemplate(const Array& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() > 1)
|
||||
throw runtime_error(
|
||||
"getblocktemplate [params]\n"
|
||||
"Returns data needed to construct a block to work on:\n"
|
||||
" \"version\" : block version\n"
|
||||
" \"previousblockhash\" : hash of current highest block\n"
|
||||
" \"transactions\" : contents of non-coinbase transactions that should be included in the next block\n"
|
||||
" \"coinbaseaux\" : data that should be included in coinbase\n"
|
||||
" \"coinbasevalue\" : maximum allowable input to coinbase transaction, including the generation award and transaction fees\n"
|
||||
" \"target\" : hash target\n"
|
||||
" \"mintime\" : minimum timestamp appropriate for next block\n"
|
||||
" \"curtime\" : current timestamp\n"
|
||||
" \"mutable\" : list of ways the block template may be changed\n"
|
||||
" \"noncerange\" : range of valid nonces\n"
|
||||
" \"sigoplimit\" : limit of sigops in blocks\n"
|
||||
" \"sizelimit\" : limit of block size\n"
|
||||
" \"bits\" : compressed target of next block\n"
|
||||
" \"height\" : height of the next block");
|
||||
|
||||
std::string strMode = "template";
|
||||
if (params.size() > 0)
|
||||
{
|
||||
const Object& oparam = params[0].get_obj();
|
||||
const Value& modeval = find_value(oparam, "mode");
|
||||
if (modeval.type() == str_type)
|
||||
strMode = modeval.get_str();
|
||||
else if (modeval.type() == null_type)
|
||||
{
|
||||
/* Do nothing */
|
||||
}
|
||||
else
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode");
|
||||
}
|
||||
|
||||
if (strMode != "template")
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode");
|
||||
|
||||
if (vNodes.empty())
|
||||
throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Triangles is not connected!");
|
||||
|
||||
if (IsInitialBlockDownload())
|
||||
throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Triangles is downloading blocks...");
|
||||
|
||||
if (pindexBest->nHeight >= CUTOFF_POW_BLOCK)
|
||||
throw JSONRPCError(RPC_MISC_ERROR, "No more PoW blocks");
|
||||
|
||||
static CReserveKey reservekey(pwalletMain);
|
||||
|
||||
// Update block
|
||||
static unsigned int nTransactionsUpdatedLast;
|
||||
static CBlockIndex* pindexPrev;
|
||||
static int64_t nStart;
|
||||
static CBlock* pblock;
|
||||
if (pindexPrev != pindexBest ||
|
||||
(nTransactionsUpdated != nTransactionsUpdatedLast && GetTime() - nStart > 5))
|
||||
{
|
||||
// Clear pindexPrev so future calls make a new block, despite any failures from here on
|
||||
pindexPrev = NULL;
|
||||
|
||||
// Store the pindexBest used before CreateNewBlock, to avoid races
|
||||
nTransactionsUpdatedLast = nTransactionsUpdated;
|
||||
CBlockIndex* pindexPrevNew = pindexBest;
|
||||
nStart = GetTime();
|
||||
|
||||
// Create new block
|
||||
if(pblock)
|
||||
{
|
||||
delete pblock;
|
||||
pblock = NULL;
|
||||
}
|
||||
pblock = CreateNewBlock(pwalletMain);
|
||||
if (!pblock)
|
||||
throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory");
|
||||
|
||||
// Need to update only after we know CreateNewBlock succeeded
|
||||
pindexPrev = pindexPrevNew;
|
||||
}
|
||||
|
||||
// Update nTime
|
||||
pblock->UpdateTime(pindexPrev);
|
||||
pblock->nNonce = 0;
|
||||
|
||||
Array transactions;
|
||||
map<uint256, int64_t> setTxIndex;
|
||||
int i = 0;
|
||||
CTxDB txdb("r");
|
||||
for (CTransaction& tx : pblock->vtx)
|
||||
{
|
||||
uint256 txHash = tx.GetHash();
|
||||
setTxIndex[txHash] = i++;
|
||||
|
||||
if (tx.IsCoinBase() || tx.IsCoinStake())
|
||||
continue;
|
||||
|
||||
Object entry;
|
||||
|
||||
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
|
||||
ssTx << tx;
|
||||
entry.push_back(Pair("data", HexStr(ssTx.begin(), ssTx.end())));
|
||||
|
||||
entry.push_back(Pair("hash", txHash.GetHex()));
|
||||
|
||||
MapPrevTx mapInputs;
|
||||
map<uint256, CTxIndex> mapUnused;
|
||||
bool fInvalid = false;
|
||||
if (tx.FetchInputs(txdb, mapUnused, false, false, mapInputs, fInvalid))
|
||||
{
|
||||
entry.push_back(Pair("fee", (int64_t)(tx.GetValueIn(mapInputs) - tx.GetValueOut())));
|
||||
|
||||
Array deps;
|
||||
for (MapPrevTx::value_type& inp : mapInputs)
|
||||
{
|
||||
if (setTxIndex.count(inp.first))
|
||||
deps.push_back(setTxIndex[inp.first]);
|
||||
}
|
||||
entry.push_back(Pair("depends", deps));
|
||||
|
||||
int64_t nSigOps = tx.GetLegacySigOpCount();
|
||||
nSigOps += tx.GetP2SHSigOpCount(mapInputs);
|
||||
entry.push_back(Pair("sigops", nSigOps));
|
||||
}
|
||||
|
||||
transactions.push_back(entry);
|
||||
}
|
||||
|
||||
Object aux;
|
||||
aux.push_back(Pair("flags", HexStr(COINBASE_FLAGS.begin(), COINBASE_FLAGS.end())));
|
||||
|
||||
uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256();
|
||||
|
||||
static Array aMutable;
|
||||
if (aMutable.empty())
|
||||
{
|
||||
aMutable.push_back("time");
|
||||
aMutable.push_back("transactions");
|
||||
aMutable.push_back("prevblock");
|
||||
}
|
||||
|
||||
Object result;
|
||||
result.push_back(Pair("version", pblock->nVersion));
|
||||
result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex()));
|
||||
result.push_back(Pair("transactions", transactions));
|
||||
result.push_back(Pair("coinbaseaux", aux));
|
||||
result.push_back(Pair("coinbasevalue", (int64_t)pblock->vtx[0].vout[0].nValue));
|
||||
result.push_back(Pair("target", hashTarget.GetHex()));
|
||||
result.push_back(Pair("mintime", (int64_t)pindexPrev->GetPastTimeLimit()+1));
|
||||
result.push_back(Pair("mutable", aMutable));
|
||||
result.push_back(Pair("noncerange", "00000000ffffffff"));
|
||||
result.push_back(Pair("sigoplimit", (int64_t)MAX_BLOCK_SIGOPS));
|
||||
result.push_back(Pair("sizelimit", (int64_t)MAX_BLOCK_SIZE));
|
||||
result.push_back(Pair("curtime", (int64_t)pblock->nTime));
|
||||
result.push_back(Pair("bits", HexBits(pblock->nBits)));
|
||||
result.push_back(Pair("height", (int64_t)(pindexPrev->nHeight+1)));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Value submitblock(const Array& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() < 1 || params.size() > 2)
|
||||
throw runtime_error(
|
||||
"submitblock <hex data> [optional-params-obj]\n"
|
||||
"[optional-params-obj] parameter is currently ignored.\n"
|
||||
"Attempts to submit new block to network.");
|
||||
|
||||
vector<unsigned char> blockData(ParseHex(params[0].get_str()));
|
||||
CDataStream ssBlock(blockData, SER_NETWORK, PROTOCOL_VERSION);
|
||||
CBlock block;
|
||||
try {
|
||||
ssBlock >> block;
|
||||
}
|
||||
catch (std::exception &e) {
|
||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed");
|
||||
}
|
||||
|
||||
bool fAccepted = ProcessBlock(NULL, &block);
|
||||
if (!fAccepted)
|
||||
return "rejected";
|
||||
|
||||
return Value::null;
|
||||
}
|
||||
// PoW mining RPCs (getwork, getworkex, getblocktemplate, submitblock) removed.
|
||||
// PoW ended at block 9000 (CUTOFF_POW_BLOCK). These dead-code mining pool
|
||||
// interfaces were removed to reduce false-positive antivirus detections,
|
||||
// since AV engines pattern-match nonce-incrementing loops and mining pool
|
||||
// protocols as "cryptominer" signatures.
|
||||
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <algorithm>
|
||||
#include "net.h"
|
||||
#include "addrman.h"
|
||||
#include "trianglesrpc.h"
|
||||
@@ -14,37 +15,6 @@
|
||||
using namespace json_spirit;
|
||||
using namespace std;
|
||||
|
||||
namespace {
|
||||
|
||||
const char* BootstrapModeToString(NetBootstrap::BootstrapMode mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case NetBootstrap::BOOTSTRAP_LEGACY:
|
||||
return "legacy";
|
||||
case NetBootstrap::BOOTSTRAP_TOR_MIXED:
|
||||
return "tor_mixed";
|
||||
case NetBootstrap::BOOTSTRAP_TOR_ONLY:
|
||||
return "tor_only";
|
||||
}
|
||||
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
NetBootstrap::BootstrapMode GetBootstrapModeForRPC()
|
||||
{
|
||||
const bool torEnabled = GetBoolArg("-tor", false) || GetBoolArg("-proxy", false);
|
||||
const bool onlyTor = GetBoolArg("-onlynet", false) && GetArg("-onlynet", "") == "tor";
|
||||
|
||||
if (onlyTor)
|
||||
return NetBootstrap::BOOTSTRAP_TOR_ONLY;
|
||||
if (torEnabled)
|
||||
return NetBootstrap::BOOTSTRAP_TOR_MIXED;
|
||||
return NetBootstrap::BOOTSTRAP_LEGACY;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Value getnetworkinfo(const Array& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() != 0)
|
||||
@@ -60,11 +30,10 @@ Value getnetworkinfo(const Array& params, bool fHelp)
|
||||
Object healthObj;
|
||||
healthObj.push_back(Pair("connectedpeers", health.connectedPeers));
|
||||
healthObj.push_back(Pair("torpeers", health.torPeers));
|
||||
healthObj.push_back(Pair("clearnetpeers", health.clearnetPeers));
|
||||
healthObj.push_back(Pair("bootstrapped", health.isBootstrapped));
|
||||
healthObj.push_back(Pair("syncing", health.isSyncing));
|
||||
healthObj.push_back(Pair("lastblocktime", static_cast<boost::int64_t>(health.lastBlockTime)));
|
||||
healthObj.push_back(Pair("bootstrapmode", BootstrapModeToString(GetBootstrapModeForRPC())));
|
||||
healthObj.push_back(Pair("networkmode", "tor_native"));
|
||||
|
||||
Object obj;
|
||||
obj.push_back(Pair("version", FormatFullVersion()));
|
||||
@@ -128,6 +97,9 @@ Value getpeerinfo(const Array& params, bool fHelp)
|
||||
obj.push_back(Pair("inbound", stats.fInbound));
|
||||
obj.push_back(Pair("startingheight", stats.nStartingHeight));
|
||||
obj.push_back(Pair("banscore", stats.nMisbehavior));
|
||||
obj.push_back(Pair("pingtime", stats.nPingUsecTime > 0 ? (double)stats.nPingUsecTime / 1000000.0 : -1.0));
|
||||
obj.push_back(Pair("blocksdelivered", stats.nBlocksDelivered));
|
||||
obj.push_back(Pair("avglatency", stats.nAvgBlockLatencyUs > 0 ? (double)stats.nAvgBlockLatencyUs / 1000.0 : -1.0));
|
||||
|
||||
ret.push_back(obj);
|
||||
}
|
||||
@@ -201,6 +173,76 @@ Value sendalert(const Array& params, bool fHelp)
|
||||
return result;
|
||||
}
|
||||
|
||||
Value addnode(const Array& params, bool fHelp)
|
||||
{
|
||||
string strCommand;
|
||||
if (params.size() == 2)
|
||||
strCommand = params[1].get_str();
|
||||
if (fHelp || params.size() != 2 ||
|
||||
(strCommand != "onetry" && strCommand != "add" && strCommand != "remove"))
|
||||
throw runtime_error(
|
||||
"addnode <node> <add|remove|onetry>\n"
|
||||
"Attempts to add or remove a node from the addnode list,\n"
|
||||
"or try a connection to a node once.\n"
|
||||
"<node> must be a .onion address (Tor-native network).");
|
||||
|
||||
string strNode = params[0].get_str();
|
||||
|
||||
// Tor-native: require .onion addresses
|
||||
if (strNode.find(".onion") == string::npos)
|
||||
throw runtime_error("Only .onion addresses are supported on this network.");
|
||||
|
||||
if (strCommand == "onetry")
|
||||
{
|
||||
CAddress addr;
|
||||
CNode* pnode = ConnectNode(addr, strNode.c_str());
|
||||
if (!pnode)
|
||||
throw runtime_error("Failed to connect to node (may already be connected or unreachable).");
|
||||
pnode->Release();
|
||||
return Value::null;
|
||||
}
|
||||
|
||||
// For add/remove, manipulate the -addnode list that ThreadOpenAddedConnections uses
|
||||
LOCK(cs_vNodes);
|
||||
vector<string>& vAddedNodes = mapMultiArgs["-addnode"];
|
||||
|
||||
if (strCommand == "add")
|
||||
{
|
||||
for (const string& existing : vAddedNodes)
|
||||
if (existing == strNode)
|
||||
throw runtime_error("Node already added.");
|
||||
vAddedNodes.push_back(strNode);
|
||||
}
|
||||
else if (strCommand == "remove")
|
||||
{
|
||||
auto it = std::find(vAddedNodes.begin(), vAddedNodes.end(), strNode);
|
||||
if (it == vAddedNodes.end())
|
||||
throw runtime_error("Node not found in addnode list.");
|
||||
vAddedNodes.erase(it);
|
||||
}
|
||||
|
||||
return Value::null;
|
||||
}
|
||||
|
||||
Value disconnectnode(const Array& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() != 1)
|
||||
throw runtime_error(
|
||||
"disconnectnode <node>\n"
|
||||
"Immediately disconnects from the specified node.");
|
||||
|
||||
string strNode = params[0].get_str();
|
||||
|
||||
LOCK(cs_vNodes);
|
||||
for (CNode* pnode : vNodes) {
|
||||
if (pnode->addrName == strNode || pnode->addr.ToString() == strNode) {
|
||||
pnode->CloseSocketDisconnect();
|
||||
return Value::null;
|
||||
}
|
||||
}
|
||||
throw runtime_error("Node not found.");
|
||||
}
|
||||
|
||||
Value getseedlist(const Array& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() != 0)
|
||||
@@ -225,3 +267,85 @@ Value getseedlist(const Array& params, bool fHelp)
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Value getnetworkstability(const Array& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() != 0)
|
||||
throw runtime_error(
|
||||
"getnetworkstability\n"
|
||||
"Returns detailed network stability metrics including peer quality,\n"
|
||||
"connection health, and isolation risk assessment.");
|
||||
|
||||
int nOutbound = 0, nInbound = 0, nTotal = 0;
|
||||
int64_t nBestPing = INT64_MAX, nWorstPing = 0, nTotalPing = 0;
|
||||
int nPingCount = 0;
|
||||
int nTotalBlocksDelivered = 0;
|
||||
int64_t nOldestConnection = 0;
|
||||
int64_t nNewestConnection = INT64_MAX;
|
||||
|
||||
{
|
||||
LOCK(cs_vNodes);
|
||||
nTotal = vNodes.size();
|
||||
for (CNode* pnode : vNodes) {
|
||||
if (pnode->fInbound)
|
||||
nInbound++;
|
||||
else
|
||||
nOutbound++;
|
||||
|
||||
if (pnode->nPingUsecTime > 0) {
|
||||
nTotalPing += pnode->nPingUsecTime;
|
||||
nPingCount++;
|
||||
if (pnode->nPingUsecTime < nBestPing)
|
||||
nBestPing = pnode->nPingUsecTime;
|
||||
if (pnode->nPingUsecTime > nWorstPing)
|
||||
nWorstPing = pnode->nPingUsecTime;
|
||||
}
|
||||
|
||||
nTotalBlocksDelivered += pnode->nBlocksDelivered;
|
||||
|
||||
int64_t uptime = GetTime() - pnode->nTimeConnected;
|
||||
if (uptime > nOldestConnection)
|
||||
nOldestConnection = uptime;
|
||||
if (uptime < nNewestConnection)
|
||||
nNewestConnection = uptime;
|
||||
}
|
||||
}
|
||||
|
||||
// Determine isolation risk
|
||||
string strRisk;
|
||||
if (nOutbound == 0 && nInbound == 0)
|
||||
strRisk = "critical";
|
||||
else if (nOutbound == 0)
|
||||
strRisk = "high";
|
||||
else if (nOutbound == 1)
|
||||
strRisk = "elevated";
|
||||
else if (nOutbound < 3)
|
||||
strRisk = "moderate";
|
||||
else
|
||||
strRisk = "low";
|
||||
|
||||
Object obj;
|
||||
obj.push_back(Pair("connections_total", nTotal));
|
||||
obj.push_back(Pair("connections_outbound", nOutbound));
|
||||
obj.push_back(Pair("connections_inbound", nInbound));
|
||||
obj.push_back(Pair("isolation_risk", strRisk));
|
||||
obj.push_back(Pair("blocks_delivered_total", nTotalBlocksDelivered));
|
||||
obj.push_back(Pair("known_addresses", (int)addrman.size()));
|
||||
|
||||
Object pingObj;
|
||||
pingObj.push_back(Pair("best_ms", nPingCount > 0 ? (double)nBestPing / 1000.0 : -1.0));
|
||||
pingObj.push_back(Pair("worst_ms", nPingCount > 0 ? (double)nWorstPing / 1000.0 : -1.0));
|
||||
pingObj.push_back(Pair("avg_ms", nPingCount > 0 ? (double)nTotalPing / nPingCount / 1000.0 : -1.0));
|
||||
pingObj.push_back(Pair("peers_measured", nPingCount));
|
||||
obj.push_back(Pair("ping", pingObj));
|
||||
|
||||
Object uptimeObj;
|
||||
uptimeObj.push_back(Pair("newest_sec", nTotal > 0 ? (boost::int64_t)nNewestConnection : 0));
|
||||
uptimeObj.push_back(Pair("oldest_sec", nTotal > 0 ? (boost::int64_t)nOldestConnection : 0));
|
||||
obj.push_back(Pair("connection_uptime", uptimeObj));
|
||||
|
||||
obj.push_back(Pair("seconds_since_last_block", (boost::int64_t)(GetTime() - nTimeBestReceived)));
|
||||
obj.push_back(Pair("current_height", nBestHeight));
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
@@ -370,20 +370,20 @@ Value signrawtransaction(const Array& params, bool fHelp)
|
||||
{
|
||||
CTransaction tempTx;
|
||||
MapPrevTx mapPrevTx;
|
||||
MapPrevTx mapEmpty;
|
||||
CTxDB txdb("r");
|
||||
map<uint256, CTxIndex> unused;
|
||||
bool fInvalid;
|
||||
|
||||
// FetchInputs aborts on failure, so we go one at a time.
|
||||
tempTx.vin.push_back(mergedTx.vin[i]);
|
||||
tempTx.FetchInputs(txdb, unused, false, false, mapPrevTx, fInvalid);
|
||||
tempTx.FetchInputs(txdb, mapEmpty, false, false, mapPrevTx, fInvalid);
|
||||
|
||||
// Copy results into mapPrevOut:
|
||||
for (const CTxIn& txin : tempTx.vin)
|
||||
{
|
||||
const uint256& prevHash = txin.prevout.hash;
|
||||
if (mapPrevTx.count(prevHash) && mapPrevTx[prevHash].second.vout.size()>txin.prevout.n)
|
||||
mapPrevOut[txin.prevout] = mapPrevTx[prevHash].second.vout[txin.prevout.n].scriptPubKey;
|
||||
MapPrevTx::const_iterator mi = mapPrevTx.find(txin.prevout);
|
||||
if (mi != mapPrevTx.end())
|
||||
mapPrevOut[txin.prevout] = mi->second.scriptPubKey;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||