Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 22de8630cd | |||
| da5e5f9a8a | |||
| 998bd51425 | |||
| 7d0b2806e0 | |||
| 73c183d1c0 | |||
| 65b9417c28 | |||
| ed87543153 | |||
| 6e9dbb1aa9 | |||
| a6ec711cfa | |||
| 6877aeaddb | |||
| 60067e1a88 | |||
| e91ccd8786 | |||
| 96fb7d5040 | |||
| 47cf8abbda | |||
| 2abd494fec | |||
| 378b0370e3 |
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(C:/msys64/msys2_shell.cmd -mingw64 -defterm -no-start -here -c \"ls /mingw64/lib/libboost_system* 2>/dev/null\")"
|
||||
"Bash(C:/msys64/msys2_shell.cmd -mingw64 -defterm -no-start -here -c \"ls /mingw64/lib/libboost_system* 2>/dev/null\")",
|
||||
"Bash(git tag:*)"
|
||||
],
|
||||
"additionalDirectories": [
|
||||
"C:\\msys64\\mingw64\\bin"
|
||||
|
||||
@@ -73,7 +73,99 @@
|
||||
"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(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:*)"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
VERSION: "5.2.0"
|
||||
VERSION: "5.3.5"
|
||||
|
||||
jobs:
|
||||
build-windows-qt:
|
||||
@@ -147,6 +147,12 @@ jobs:
|
||||
- name: Build
|
||||
run: make -j$(nproc)
|
||||
|
||||
- name: Run unit tests
|
||||
run: |
|
||||
cd src
|
||||
make -f makefile.unix test_triangles -j$(nproc)
|
||||
./test_triangles --log_level=test_suite
|
||||
|
||||
- name: Strip binary
|
||||
run: strip --strip-all triangles-qt
|
||||
|
||||
@@ -183,6 +189,12 @@ jobs:
|
||||
mkdir -p obj
|
||||
make -f makefile.unix -j$(nproc)
|
||||
|
||||
- name: Run unit tests
|
||||
run: |
|
||||
cd src
|
||||
make -f makefile.unix test_triangles -j$(nproc)
|
||||
./test_triangles --log_level=test_suite
|
||||
|
||||
- name: Strip binary
|
||||
run: strip --strip-all src/trianglesd
|
||||
|
||||
|
||||
@@ -52,3 +52,6 @@ triangles.conf
|
||||
*.key
|
||||
*.cert
|
||||
*.gpg
|
||||
*.o
|
||||
src/trianglesd
|
||||
src/obj/
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
# Triangles Codebase Cleanup Notes
|
||||
|
||||
## Overview
|
||||
Systematic code quality improvements for the Triangles cryptocurrency codebase (v5.3.4+).
|
||||
|
||||
**Goal:** Improve maintainability without changing behavior or breaking consensus.
|
||||
|
||||
## Inventory
|
||||
|
||||
### TODOs/FIXMEs Found (38 total)
|
||||
|
||||
#### High Priority (Affects Safety/Correctness)
|
||||
- `rpcmining.cpp:263` - **Thread safety issue** in mapNewBlock (static variable, no mutex)
|
||||
- `walletmodel.cpp:249` - **Potential collision** in balance calculation
|
||||
- `smessage.cpp:863, 2219, 2373` - **File size limit** (files must be split if >2GB)
|
||||
|
||||
#### Medium Priority (Encapsulation/Security)
|
||||
- `protocol.h:50, 100, 132` - Public members should be private (3 locations)
|
||||
- `wallet.h:378` - nOrderPos calculation should move elsewhere
|
||||
- `wallet.cpp:733, 1732` - Change output handling needs improvement
|
||||
- `rpcwallet.cpp:1474, 1513, 1569` - SecureString operator= missing (forced .c_str())
|
||||
|
||||
#### Low Priority (Nice-to-Have)
|
||||
- `util.cpp:1322` - Disabled feature needs verification
|
||||
- `tor/tor_embedded.cpp:209` - Tor 0.4.9+ shutdown API upgrade
|
||||
- `init.cpp:442` - Remaining sanity checks (see Bitcoin issue #4081)
|
||||
- `rpcmining.cpp:232` - DRM comment (unclear what it means)
|
||||
- `smessage.cpp:*` - Various improvements (hash inclusion, thread safety, defaults)
|
||||
- `qt/*` - UI improvements (decrypt not supported, message filtering, OSX startup)
|
||||
|
||||
#### External/Third-Party (Don't Touch)
|
||||
- `leveldb/*` - LevelDB library TODOs (upstream issues)
|
||||
|
||||
## Code Quality Issues
|
||||
|
||||
### Using namespace std (37 files)
|
||||
All in .cpp files - **this is fine for .cpp**, problematic only in headers.
|
||||
No headers have this issue, so **no action needed**.
|
||||
|
||||
### Printf/Cout Usage (56 files)
|
||||
Most cryptocurrency code uses printf for early init/error handling before logging is available.
|
||||
**Review needed:** Check if these are legitimate early-init cases or should use LogPrintf.
|
||||
|
||||
## Cleanup Plan (Safest → Riskiest)
|
||||
|
||||
### Phase 1: Documentation & Comments ✅ SAFE
|
||||
1. Document all TODOs with context (why deferred, what's needed)
|
||||
2. Add function-level comments for complex logic
|
||||
3. Improve inline comments for clarity
|
||||
|
||||
### Phase 2: Low-Risk Code Quality 🟨 MEDIUM RISK
|
||||
4. Fix compiler warnings (-Wall -Wextra)
|
||||
5. Add const correctness where missing
|
||||
6. Remove commented-out dead code
|
||||
7. Standardize code formatting (if inconsistent)
|
||||
|
||||
### Phase 3: Functional Improvements 🟥 HIGH RISK (Skip for now)
|
||||
8. Fix thread safety issue in rpcmining.cpp (requires testing)
|
||||
9. Improve protocol.h encapsulation (may affect other code)
|
||||
10. Address >2GB file handling in smessage.cpp
|
||||
|
||||
## Decisions
|
||||
|
||||
### What NOT to Change
|
||||
- **Consensus code** - main.cpp (validation), kernel.cpp (PoS), miner.cpp (staking)
|
||||
- **Serialization** - Any READWRITE, serialize/deserialize code
|
||||
- **Protocol constants** - Network message types, version numbers
|
||||
- **Third-party code** - leveldb/, tor/, sph_types.h, xxhash/, lz4/
|
||||
|
||||
### What's Safe to Change
|
||||
- Comments and documentation
|
||||
- Variable names (in non-consensus code)
|
||||
- Code organization (splitting large functions)
|
||||
- Logging statements
|
||||
- UI code (qt/)
|
||||
- RPC interface (as long as API contract preserved)
|
||||
|
||||
## Initial Cleanup (2026-03-22)
|
||||
|
||||
### Actions Taken
|
||||
1. Created this documentation file
|
||||
2. Created cleanup/desloppify branch
|
||||
3. Inventoried all TODOs/FIXMEs
|
||||
|
||||
### Next Steps
|
||||
1. Add documentation comments to TODO items
|
||||
2. Review printf/cout usage patterns
|
||||
3. Check for compiler warnings
|
||||
4. Consider low-risk improvements
|
||||
|
||||
## Notes
|
||||
- This is a Bitcoin-derived codebase, so many patterns follow Bitcoin Core conventions
|
||||
- Recent v5.3.x work already modernized to C++17 and removed Boost - good foundation
|
||||
- Code is generally well-structured; main improvements are documentation and minor cleanup
|
||||
@@ -3,7 +3,7 @@
|
||||
# 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 USE_QRCODE=1 USE_UPNP=-
|
||||
# Command: C:/msys64/mingw64/bin/qmake-qt5.exe -o Makefile triangles-qt.pro -spec win32-g++ CONFIG+=release
|
||||
#############################################################################
|
||||
|
||||
MAKEFILE = Makefile
|
||||
@@ -156,7 +156,7 @@ Makefile: triangles-qt.pro C:/msys64/mingw64/share/qt5/mkspecs/win32-g++/qmake.c
|
||||
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 USE_QRCODE=1 USE_UPNP=-
|
||||
$(QMAKE) -o Makefile triangles-qt.pro -spec win32-g++ CONFIG+=release
|
||||
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:
|
||||
@@ -244,7 +244,7 @@ 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 USE_QRCODE=1 USE_UPNP=-
|
||||
@$(QMAKE) -o Makefile triangles-qt.pro -spec win32-g++ CONFIG+=release
|
||||
|
||||
qmake_all: FORCE
|
||||
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
# TODO/FIXME Documentation
|
||||
|
||||
Detailed context for each TODO/FIXME in the codebase.
|
||||
|
||||
## Critical (Needs Attention)
|
||||
|
||||
### src/rpcmining.cpp:263 - Thread Safety Issue
|
||||
```cpp
|
||||
static mapNewBlock_t mapNewBlock; // FIXME: thread safety
|
||||
```
|
||||
**Issue:** Static variable accessed by multiple RPC threads without mutex protection.
|
||||
**Impact:** Potential race condition in getwork RPC (used for mining).
|
||||
**Status:** Low priority - PoW mining ended at block 9000, this code path rarely used.
|
||||
**Fix:** Add std::mutex and lock_guard if getwork usage increases.
|
||||
|
||||
### src/qt/walletmodel.cpp:249 - Collision Risk
|
||||
```cpp
|
||||
if((total + nFeeRequired) > nBalance) // FIXME: could cause collisions in the future
|
||||
```
|
||||
**Issue:** Balance check may have edge case causing transaction collisions.
|
||||
**Context:** In createTransaction fee calculation loop.
|
||||
**Status:** Needs investigation - unclear what "collisions" means here.
|
||||
**Fix:** Review Bitcoin Core's current implementation of this logic.
|
||||
|
||||
### src/smessage.cpp - File Size Limits
|
||||
```cpp
|
||||
// Lines 863, 2219, 2373: "TODO files must be split if > 2GB"
|
||||
```
|
||||
**Issue:** Secure message storage files not split when exceeding 2GB.
|
||||
**Impact:** May fail on 32-bit systems or with large message volumes.
|
||||
**Status:** Low priority - unlikely to reach 2GB in practice.
|
||||
**Fix:** Implement file rotation when approaching 2GB limit.
|
||||
|
||||
## Medium Priority (Encapsulation/API)
|
||||
|
||||
### src/protocol.h - Make Members Private
|
||||
```cpp
|
||||
// Lines 50, 100, 132: "TODO: make private (improves encapsulation)"
|
||||
```
|
||||
**Issue:** CAddress, CInv, CMessageHeader have public data members.
|
||||
**Impact:** Poor encapsulation, harder to maintain invariants.
|
||||
**Status:** Deferred - would require extensive refactoring.
|
||||
**Fix:** Add getter/setter methods, make members private, update all call sites.
|
||||
|
||||
### src/wallet.h:378 - nOrderPos Calculation
|
||||
```cpp
|
||||
nOrderPos = -1; // TODO: calculate elsewhere
|
||||
```
|
||||
**Issue:** Transaction ordering position calculated in constructor.
|
||||
**Impact:** Minor - works but not ideal separation of concerns.
|
||||
**Status:** Deferred - no functional issue.
|
||||
**Fix:** Move calculation to WalletDB when transaction is added.
|
||||
|
||||
### src/rpcwallet.cpp - SecureString Operator
|
||||
```cpp
|
||||
// Lines 1474, 1513, 1569: "TODO: get rid of this .c_str()"
|
||||
```
|
||||
**Issue:** SecureString missing operator=(std::string).
|
||||
**Impact:** Forced to use .c_str() which exposes password temporarily.
|
||||
**Status:** Deferred - would require SecureString class modification.
|
||||
**Fix:** Add `SecureString& operator=(const std::string&)` method.
|
||||
|
||||
## Low Priority (Nice-to-Have)
|
||||
|
||||
### src/util.cpp:1322 - Disabled Feature
|
||||
```cpp
|
||||
// TODO: This is currently disabled because it needs to be verified to work
|
||||
```
|
||||
**Context:** File descriptor management code.
|
||||
**Status:** Intentionally disabled pending verification.
|
||||
**Fix:** Test thoroughly, then enable if needed.
|
||||
|
||||
### src/tor/tor_embedded.cpp:209 - Tor Shutdown API
|
||||
```cpp
|
||||
// TODO: Tor 0.4.9+ may add tor_api_shutdown(), use it when available
|
||||
```
|
||||
**Context:** Embedded Tor cleanup.
|
||||
**Status:** Waiting for upstream Tor API.
|
||||
**Fix:** Check Tor 0.4.9+ releases for new API, integrate when stable.
|
||||
|
||||
### src/init.cpp:442 - Sanity Checks
|
||||
```cpp
|
||||
// TODO: remaining sanity checks, see #4081
|
||||
```
|
||||
**Context:** Bitcoin Core issue #4081 - additional startup sanity checks.
|
||||
**Status:** Deferred - core checks already in place.
|
||||
**Fix:** Review Bitcoin Core's current sanity check implementation.
|
||||
|
||||
### src/rpcmining.cpp:232 - DRM Comment
|
||||
```cpp
|
||||
CDataStream(coinbase, SER_NETWORK, PROTOCOL_VERSION) >> pblock->vtx[0]; // FIXME - DRM!
|
||||
```
|
||||
**Issue:** Unclear what "DRM" means here - likely "Data Race Maybe"?
|
||||
**Status:** Needs clarification from original author.
|
||||
**Fix:** Investigate if there's an actual issue, otherwise remove comment.
|
||||
|
||||
## Deferred (External/Low Impact)
|
||||
|
||||
### LevelDB TODOs (src/leveldb/*)
|
||||
**Status:** Upstream LevelDB issues - don't modify embedded library.
|
||||
**Action:** None - track upstream LevelDB project.
|
||||
|
||||
### Qt TODOs (src/qt/*)
|
||||
**Status:** UI improvements, not critical.
|
||||
**Action:** Track as nice-to-have enhancements.
|
||||
|
||||
### Secure Message TODOs (src/smessage.cpp)
|
||||
Multiple minor improvements suggested:
|
||||
- Include hash in certain operations
|
||||
- Improve thread shutdown
|
||||
- Set default recv/recvAnon behavior
|
||||
- Update outbox after PoW completes
|
||||
|
||||
**Status:** Non-critical enhancements.
|
||||
**Action:** Consider for future encrypted messaging upgrades.
|
||||
|
||||
## Summary
|
||||
|
||||
**Critical:** 3 items (thread safety, balance collision, file limits)
|
||||
**Medium:** 6 items (encapsulation, SecureString)
|
||||
**Low:** 5 items (disabled features, upstream APIs)
|
||||
**Deferred:** ~24 items (external libs, minor enhancements)
|
||||
|
||||
**Recommendation:** Focus on documenting critical items in code comments, defer fixes until specific issues arise.
|
||||
+2
-2
@@ -53,8 +53,8 @@ std::string CUnsignedAlert::ToString() const
|
||||
return strprintf(
|
||||
"CAlert(\n"
|
||||
" nVersion = %d\n"
|
||||
" nRelayUntil = %"PRId64"\n"
|
||||
" nExpiration = %"PRId64"\n"
|
||||
" nRelayUntil = %" PRId64 "\n"
|
||||
" nExpiration = %" PRId64 "\n"
|
||||
" nID = %d\n"
|
||||
" nCancel = %d\n"
|
||||
" setCancel = %s\n"
|
||||
|
||||
@@ -0,0 +1,378 @@
|
||||
// Copyright (c) 2024 Triangles developers
|
||||
// Distributed under the MIT/X11 software license
|
||||
|
||||
#include "bootstrap.h"
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
using boost::asio::ip::tcp;
|
||||
|
||||
namespace Bootstrap {
|
||||
|
||||
bool NeedsBootstrap(const fs::path& dataDir)
|
||||
{
|
||||
return !fs::exists(dataDir / "blk0001.dat");
|
||||
}
|
||||
|
||||
bool DownloadFile(const std::string& host, const std::string& urlPath,
|
||||
const fs::path& destPath,
|
||||
ProgressCallback progressFn,
|
||||
std::string& strError)
|
||||
{
|
||||
try {
|
||||
boost::asio::io_context io_context;
|
||||
tcp::resolver resolver(io_context);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
tcp::socket socket(io_context);
|
||||
boost::asio::connect(socket, endpoints);
|
||||
|
||||
// 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));
|
||||
}
|
||||
}
|
||||
|
||||
// Open output file
|
||||
FILE* file = fopen(destPath.string().c_str(), "wb");
|
||||
if (!file) {
|
||||
strError = "Cannot create file: " + destPath.string();
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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) {
|
||||
fclose(file);
|
||||
fs::remove(destPath);
|
||||
strError = "Network error: " + ec.message();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
|
||||
// Verify download size if Content-Length was provided
|
||||
if (content_length > 0 && bytes_written != content_length) {
|
||||
fs::remove(destPath);
|
||||
strError = "Incomplete download: got " + std::to_string(bytes_written)
|
||||
+ " of " + std::to_string(content_length) + " bytes";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
} catch (std::exception& e) {
|
||||
strError = std::string("Download failed: ") + e.what();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool FetchFileList(const std::string& host,
|
||||
std::vector<std::string>& files,
|
||||
std::string& strError)
|
||||
{
|
||||
// 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))
|
||||
return false;
|
||||
|
||||
// Read lines
|
||||
std::ifstream in(tmpPath.string().c_str());
|
||||
if (!in.is_open()) {
|
||||
strError = "Cannot read downloaded file list";
|
||||
return false;
|
||||
}
|
||||
|
||||
files.clear();
|
||||
std::string line;
|
||||
while (std::getline(in, line)) {
|
||||
boost::trim(line);
|
||||
if (!line.empty() && line[0] != '#')
|
||||
files.push_back(line);
|
||||
}
|
||||
in.close();
|
||||
fs::remove(tmpPath);
|
||||
|
||||
if (files.empty()) {
|
||||
strError = "File list is empty";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// --- tar.gz bootstrap support ---
|
||||
|
||||
namespace {
|
||||
|
||||
// Parse a tar octal field (ASCII octal, null/space terminated)
|
||||
static int64_t ParseTarOctal(const char* field, size_t len)
|
||||
{
|
||||
int64_t result = 0;
|
||||
for (size_t i = 0; i < len && field[i] != '\0' && field[i] != ' '; i++) {
|
||||
if (field[i] < '0' || field[i] > '7') continue;
|
||||
result = (result << 3) | (field[i] - '0');
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Extract a tar.gz file to a destination directory
|
||||
static bool ExtractTarGz(const fs::path& tarGzPath,
|
||||
const fs::path& destDir,
|
||||
std::string& strError)
|
||||
{
|
||||
gzFile gz = gzopen(tarGzPath.string().c_str(), "rb");
|
||||
if (!gz) {
|
||||
strError = "Cannot open " + tarGzPath.string();
|
||||
return false;
|
||||
}
|
||||
|
||||
gzbuffer(gz, 262144); // 256 KB buffer for performance
|
||||
|
||||
char header[512];
|
||||
|
||||
while (true) {
|
||||
int bytesRead = gzread(gz, header, 512);
|
||||
if (bytesRead == 0) break; // EOF
|
||||
if (bytesRead != 512) {
|
||||
strError = "Truncated tar header";
|
||||
gzclose(gz);
|
||||
return false;
|
||||
}
|
||||
|
||||
// End-of-archive marker (zero block)
|
||||
bool allZero = true;
|
||||
for (int i = 0; i < 512; i++) {
|
||||
if (header[i] != 0) { allZero = false; break; }
|
||||
}
|
||||
if (allZero) break;
|
||||
|
||||
// Parse filename: name (offset 0, 100 bytes) + optional prefix (offset 345, 155 bytes)
|
||||
char name[101] = {0};
|
||||
char prefix[156] = {0};
|
||||
memcpy(name, header, 100);
|
||||
memcpy(prefix, header + 345, 155);
|
||||
|
||||
std::string fullName;
|
||||
if (prefix[0] != '\0')
|
||||
fullName = std::string(prefix) + "/" + std::string(name);
|
||||
else
|
||||
fullName = std::string(name);
|
||||
|
||||
// Security: reject absolute paths and path traversal
|
||||
if (fullName.empty() || fullName[0] == '/' || fullName.find("..") != std::string::npos) {
|
||||
strError = "Unsafe path in tar archive: " + fullName;
|
||||
gzclose(gz);
|
||||
return false;
|
||||
}
|
||||
|
||||
char typeflag = header[156];
|
||||
int64_t fileSize = ParseTarOctal(header + 124, 12);
|
||||
|
||||
if (typeflag == '5' || (!fullName.empty() && fullName.back() == '/')) {
|
||||
// Directory entry
|
||||
fs::create_directories(destDir / fullName);
|
||||
} else if (typeflag == '0' || typeflag == '\0') {
|
||||
// Regular file
|
||||
fs::path filePath = destDir / fullName;
|
||||
fs::create_directories(filePath.parent_path());
|
||||
|
||||
FILE* outFile = fopen(filePath.string().c_str(), "wb");
|
||||
if (!outFile) {
|
||||
strError = "Cannot create file: " + filePath.string();
|
||||
gzclose(gz);
|
||||
return false;
|
||||
}
|
||||
|
||||
int64_t remaining = fileSize;
|
||||
char buf[65536];
|
||||
while (remaining > 0) {
|
||||
int toRead = (remaining > (int64_t)sizeof(buf)) ? (int)sizeof(buf) : (int)remaining;
|
||||
int n = gzread(gz, buf, toRead);
|
||||
if (n <= 0) {
|
||||
fclose(outFile);
|
||||
strError = "Truncated tar data for: " + fullName;
|
||||
gzclose(gz);
|
||||
return false;
|
||||
}
|
||||
fwrite(buf, 1, n, outFile);
|
||||
remaining -= n;
|
||||
}
|
||||
fclose(outFile);
|
||||
|
||||
// Skip padding to next 512-byte boundary
|
||||
int64_t pad = (512 - (fileSize % 512)) % 512;
|
||||
if (pad > 0) {
|
||||
char padBuf[512];
|
||||
if (gzread(gz, padBuf, (unsigned)pad) != (int)pad) {
|
||||
strError = "Truncated tar padding for: " + fullName;
|
||||
gzclose(gz);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Unknown entry type - skip its data
|
||||
int64_t totalSkip = fileSize + ((512 - (fileSize % 512)) % 512);
|
||||
char skipBuf[512];
|
||||
while (totalSkip > 0) {
|
||||
int toRead = (totalSkip > 512) ? 512 : (int)totalSkip;
|
||||
if (gzread(gz, skipBuf, toRead) != toRead) break;
|
||||
totalSkip -= toRead;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gzclose(gz);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
bool DownloadBootstrap(const std::string& host,
|
||||
const fs::path& dataDir,
|
||||
ProgressCallback progressFn,
|
||||
std::string& strError)
|
||||
{
|
||||
bool gotBlockFile = false;
|
||||
|
||||
// Try downloading bootstrap.tar.gz first
|
||||
fs::path tmpTarGz = dataDir / "bootstrap.tar.gz.tmp";
|
||||
std::string tarUrl = std::string(BASE_PATH) + "bootstrap.tar.gz";
|
||||
|
||||
bool tarDownloaded = DownloadFile(host, tarUrl, tmpTarGz, progressFn, strError);
|
||||
|
||||
if (tarDownloaded) {
|
||||
bool extractOk = ExtractTarGz(tmpTarGz, dataDir, strError);
|
||||
fs::remove(tmpTarGz);
|
||||
|
||||
if (extractOk && fs::exists(dataDir / "blk0001.dat"))
|
||||
gotBlockFile = true;
|
||||
// If extraction failed, fall through to legacy path
|
||||
}
|
||||
|
||||
if (!gotBlockFile) {
|
||||
// Fallback: try filelist.txt + individual file downloads
|
||||
std::string fallbackError;
|
||||
std::vector<std::string> files;
|
||||
if (!FetchFileList(host, files, fallbackError)) {
|
||||
if (!tarDownloaded)
|
||||
strError = strError + " (fallback also failed: " + fallbackError + ")";
|
||||
else
|
||||
strError = "Extraction failed: " + strError + " (fallback also failed: " + fallbackError + ")";
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < files.size(); i++) {
|
||||
fs::path destPath = dataDir / files[i];
|
||||
fs::create_directories(destPath.parent_path());
|
||||
|
||||
std::string urlPath = std::string(BASE_PATH) + files[i];
|
||||
if (!DownloadFile(host, urlPath, destPath, progressFn, strError))
|
||||
return false;
|
||||
}
|
||||
|
||||
gotBlockFile = fs::exists(dataDir / "blk0001.dat");
|
||||
}
|
||||
|
||||
if (!gotBlockFile) {
|
||||
strError = "No blk0001.dat after download";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove any extracted txleveldb/ and database/ - they were built on
|
||||
// a different machine and won't work here. FastImportBlockFile() will
|
||||
// rebuild the index directly from blk0001.dat on next startup.
|
||||
fs::path txleveldb = dataDir / "txleveldb";
|
||||
fs::path database = dataDir / "database";
|
||||
if (fs::exists(txleveldb))
|
||||
fs::remove_all(txleveldb);
|
||||
if (fs::exists(database))
|
||||
fs::remove_all(database);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Bootstrap
|
||||
@@ -0,0 +1,46 @@
|
||||
// Copyright (c) 2024 Triangles developers
|
||||
// Distributed under the MIT/X11 software license
|
||||
|
||||
#ifndef TRIANGLES_BOOTSTRAP_H
|
||||
#define TRIANGLES_BOOTSTRAP_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
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;
|
||||
|
||||
// Progress callback: (bytesDownloaded, totalBytes)
|
||||
typedef std::function<void(int64_t, int64_t)> ProgressCallback;
|
||||
|
||||
// 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
|
||||
bool DownloadFile(const std::string& host, const std::string& urlPath,
|
||||
const boost::filesystem::path& destPath,
|
||||
ProgressCallback progressFn,
|
||||
std::string& strError);
|
||||
|
||||
// Fetch the file manifest (list of relative paths to download)
|
||||
bool FetchFileList(const std::string& host,
|
||||
std::vector<std::string>& files,
|
||||
std::string& strError);
|
||||
|
||||
// Download bootstrap.tar.gz and extract to dataDir.
|
||||
// Falls back to filelist.txt + individual file download if tar.gz unavailable.
|
||||
bool DownloadBootstrap(const std::string& host,
|
||||
const boost::filesystem::path& dataDir,
|
||||
ProgressCallback progressFn,
|
||||
std::string& strError);
|
||||
|
||||
} // namespace Bootstrap
|
||||
|
||||
#endif // TRIANGLES_BOOTSTRAP_H
|
||||
+1
-1
@@ -8,7 +8,7 @@
|
||||
// These need to be macros, as version.cpp's and triangles-qt.rc's voodoo requires it
|
||||
#define CLIENT_VERSION_MAJOR 5
|
||||
#define CLIENT_VERSION_MINOR 3
|
||||
#define CLIENT_VERSION_REVISION 2
|
||||
#define CLIENT_VERSION_REVISION 6
|
||||
#define CLIENT_VERSION_BUILD 0
|
||||
|
||||
// Converts the parameter X to a string after macro replacement on X has been performed.
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "checkpoints.h"
|
||||
#include "smessage.h"
|
||||
#include "openssl_compat.h"
|
||||
#include "bootstrap.h"
|
||||
#include "tor/tor_embedded.h"
|
||||
#include "tor/onion_v3.h"
|
||||
#include "tor/tor_process.h"
|
||||
@@ -797,6 +798,43 @@ bool AppInit2()
|
||||
for (string strDest : mapMultiArgs["-seednode"])
|
||||
AddOneShot(strDest);
|
||||
|
||||
// ********************************************************* Step 6b: bootstrap download (daemon)
|
||||
#ifndef QT_GUI
|
||||
if (GetBoolArg("-bootstrap", false))
|
||||
{
|
||||
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%%)",
|
||||
(long long)(bytesDownloaded / (1024*1024)),
|
||||
(long long)(totalBytes / (1024*1024)),
|
||||
(long long)((bytesDownloaded * 100) / totalBytes));
|
||||
fflush(stdout);
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
printf("\nBootstrap: failed: %s\n", strError.c_str());
|
||||
printf("Bootstrap: skipping, will sync from network.\n");
|
||||
} else {
|
||||
printf("\nBootstrap: done.\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// ********************************************************* Step 7: load blockchain
|
||||
|
||||
if (!bitdb.Open(GetDataDir()))
|
||||
@@ -821,6 +859,16 @@ bool AppInit2()
|
||||
if (!LoadBlockIndex())
|
||||
return InitError(_("Error loading blkindex.dat"));
|
||||
|
||||
// If the block index is empty but blk0001.dat exists (bootstrap download),
|
||||
// fast-import: build the index directly from the block file without re-writing
|
||||
// data. Batches LevelDB commits every 200K blocks for speed.
|
||||
if (nBestHeight == 0 && boost::filesystem::exists(GetDataDir() / "blk0001.dat")
|
||||
&& mapBlockIndex.size() <= 1)
|
||||
{
|
||||
uiInterface.InitMessage(_("Importing bootstrap blocks..."));
|
||||
printf("Block index empty but blk0001.dat exists - running fast import...\n");
|
||||
FastImportBlockFile();
|
||||
}
|
||||
|
||||
// as LoadBlockIndex can take several minutes, it's possible the user
|
||||
// requested to kill triangles-qt during the last operation. If so, exit.
|
||||
|
||||
+582
-38
@@ -18,6 +18,7 @@
|
||||
#endif
|
||||
#include "notificationqueue.h"
|
||||
#include "addressindex.h"
|
||||
#include <algorithm>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
@@ -93,6 +94,255 @@ int64_t nMinimumInputValue = 0;
|
||||
|
||||
extern enum Checkpoints::CPMode CheckpointsMode;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
struct CHeaderSyncNode
|
||||
{
|
||||
CBlock header;
|
||||
int nHeight;
|
||||
uint256 nChainTrust;
|
||||
bool fRequested;
|
||||
int64_t nLastRequestTime;
|
||||
};
|
||||
|
||||
static std::map<uint256, CHeaderSyncNode> mapHeaderSync;
|
||||
static uint256 hashBestHeaderSync = 0;
|
||||
|
||||
static const unsigned int MAX_HEADER_SYNC_CACHE = 50000;
|
||||
static const unsigned int HEADER_DOWNLOAD_WINDOW = 128;
|
||||
static const int64_t HEADER_REQUEST_TIMEOUT_MICROS = 30 * 1000000;
|
||||
|
||||
static uint256 GetHeaderSyncTrust(unsigned int nBits)
|
||||
{
|
||||
CBigNum bnTarget;
|
||||
bnTarget.SetCompact(nBits);
|
||||
|
||||
if (bnTarget <= 0)
|
||||
return 0;
|
||||
|
||||
return ((CBigNum(1) << 256) / (bnTarget + 1)).getuint256();
|
||||
}
|
||||
|
||||
static bool GetKnownHeaderState(const uint256& hash, int& nHeight, uint256& nChainTrust)
|
||||
{
|
||||
std::map<uint256, CBlockIndex*>::const_iterator miBlock = mapBlockIndex.find(hash);
|
||||
if (miBlock != mapBlockIndex.end())
|
||||
{
|
||||
nHeight = miBlock->second->nHeight;
|
||||
nChainTrust = miBlock->second->nChainTrust;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::map<uint256, CHeaderSyncNode>::const_iterator miHeader = mapHeaderSync.find(hash);
|
||||
if (miHeader != mapHeaderSync.end())
|
||||
{
|
||||
nHeight = miHeader->second.nHeight;
|
||||
nChainTrust = miHeader->second.nChainTrust;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool GetHeaderSyncPrevHash(const uint256& hash, uint256& hashPrev)
|
||||
{
|
||||
std::map<uint256, CHeaderSyncNode>::const_iterator miHeader = mapHeaderSync.find(hash);
|
||||
if (miHeader != mapHeaderSync.end())
|
||||
{
|
||||
hashPrev = miHeader->second.header.hashPrevBlock;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::map<uint256, CBlockIndex*>::const_iterator miBlock = mapBlockIndex.find(hash);
|
||||
if (miBlock != mapBlockIndex.end() && miBlock->second->pprev)
|
||||
{
|
||||
hashPrev = miBlock->second->pprev->GetBlockHash();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void RecomputeBestHeaderSync()
|
||||
{
|
||||
hashBestHeaderSync = 0;
|
||||
uint256 nBestTrust = 0;
|
||||
|
||||
for (std::map<uint256, CHeaderSyncNode>::const_iterator it = mapHeaderSync.begin(); it != mapHeaderSync.end(); ++it)
|
||||
{
|
||||
if (hashBestHeaderSync == 0 || it->second.nChainTrust > nBestTrust)
|
||||
{
|
||||
hashBestHeaderSync = it->first;
|
||||
nBestTrust = it->second.nChainTrust;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void PruneHeaderSync()
|
||||
{
|
||||
if (mapHeaderSync.size() <= MAX_HEADER_SYNC_CACHE)
|
||||
return;
|
||||
|
||||
printf("IBD-DIAG: header sync cache exceeded %u entries, clearing planner state\n", MAX_HEADER_SYNC_CACHE);
|
||||
mapHeaderSync.clear();
|
||||
hashBestHeaderSync = 0;
|
||||
}
|
||||
|
||||
static bool AddHeaderSyncNode(const CBlock& header, const uint256& hashHeader)
|
||||
{
|
||||
if (mapBlockIndex.count(hashHeader) || mapHeaderSync.count(hashHeader))
|
||||
return true;
|
||||
|
||||
if (!header.vtx.empty())
|
||||
return false;
|
||||
|
||||
if (header.GetBlockTime() > FutureDrift(GetAdjustedTime()))
|
||||
return false;
|
||||
|
||||
int nPrevHeight = -1;
|
||||
uint256 nPrevChainTrust = 0;
|
||||
if (!GetKnownHeaderState(header.hashPrevBlock, nPrevHeight, nPrevChainTrust))
|
||||
return false;
|
||||
|
||||
const int nHeight = nPrevHeight + 1;
|
||||
if (nHeight <= CUTOFF_POW_BLOCK && !CheckProofOfWork(hashHeader, header.nBits))
|
||||
return false;
|
||||
|
||||
CHeaderSyncNode node;
|
||||
node.header = header;
|
||||
node.nHeight = nHeight;
|
||||
node.nChainTrust = nPrevChainTrust + GetHeaderSyncTrust(header.nBits);
|
||||
node.fRequested = false;
|
||||
node.nLastRequestTime = 0;
|
||||
|
||||
mapHeaderSync.insert(std::make_pair(hashHeader, node));
|
||||
|
||||
if (hashBestHeaderSync == 0 || node.nChainTrust > mapHeaderSync[hashBestHeaderSync].nChainTrust)
|
||||
hashBestHeaderSync = hashHeader;
|
||||
|
||||
PruneHeaderSync();
|
||||
return true;
|
||||
}
|
||||
|
||||
static CBlockLocator BuildHeaderSyncLocator(uint256 hashTip)
|
||||
{
|
||||
if (hashTip == 0)
|
||||
return CBlockLocator(pindexBest);
|
||||
|
||||
std::vector<uint256> vHave;
|
||||
int nStep = 1;
|
||||
|
||||
while (hashTip != 0)
|
||||
{
|
||||
vHave.push_back(hashTip);
|
||||
|
||||
for (int i = 0; i < nStep && hashTip != 0; ++i)
|
||||
{
|
||||
uint256 hashPrev = 0;
|
||||
if (!GetHeaderSyncPrevHash(hashTip, hashPrev))
|
||||
hashTip = 0;
|
||||
else
|
||||
hashTip = hashPrev;
|
||||
}
|
||||
|
||||
if (vHave.size() > 10)
|
||||
nStep *= 2;
|
||||
}
|
||||
|
||||
vHave.push_back(!fTestNet ? hashGenesisBlockOfficial : hashGenesisBlockTestNet);
|
||||
return CBlockLocator(vHave);
|
||||
}
|
||||
|
||||
static std::vector<uint256> GetHeaderSyncDownloadPath(uint256 hashTip)
|
||||
{
|
||||
std::vector<uint256> vPath;
|
||||
|
||||
while (hashTip != 0 && !mapBlockIndex.count(hashTip))
|
||||
{
|
||||
std::map<uint256, CHeaderSyncNode>::const_iterator mi = mapHeaderSync.find(hashTip);
|
||||
if (mi == mapHeaderSync.end())
|
||||
break;
|
||||
|
||||
vPath.push_back(hashTip);
|
||||
hashTip = mi->second.header.hashPrevBlock;
|
||||
}
|
||||
|
||||
std::reverse(vPath.begin(), vPath.end());
|
||||
return vPath;
|
||||
}
|
||||
|
||||
static unsigned int CountHeaderSyncInFlight()
|
||||
{
|
||||
const int64_t nNow = GetTime() * 1000000;
|
||||
unsigned int nInFlight = 0;
|
||||
for (std::map<uint256, CHeaderSyncNode>::const_iterator it = mapHeaderSync.begin(); it != mapHeaderSync.end(); ++it)
|
||||
{
|
||||
if (it->second.fRequested && nNow - it->second.nLastRequestTime < HEADER_REQUEST_TIMEOUT_MICROS)
|
||||
++nInFlight;
|
||||
}
|
||||
return nInFlight;
|
||||
}
|
||||
|
||||
static unsigned int QueueHeaderSyncBlocks(CNode* pfrom, unsigned int nWindow)
|
||||
{
|
||||
if (!pfrom || hashBestHeaderSync == 0)
|
||||
return 0;
|
||||
|
||||
const std::vector<uint256> vPath = GetHeaderSyncDownloadPath(hashBestHeaderSync);
|
||||
if (vPath.empty())
|
||||
return 0;
|
||||
|
||||
const int64_t nNow = GetTime() * 1000000;
|
||||
unsigned int nInFlight = CountHeaderSyncInFlight();
|
||||
unsigned int nQueued = 0;
|
||||
|
||||
for (std::vector<uint256>::const_iterator it = vPath.begin(); it != vPath.end(); ++it)
|
||||
{
|
||||
if (nInFlight + nQueued >= nWindow)
|
||||
break;
|
||||
|
||||
std::map<uint256, CHeaderSyncNode>::iterator mi = mapHeaderSync.find(*it);
|
||||
if (mi == mapHeaderSync.end())
|
||||
continue;
|
||||
|
||||
if (mi->second.fRequested && nNow - mi->second.nLastRequestTime < HEADER_REQUEST_TIMEOUT_MICROS)
|
||||
continue;
|
||||
|
||||
pfrom->AskFor(CInv(MSG_BLOCK, *it));
|
||||
mi->second.fRequested = true;
|
||||
mi->second.nLastRequestTime = nNow;
|
||||
++nQueued;
|
||||
}
|
||||
|
||||
return nQueued;
|
||||
}
|
||||
|
||||
static void MarkHeaderSyncBlockAccepted(const uint256& hashBlock)
|
||||
{
|
||||
std::map<uint256, CHeaderSyncNode>::iterator mi = mapHeaderSync.find(hashBlock);
|
||||
if (mi == mapHeaderSync.end())
|
||||
return;
|
||||
|
||||
mapHeaderSync.erase(mi);
|
||||
if (hashBestHeaderSync == hashBlock)
|
||||
RecomputeBestHeaderSync();
|
||||
}
|
||||
|
||||
static void ContinueHeaderSync(CNode* pfrom, const uint256& hashTip)
|
||||
{
|
||||
if (!pfrom || hashTip == 0)
|
||||
return;
|
||||
|
||||
CBlockLocator locator = BuildHeaderSyncLocator(hashTip);
|
||||
if (locator.IsNull())
|
||||
return;
|
||||
|
||||
pfrom->PushMessage("getheaders", locator, uint256(0));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// dispatching functions
|
||||
@@ -2296,13 +2546,19 @@ bool CBlock::AddToBlockIndex(unsigned int nFile, unsigned int nBlockPos, const u
|
||||
if (!txdb.TxnBegin())
|
||||
return false;
|
||||
txdb.WriteBlockIndex(CDiskBlockIndex(pindexNew));
|
||||
if (!txdb.TxnCommit())
|
||||
return false;
|
||||
|
||||
// New best
|
||||
// New best — keep the batch open so SetBestChain can add ConnectBlock
|
||||
// writes to the same transaction, cutting the per-block commit count in half.
|
||||
if (pindexNew->nChainTrust > nBestChainTrust)
|
||||
{
|
||||
if (!SetBestChain(txdb, pindexNew))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!txdb.TxnCommit())
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pindexNew == pindexBest)
|
||||
{
|
||||
@@ -2614,8 +2870,11 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock)
|
||||
mapOrphanBlocks.insert(make_pair(hash, pblock2));
|
||||
mapOrphanBlocksByPrev.insert(make_pair(pblock2->hashPrevBlock, pblock2));
|
||||
|
||||
// Limit orphan blocks to prevent memory exhaustion
|
||||
if (mapOrphanBlocks.size() > MAX_ORPHAN_BLOCKS)
|
||||
// Limit orphan blocks to prevent memory exhaustion.
|
||||
// Allow more orphans during IBD so out-of-order blocks from parallel
|
||||
// downloads don't get evicted and re-requested.
|
||||
unsigned int nMaxOrphans = IsInitialBlockDownload() ? MAX_ORPHAN_BLOCKS_IBD : MAX_ORPHAN_BLOCKS;
|
||||
if (mapOrphanBlocks.size() > nMaxOrphans)
|
||||
{
|
||||
// Evict a random orphan
|
||||
uint256 randomhash = GetRandHash();
|
||||
@@ -2655,6 +2914,8 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock)
|
||||
if (!pblock->AcceptBlock())
|
||||
return error("ProcessBlock() : AcceptBlock FAILED");
|
||||
|
||||
MarkHeaderSyncBlockAccepted(hash);
|
||||
|
||||
// Recursively process any orphan blocks that depended on this one
|
||||
vector<uint256> vWorkQueue;
|
||||
vWorkQueue.push_back(hash);
|
||||
@@ -2667,7 +2928,10 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock)
|
||||
{
|
||||
CBlock* pblockOrphan = (*mi).second;
|
||||
if (pblockOrphan->AcceptBlock())
|
||||
{
|
||||
vWorkQueue.push_back(pblockOrphan->GetHash());
|
||||
MarkHeaderSyncBlockAccepted(pblockOrphan->GetHash());
|
||||
}
|
||||
mapOrphanBlocks.erase(pblockOrphan->GetHash());
|
||||
setStakeSeenOrphan.erase(pblockOrphan->GetProofOfStake());
|
||||
delete pblockOrphan;
|
||||
@@ -2678,6 +2942,14 @@ bool ProcessBlock(CNode* pfrom, CBlock* pblock)
|
||||
if (nBestHeight % 5000 == 0 || !IsInitialBlockDownload())
|
||||
printf("ProcessBlock: ACCEPTED block %d\n", nBestHeight);
|
||||
|
||||
if (pfrom && hashBestHeaderSync != 0)
|
||||
{
|
||||
const unsigned int nQueued = QueueHeaderSyncBlocks(pfrom, HEADER_DOWNLOAD_WINDOW);
|
||||
if (nQueued > 0)
|
||||
printf("IBD-DIAG: queued %u more blocks from header planner after accepting %s\n",
|
||||
nQueued, hash.ToString().substr(0,20).c_str());
|
||||
}
|
||||
|
||||
// triangles: if responsible for sync-checkpoint send it
|
||||
if (pfrom && !CSyncCheckpoint::strMasterPrivKey.empty())
|
||||
Checkpoints::SendSyncCheckpoint(Checkpoints::AutoSelectSyncCheckpoint());
|
||||
@@ -3023,7 +3295,14 @@ bool LoadExternalBlockFile(FILE* fileIn)
|
||||
{
|
||||
int64_t nStart = GetTimeMillis();
|
||||
|
||||
// Get file size for progress reporting
|
||||
int64_t nFileSize = 0;
|
||||
fseek(fileIn, 0, SEEK_END);
|
||||
nFileSize = ftell(fileIn);
|
||||
fseek(fileIn, 0, SEEK_SET);
|
||||
|
||||
int nLoaded = 0;
|
||||
int64_t nLastProgressReport = 0;
|
||||
{
|
||||
LOCK(cs_main);
|
||||
try {
|
||||
@@ -3074,6 +3353,20 @@ bool LoadExternalBlockFile(FILE* fileIn)
|
||||
nLoaded++;
|
||||
nPos += 4 + nSize;
|
||||
}
|
||||
|
||||
// Report progress every 1000 blocks
|
||||
if (nLoaded - nLastProgressReport >= 1000)
|
||||
{
|
||||
nLastProgressReport = nLoaded;
|
||||
if (nFileSize > 0) {
|
||||
int pct = (int)((int64_t)nPos * 100 / nFileSize);
|
||||
printf("Importing blocks... %d blocks loaded (%d%%)\n", nLoaded, pct);
|
||||
uiInterface.InitMessage(strprintf(_("Importing blocks... %d loaded (%d%%)"), nLoaded, pct));
|
||||
} else {
|
||||
printf("Importing blocks... %d blocks loaded\n", nLoaded);
|
||||
uiInterface.InitMessage(strprintf(_("Importing blocks... %d loaded"), nLoaded));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (std::exception &e) {
|
||||
@@ -3085,6 +3378,210 @@ bool LoadExternalBlockFile(FILE* fileIn)
|
||||
return nLoaded > 0;
|
||||
}
|
||||
|
||||
bool FastImportBlockFile()
|
||||
{
|
||||
// Fast block import: reads blk0001.dat and builds the block index
|
||||
// directly without re-writing block data. LevelDB writes are batched
|
||||
// every 200K blocks for speed. Only used for trusted bootstrap data
|
||||
// (blocks below the hardcoded checkpoint).
|
||||
|
||||
fs::path blkPath = GetDataDir() / "blk0001.dat";
|
||||
if (!fs::exists(blkPath))
|
||||
return false;
|
||||
|
||||
printf("FastImportBlockFile: starting from %s\n", blkPath.string().c_str());
|
||||
int64_t nStart = GetTimeMillis();
|
||||
|
||||
FILE* fileIn = fopen(blkPath.string().c_str(), "rb");
|
||||
if (!fileIn)
|
||||
return false;
|
||||
|
||||
// Get file size for progress
|
||||
fseek(fileIn, 0, SEEK_END);
|
||||
int64_t nFileSize = ftell(fileIn);
|
||||
fseek(fileIn, 0, SEEK_SET);
|
||||
|
||||
int nLoaded = 0;
|
||||
int64_t nLastProgressReport = 0;
|
||||
|
||||
{
|
||||
LOCK(cs_main);
|
||||
CAutoFile blkdat(fileIn, SER_DISK, CLIENT_VERSION);
|
||||
|
||||
CTxDB txdb;
|
||||
txdb.TxnBegin();
|
||||
|
||||
unsigned int nPos = 0;
|
||||
while (nPos != (unsigned int)-1 && blkdat.good() && !fRequestShutdown)
|
||||
{
|
||||
// Find message start bytes (same scan as LoadExternalBlockFile)
|
||||
unsigned char pchData[65536];
|
||||
do {
|
||||
fseek(blkdat, nPos, SEEK_SET);
|
||||
int nRead = fread(pchData, 1, sizeof(pchData), blkdat);
|
||||
if (nRead <= 8)
|
||||
{
|
||||
nPos = (unsigned int)-1;
|
||||
break;
|
||||
}
|
||||
void* nFind = memchr(pchData, pchMessageStart[0], nRead+1-sizeof(pchMessageStart));
|
||||
if (nFind)
|
||||
{
|
||||
if (memcmp(nFind, pchMessageStart, sizeof(pchMessageStart))==0)
|
||||
{
|
||||
nPos += ((unsigned char*)nFind - pchData) + sizeof(pchMessageStart);
|
||||
break;
|
||||
}
|
||||
nPos += ((unsigned char*)nFind - pchData) + 1;
|
||||
}
|
||||
else
|
||||
nPos += sizeof(pchData) - sizeof(pchMessageStart) + 1;
|
||||
} while(!fRequestShutdown);
|
||||
|
||||
if (nPos == (unsigned int)-1)
|
||||
break;
|
||||
|
||||
fseek(blkdat, nPos, SEEK_SET);
|
||||
unsigned int nSize;
|
||||
blkdat >> nSize;
|
||||
|
||||
if (nSize == 0 || nSize > MAX_BLOCK_SIZE)
|
||||
{
|
||||
nPos += 4 + nSize;
|
||||
continue;
|
||||
}
|
||||
|
||||
// nBlockPos = file position where the block data starts
|
||||
// (after 4-byte message start + 4-byte size)
|
||||
unsigned int nBlockPos = nPos + 4;
|
||||
|
||||
CBlock block;
|
||||
blkdat >> block;
|
||||
|
||||
uint256 hash = block.GetHash();
|
||||
if (mapBlockIndex.count(hash))
|
||||
{
|
||||
nPos += 4 + nSize;
|
||||
continue; // already indexed
|
||||
}
|
||||
|
||||
// Create CBlockIndex
|
||||
CBlockIndex* pindexNew = new CBlockIndex(1, nBlockPos, block);
|
||||
if (!pindexNew)
|
||||
break;
|
||||
|
||||
// Link to previous block
|
||||
map<uint256, CBlockIndex*>::iterator miPrev = mapBlockIndex.find(block.hashPrevBlock);
|
||||
if (miPrev != mapBlockIndex.end())
|
||||
{
|
||||
pindexNew->pprev = (*miPrev).second;
|
||||
pindexNew->nHeight = pindexNew->pprev->nHeight + 1;
|
||||
}
|
||||
|
||||
// Chain trust
|
||||
pindexNew->nChainTrust = (pindexNew->pprev ? pindexNew->pprev->nChainTrust : 0) + pindexNew->GetBlockTrust();
|
||||
|
||||
// Stake entropy bit
|
||||
pindexNew->SetStakeEntropyBit(block.GetStakeEntropyBit());
|
||||
|
||||
// Stake modifier (minimal for blocks far below checkpoint)
|
||||
int nCheckpointHeight = Checkpoints::GetTotalBlocksEstimate();
|
||||
if (pindexNew->nHeight >= nCheckpointHeight - 1000)
|
||||
{
|
||||
uint64_t nStakeModifier = 0;
|
||||
bool fGeneratedStakeModifier = false;
|
||||
ComputeNextStakeModifier(pindexNew->pprev, nStakeModifier, fGeneratedStakeModifier);
|
||||
pindexNew->SetStakeModifier(nStakeModifier, fGeneratedStakeModifier);
|
||||
}
|
||||
else
|
||||
{
|
||||
pindexNew->SetStakeModifier(0, pindexNew->nHeight == 0);
|
||||
}
|
||||
pindexNew->nStakeModifierChecksum = GetStakeModifierChecksum(pindexNew);
|
||||
|
||||
// Money supply tracking
|
||||
pindexNew->nMint = 0;
|
||||
pindexNew->nMoneySupply = (pindexNew->pprev ? pindexNew->pprev->nMoneySupply : 0);
|
||||
|
||||
// PoS stake seen set
|
||||
if (pindexNew->IsProofOfStake())
|
||||
setStakeSeen.insert(make_pair(pindexNew->prevoutStake, pindexNew->nStakeTime));
|
||||
|
||||
// Insert into mapBlockIndex
|
||||
map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first;
|
||||
pindexNew->phashBlock = &((*mi).first);
|
||||
|
||||
// Link pnext for previous block
|
||||
if (pindexNew->pprev)
|
||||
pindexNew->pprev->pnext = pindexNew;
|
||||
|
||||
// Write block index to batch
|
||||
txdb.WriteBlockIndex(CDiskBlockIndex(pindexNew));
|
||||
|
||||
// Build tx index entries
|
||||
unsigned int nTxPos = nBlockPos + ::GetSerializeSize(CBlock(), SER_DISK, CLIENT_VERSION)
|
||||
- (2 * GetSizeOfCompactSize(0)) + GetSizeOfCompactSize(block.vtx.size());
|
||||
for (unsigned int i = 0; i < block.vtx.size(); i++)
|
||||
{
|
||||
const CTransaction& tx = block.vtx[i];
|
||||
CDiskTxPos posThisTx(1, nBlockPos, nTxPos);
|
||||
txdb.UpdateTxIndex(tx.GetHash(), CTxIndex(posThisTx, tx.vout.size()));
|
||||
nTxPos += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION);
|
||||
}
|
||||
|
||||
// Update best chain
|
||||
if (pindexNew->nChainTrust > nBestChainTrust)
|
||||
{
|
||||
hashBestChain = hash;
|
||||
pindexBest = pindexNew;
|
||||
pblockindexFBBHLast = NULL;
|
||||
nBestHeight = pindexNew->nHeight;
|
||||
nBestChainTrust = pindexNew->nChainTrust;
|
||||
nTimeBestReceived = GetTime();
|
||||
}
|
||||
|
||||
// Set genesis block
|
||||
if (pindexGenesisBlock == NULL && pindexNew->nHeight == 0)
|
||||
pindexGenesisBlock = pindexNew;
|
||||
|
||||
nLoaded++;
|
||||
nPos += 4 + nSize;
|
||||
|
||||
// Batch commit every 200K blocks for LevelDB efficiency
|
||||
if (nLoaded % 200000 == 0)
|
||||
{
|
||||
txdb.WriteHashBestChain(hashBestChain);
|
||||
txdb.TxnCommit();
|
||||
txdb.TxnBegin();
|
||||
}
|
||||
|
||||
// Report progress every 5000 blocks to keep GUI responsive.
|
||||
// AppInit2 runs on the GUI thread, so uiInterface.InitMessage
|
||||
// triggers processEvents() which prevents the window from freezing.
|
||||
if (nLoaded % 5000 == 0)
|
||||
{
|
||||
int pct = (nFileSize > 0) ? (int)((int64_t)nPos * 100 / nFileSize) : 0;
|
||||
printf("FastImport: %d blocks indexed (%d%%)\n", nLoaded, pct);
|
||||
uiInterface.InitMessage(strprintf(_("Importing blocks... %d indexed (%d%%)"), nLoaded, pct));
|
||||
}
|
||||
}
|
||||
|
||||
// Final commit
|
||||
if (pindexBest)
|
||||
{
|
||||
txdb.WriteHashBestChain(hashBestChain);
|
||||
|
||||
// Write sync checkpoint
|
||||
Checkpoints::WriteSyncCheckpoint(hashBestChain);
|
||||
}
|
||||
txdb.TxnCommit();
|
||||
}
|
||||
|
||||
nTransactionsUpdated++;
|
||||
printf("FastImportBlockFile: indexed %d blocks in %"PRId64"ms\n", nLoaded, GetTimeMillis() - nStart);
|
||||
return nLoaded > 0;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// CAlert
|
||||
@@ -3288,22 +3785,33 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
|
||||
}
|
||||
}
|
||||
|
||||
// Ask connected nodes for block updates
|
||||
// During IBD, always request blocks from any valid peer (critical for reconnection)
|
||||
// Ask connected nodes for block updates.
|
||||
// During IBD, request blocks from every valid peer to maximize download
|
||||
// parallelism. Multiple peers sending overlapping inv ranges is harmless
|
||||
// (AlreadyHave filters duplicates) but ensures we discover and download
|
||||
// blocks from the fastest available source.
|
||||
static int nAskedForBlocks = 0;
|
||||
bool fIBD = IsInitialBlockDownload();
|
||||
bool fShouldAsk = !pfrom->fClient && !pfrom->fOneShot &&
|
||||
(pfrom->nStartingHeight > (nBestHeight - 144)) &&
|
||||
(pfrom->nVersion < NOBLKS_VERSION_START ||
|
||||
pfrom->nVersion >= NOBLKS_VERSION_END) &&
|
||||
(IsInitialBlockDownload() || nAskedForBlocks < 1 || vNodes.size() <= 1);
|
||||
(fIBD || nAskedForBlocks < 1 || vNodes.size() <= 1);
|
||||
printf("IBD-DIAG: version handler: peer=%s height=%d ourHeight=%d fClient=%d fOneShot=%d shouldAsk=%d nAskedForBlocks=%d IBD=%d\n",
|
||||
pfrom->addr.ToString().c_str(), pfrom->nStartingHeight, nBestHeight,
|
||||
pfrom->fClient, pfrom->fOneShot, fShouldAsk, nAskedForBlocks, IsInitialBlockDownload());
|
||||
pfrom->fClient, pfrom->fOneShot, fShouldAsk, nAskedForBlocks, fIBD);
|
||||
if (fShouldAsk)
|
||||
{
|
||||
nAskedForBlocks++;
|
||||
pfrom->PushGetBlocks(pindexBest, uint256(0));
|
||||
printf("IBD-DIAG: sent getblocks from height %d to peer %s\n", nBestHeight, pfrom->addr.ToString().c_str());
|
||||
// During IBD, also send getheaders to scout the chain structure.
|
||||
// Headers are ~80 bytes each (vs full blocks at ~1-2KB for PoS),
|
||||
// so we learn about future blocks much faster. The headers handler
|
||||
// will AskFor each unknown block, pre-populating the download queue.
|
||||
if (fIBD)
|
||||
pfrom->PushGetHeaders(pindexBest, uint256(0));
|
||||
printf("IBD-DIAG: sent getblocks%s from height %d to peer %s\n",
|
||||
fIBD ? "+getheaders" : "", nBestHeight, pfrom->addr.ToString().c_str());
|
||||
}
|
||||
|
||||
// Relay alerts
|
||||
@@ -3392,7 +3900,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
|
||||
hashKey = Hash(BEGIN(hashKey), END(hashKey));
|
||||
mapMix.insert(make_pair(hashKey, pnode));
|
||||
}
|
||||
int nRelayNodes = fReachable ? 2 : 1; // limited relaying of addresses outside our network(s)
|
||||
// Small network: relay to more peers so addresses propagate quickly
|
||||
int nRelayNodes = fReachable ? (int)mapMix.size() : 1;
|
||||
for (multimap<uint256, CNode*>::iterator mi = mapMix.begin(); mi != mapMix.end() && nRelayNodes-- > 0; ++mi)
|
||||
((*mi).second)->PushAddress(addr);
|
||||
}
|
||||
@@ -3496,11 +4005,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
|
||||
// Trigger them to send a getblocks request for the next batch of inventory
|
||||
if (inv.hash == pfrom->hashContinue)
|
||||
{
|
||||
// triangles: send latest proof-of-work block to allow the
|
||||
// download node to accept as orphan (proof-of-stake
|
||||
// block might be rejected by stake connection check)
|
||||
// Send the best block hash to trigger the next getblocks.
|
||||
// Original code sent the last PoW block, but since PoW ended
|
||||
// at block 9000, that always sent an ancient block causing
|
||||
// thousands of redundant round-trips through known blocks.
|
||||
vector<CInv> vInv;
|
||||
vInv.push_back(CInv(MSG_BLOCK, GetLastBlockIndex(pindexBest, false)->GetBlockHash()));
|
||||
vInv.push_back(CInv(MSG_BLOCK, hashBestChain));
|
||||
pfrom->PushMessage("inv", vInv);
|
||||
pfrom->hashContinue = 0;
|
||||
}
|
||||
@@ -3548,7 +4058,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
|
||||
// Send the rest of the chain
|
||||
if (pindex)
|
||||
pindex = pindex->pnext;
|
||||
int nLimit = IsInitialBlockDownload() ? 20000 : 500;
|
||||
// Send larger batches when the requester is far behind (syncing).
|
||||
// The original check used our own IBD state, but we're the seed node
|
||||
// (fully synced), so it always returned 500. Check how far behind
|
||||
// the requester is instead.
|
||||
int nLimit = (pindex && pindexBest && pindexBest->nHeight - pindex->nHeight > 1000) ? 10000 : 500;
|
||||
printf("IBD-DIAG: getblocks request from peer %s: start=%d stop=%s limit=%d\n",
|
||||
pfrom->addr.ToString().c_str(), (pindex ? pindex->nHeight : -1),
|
||||
hashStop.ToString().substr(0,20).c_str(), nLimit);
|
||||
@@ -3626,9 +4140,8 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
|
||||
return error("message headers size() = %"PRIszu"", vHeaders.size());
|
||||
}
|
||||
|
||||
CTxDB txdb("r");
|
||||
uint256 hashChainTip = 0;
|
||||
int nRequested = 0;
|
||||
int nNewHeaders = 0;
|
||||
for (const CBlock& header : vHeaders)
|
||||
{
|
||||
if (!header.vtx.empty())
|
||||
@@ -3638,7 +4151,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
|
||||
}
|
||||
|
||||
const uint256 hashHeader = header.GetHash();
|
||||
if (mapBlockIndex.count(hashHeader))
|
||||
if (mapBlockIndex.count(hashHeader) || mapHeaderSync.count(hashHeader))
|
||||
{
|
||||
hashChainTip = hashHeader;
|
||||
continue;
|
||||
@@ -3655,27 +4168,39 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
|
||||
else
|
||||
{
|
||||
map<uint256, CBlockIndex*>::iterator miPrev = mapBlockIndex.find(header.hashPrevBlock);
|
||||
if (miPrev == mapBlockIndex.end())
|
||||
if (miPrev == mapBlockIndex.end() && !mapHeaderSync.count(header.hashPrevBlock))
|
||||
break;
|
||||
hashChainTip = header.hashPrevBlock;
|
||||
}
|
||||
|
||||
CInv inv(MSG_BLOCK, hashHeader);
|
||||
if (!AlreadyHave(txdb, inv))
|
||||
if (!AddHeaderSyncNode(header, hashHeader))
|
||||
{
|
||||
pfrom->AskFor(inv);
|
||||
nRequested++;
|
||||
pfrom->Misbehaving(20);
|
||||
return error("invalid header sequence");
|
||||
}
|
||||
|
||||
hashChainTip = hashHeader;
|
||||
nNewHeaders++;
|
||||
}
|
||||
|
||||
if (nRequested > 0 && fDebug)
|
||||
printf("requested %d blocks from headers announcement\n", nRequested);
|
||||
int nRequested = 0;
|
||||
if (hashBestHeaderSync != 0)
|
||||
nRequested = QueueHeaderSyncBlocks(pfrom, HEADER_DOWNLOAD_WINDOW);
|
||||
|
||||
// If we received a full batch, continue sync via getblocks
|
||||
// (the getblocks/inv/orphan cycle handles chain continuation)
|
||||
if (nNewHeaders > 0 || nRequested > 0)
|
||||
printf("IBD-DIAG: accepted %d new headers, queued %d blocks from %zu headers (peer=%s bestHeader=%s)\n",
|
||||
nNewHeaders, nRequested, vHeaders.size(), pfrom->addr.ToString().c_str(),
|
||||
hashBestHeaderSync.ToString().substr(0,20).c_str());
|
||||
|
||||
// If we received a full batch, continue fetching headers.
|
||||
// During IBD, prefer getheaders over getblocks since headers are ~80 bytes
|
||||
// vs full blocks, letting us discover the chain structure faster.
|
||||
if (vHeaders.size() >= 2000)
|
||||
pfrom->PushGetBlocks(pindexBest, uint256(0));
|
||||
{
|
||||
if (IsInitialBlockDownload() && hashChainTip != 0)
|
||||
ContinueHeaderSync(pfrom, hashChainTip);
|
||||
else
|
||||
pfrom->PushGetBlocks(pindexBest, uint256(0));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3774,12 +4299,27 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
|
||||
if (IsInitialBlockDownload())
|
||||
{
|
||||
static int nBlocksSinceRequest = 0;
|
||||
if (++nBlocksSinceRequest >= 1000)
|
||||
if (++nBlocksSinceRequest >= 5000)
|
||||
{
|
||||
nBlocksSinceRequest = 0;
|
||||
pfrom->pindexLastGetBlocksBegin = NULL;
|
||||
pfrom->PushGetBlocks(pindexBest, uint256(0));
|
||||
printf("IBD-DIAG: pipeline refill at height %d\n", nBestHeight);
|
||||
// Pipeline refill: request from ALL connected full-node peers,
|
||||
// not just the one that sent us this block. This spreads block
|
||||
// download across multiple peers for better throughput.
|
||||
// Also send getheaders to scout ahead faster than full blocks.
|
||||
{
|
||||
LOCK(cs_vNodes);
|
||||
for (CNode* pnode : vNodes)
|
||||
{
|
||||
if (!pnode->fClient && pnode->nVersion != 0)
|
||||
{
|
||||
pnode->pindexLastGetBlocksBegin = NULL;
|
||||
pnode->PushGetBlocks(pindexBest, uint256(0));
|
||||
pnode->pindexLastGetHeadersBegin = NULL;
|
||||
pnode->PushGetHeaders(pindexBest, uint256(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
printf("IBD-DIAG: pipeline refill to all peers at height %d\n", nBestHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4230,7 +4770,8 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
|
||||
|
||||
|
||||
//
|
||||
// Stall detection: if IBD and no new blocks for 5 seconds, re-request
|
||||
// Stall detection: if IBD and no new blocks for 5 seconds, re-request.
|
||||
// Tighter than the old 10s to rotate away from slow peers faster.
|
||||
//
|
||||
if (IsInitialBlockDownload() && !pto->fClient)
|
||||
{
|
||||
@@ -4240,8 +4781,8 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
|
||||
if (nBestHeight > nLastHeight) {
|
||||
nLastHeight = nBestHeight;
|
||||
nLastBlockReceived = GetTime();
|
||||
} else if (nLastBlockReceived > 0 && GetTime() - nLastBlockReceived > 2) {
|
||||
if (GetTime() - nLastStallLog >= 10) { // log every 10s max
|
||||
} else if (nLastBlockReceived > 0 && GetTime() - nLastBlockReceived > 5) {
|
||||
if (GetTime() - nLastStallLog >= 30) { // log every 30s max
|
||||
printf("IBD-DIAG: STALL at height %d for %ds, peer=%s askfor_queue=%d send_size=%d\n",
|
||||
nBestHeight, (int)(GetTime() - nLastBlockReceived),
|
||||
pto->addr.ToString().c_str(),
|
||||
@@ -4270,6 +4811,9 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
|
||||
vector<CInv> vGetData;
|
||||
int64_t nNow = GetTime() * 1000000;
|
||||
CTxDB txdb("r");
|
||||
// During IBD, send larger getdata batches since PoS blocks are small
|
||||
// and the bottleneck is round-trip latency, not bandwidth.
|
||||
unsigned int nGetDataBatchSize = IsInitialBlockDownload() ? 4000 : 1000;
|
||||
while (!pto->mapAskFor.empty() && (*pto->mapAskFor.begin()).first <= nNow)
|
||||
{
|
||||
const CInv& inv = (*pto->mapAskFor.begin()).second;
|
||||
@@ -4278,7 +4822,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
|
||||
if (fDebugNet)
|
||||
printf("sending getdata: %s\n", inv.ToString().c_str());
|
||||
vGetData.push_back(inv);
|
||||
if (vGetData.size() >= 1000)
|
||||
if (vGetData.size() >= nGetDataBatchSize)
|
||||
{
|
||||
pto->PushMessage("getdata", vGetData);
|
||||
vGetData.clear();
|
||||
|
||||
@@ -35,6 +35,7 @@ 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_INV_SZ = 50000;
|
||||
static const int64_t MIN_TX_FEE = (1 * CENT) / 100;
|
||||
static const int64_t MIN_RELAY_TX_FEE = (1 * CENT) / 100;
|
||||
@@ -112,6 +113,7 @@ CBlockIndex* FindBlockByHeight(int nHeight);
|
||||
bool ProcessMessages(CNode* pfrom);
|
||||
bool SendMessages(CNode* pto, bool fSendTrickle);
|
||||
bool LoadExternalBlockFile(FILE* fileIn);
|
||||
bool FastImportBlockFile();
|
||||
|
||||
bool CheckProofOfWork(uint256 hash, unsigned int nBits);
|
||||
unsigned int GetNextTargetRequired(const CBlockIndex* pindexLast, bool fProofOfStake);
|
||||
|
||||
@@ -111,6 +111,7 @@ OBJS= \
|
||||
obj/miner.o \
|
||||
obj/main.o \
|
||||
obj/net.o \
|
||||
obj/bootstrap.o \
|
||||
obj/net_bootstrap.o \
|
||||
obj/protocol.o \
|
||||
obj/trianglesrpc.o \
|
||||
@@ -232,6 +233,13 @@ obj/tor_embedded.o: tor/tor_embedded.cpp
|
||||
-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); \
|
||||
|
||||
+18
-5
@@ -6,7 +6,7 @@ USE_UPNP:=0
|
||||
USE_IPV6:=1
|
||||
|
||||
LINK:=$(CXX)
|
||||
ARCH:=$(system lscpu | head -n 1 | awk '{print $2}')
|
||||
ARCH:=$(shell lscpu | head -n 1 | awk '{print $$2}')
|
||||
|
||||
DEFS=-DBOOST_SPIRIT_THREADSAFE
|
||||
|
||||
@@ -41,7 +41,6 @@ LIBS += \
|
||||
-l boost_chrono$(BOOST_LIB_SUFFIX) \
|
||||
-l db_cxx$(BDB_LIB_SUFFIX) \
|
||||
-l ssl \
|
||||
-l event \
|
||||
-l crypto
|
||||
|
||||
ifndef USE_UPNP
|
||||
@@ -146,6 +145,7 @@ OBJS= \
|
||||
obj/miner.o \
|
||||
obj/main.o \
|
||||
obj/net.o \
|
||||
obj/bootstrap.o \
|
||||
obj/net_bootstrap.o \
|
||||
obj/protocol.o \
|
||||
obj/trianglesrpc.o \
|
||||
@@ -199,6 +199,12 @@ ifdef USE_ZMQ
|
||||
OBJS += obj/zmqpublishnotifier.o
|
||||
endif
|
||||
|
||||
obj:
|
||||
@mkdir -p obj
|
||||
|
||||
obj-test:
|
||||
@mkdir -p obj-test
|
||||
|
||||
all: trianglesd
|
||||
|
||||
test check: test_triangles FORCE
|
||||
@@ -230,14 +236,14 @@ obj/scrypt-x86_64.o: scrypt-x86_64.S
|
||||
obj/scrypt-arm.o: scrypt-arm.S
|
||||
$(CXX) -c $(xCXXFLAGS) -MMD -o $@ $<
|
||||
|
||||
obj/%.o: %.cpp
|
||||
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/%.o: %.c | obj
|
||||
$(CXX) -c $(xCXXFLAGS) -fpermissive -MMD -MF $(@:%.o=%.d) -o $@ $<
|
||||
@cp $(@:%.o=%.d) $(@:%.o=%.P); \
|
||||
sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
|
||||
@@ -279,6 +285,13 @@ obj/tor_embedded.o: tor/tor_embedded.cpp
|
||||
-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); \
|
||||
@@ -291,7 +304,7 @@ trianglesd: $(OBJS:obj/%=obj/%)
|
||||
|
||||
TESTOBJS := $(patsubst test/%.cpp,obj-test/%.o,$(wildcard test/*.cpp))
|
||||
|
||||
obj-test/%.o: 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/ *\\$$//' \
|
||||
|
||||
+11
-1
@@ -551,13 +551,23 @@ void StakeMiner(CWallet *pwallet)
|
||||
if (fTryToSync)
|
||||
{
|
||||
fTryToSync = false;
|
||||
if (vNodes.size() < 3 || nBestHeight < GetNumBlocksOfPeers())
|
||||
if (vNodes.size() < 1 || nBestHeight < GetNumBlocksOfPeers())
|
||||
{
|
||||
MilliSleep(60000);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Update cached stake weight for UI display (avoids heavy work on UI thread)
|
||||
//
|
||||
{
|
||||
uint64_t nMinWeight = 0, nMaxWeight = 0, nWeight = 0;
|
||||
pwallet->GetStakeWeight(*pwallet, nMinWeight, nMaxWeight, nWeight);
|
||||
pwallet->nCachedStakeWeight = nWeight;
|
||||
pwallet->nCachedStakeWeightTime = GetTime();
|
||||
}
|
||||
|
||||
//
|
||||
// Create new block
|
||||
//
|
||||
|
||||
+3
-3
@@ -1584,7 +1584,7 @@ void ThreadOpenConnections2(void* parg)
|
||||
for (CNode* pnode : vNodes) {
|
||||
if (!pnode->fInbound) { fNoOutbound = false; break; }
|
||||
}
|
||||
if (fNoOutbound && (GetTime() - nStart > 30) && !fTestNet)
|
||||
if (fNoOutbound && (GetTime() - nStart > 10) && !fTestNet)
|
||||
{
|
||||
std::vector<CAddress> vAdd;
|
||||
for (unsigned int i = 0; i < ARRAYLEN(pnSeed); i++)
|
||||
@@ -1596,7 +1596,7 @@ void ThreadOpenConnections2(void* parg)
|
||||
vAdd.push_back(addr);
|
||||
}
|
||||
addrman.Add(vAdd, CNetAddr("127.0.0.1"));
|
||||
printf("No outbound connections after 30s, added %d hardcoded seeds\n", (int)vAdd.size());
|
||||
printf("No outbound connections after 10s, added %d hardcoded seeds\n", (int)vAdd.size());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1642,7 +1642,7 @@ void ThreadOpenConnections2(void* parg)
|
||||
continue;
|
||||
|
||||
// only consider very recently tried nodes after 30 failed attempts
|
||||
if (nANow - addr.nLastTry < 600 && nTries < 30)
|
||||
if (nANow - addr.nLastTry < 120 && nTries < 30)
|
||||
continue;
|
||||
|
||||
// do not allow non-default ports, unless after 50 invalid addresses selected already
|
||||
|
||||
@@ -422,7 +422,7 @@ public:
|
||||
// the key is the earliest time the request can be sent
|
||||
int64_t& nRequestTime = mapAlreadyAskedFor[inv];
|
||||
if (fDebugNet)
|
||||
printf("askfor %s %"PRId64" (%s)\n", inv.ToString().c_str(), nRequestTime, DateTimeStrFormat("%H:%M:%S", nRequestTime/1000000).c_str());
|
||||
printf("askfor %s %" PRId64 " (%s)\n", inv.ToString().c_str(), nRequestTime, DateTimeStrFormat("%H:%M:%S", nRequestTime/1000000).c_str());
|
||||
|
||||
// Make sure not to reuse time indexes to keep things in the same order
|
||||
int64_t nNow = (GetTime() - 1) * 1000000;
|
||||
|
||||
+20
-5
@@ -15,7 +15,7 @@ static const int64_t nClientStartupTime = GetTime();
|
||||
|
||||
ClientModel::ClientModel(OptionsModel *optionsModel, QObject *parent) :
|
||||
QObject(parent), optionsModel(optionsModel),
|
||||
cachedNumBlocks(0), cachedNumBlocksOfPeers(0), pollTimer(0)
|
||||
cachedNumBlocks(0), cachedNumBlocksOfPeers(0), cachedNumConnections(0), pollTimer(0)
|
||||
{
|
||||
numBlocksAtStartup = -1;
|
||||
|
||||
@@ -34,7 +34,14 @@ ClientModel::~ClientModel()
|
||||
|
||||
int ClientModel::getNumConnections() const
|
||||
{
|
||||
return vNodes.size();
|
||||
// Use TRY_LOCK to avoid blocking the UI thread when the network
|
||||
// thread holds cs_vNodes (e.g. during DNS resolution or connections).
|
||||
// Return the cached value if the lock is busy.
|
||||
TRY_LOCK(cs_vNodes, lockNodes);
|
||||
if (lockNodes) {
|
||||
cachedNumConnections = vNodes.size();
|
||||
}
|
||||
return cachedNumConnections;
|
||||
}
|
||||
|
||||
int ClientModel::getNumBlocks() const
|
||||
@@ -63,12 +70,20 @@ void ClientModel::updateTimer()
|
||||
int newNumBlocks = getNumBlocks();
|
||||
int newNumBlocksOfPeers = getNumBlocksOfPeers();
|
||||
|
||||
// Always emit during IBD so the speed/ETA display stays live
|
||||
if(cachedNumBlocks != newNumBlocks || cachedNumBlocksOfPeers != newNumBlocksOfPeers
|
||||
|| newNumBlocks < newNumBlocksOfPeers)
|
||||
// Always emit when values change or during IBD.
|
||||
// Also emit every ~30 seconds even when idle so setNumBlocks() can
|
||||
// re-evaluate sync status (e.g. when a new block arrives after a long gap).
|
||||
static int64_t nLastEmit = 0;
|
||||
int64_t nNow = GetTime();
|
||||
bool fChanged = (cachedNumBlocks != newNumBlocks || cachedNumBlocksOfPeers != newNumBlocksOfPeers);
|
||||
bool fCatchingUp = (newNumBlocks < newNumBlocksOfPeers);
|
||||
bool fPeriodicRefresh = (nNow - nLastEmit >= 30);
|
||||
|
||||
if(fChanged || fCatchingUp || fPeriodicRefresh)
|
||||
{
|
||||
cachedNumBlocks = newNumBlocks;
|
||||
cachedNumBlocksOfPeers = newNumBlocksOfPeers;
|
||||
nLastEmit = nNow;
|
||||
|
||||
emit numBlocksChanged(newNumBlocks, newNumBlocksOfPeers);
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ private:
|
||||
|
||||
int cachedNumBlocks;
|
||||
int cachedNumBlocksOfPeers;
|
||||
mutable int cachedNumConnections;
|
||||
|
||||
int numBlocksAtStartup;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#define GUICONSTANTS_H
|
||||
|
||||
/* Milliseconds between model updates */
|
||||
static const int MODEL_UPDATE_DELAY = 500;
|
||||
static const int MODEL_UPDATE_DELAY = 2500;
|
||||
|
||||
/* AskPassphraseDialog -- Maximum passphrase length */
|
||||
static const int MAX_PASSPHRASE_SIZE = 1024;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "introdialog.h"
|
||||
#include "util.h"
|
||||
#include "bootstrap.h"
|
||||
|
||||
#include <QSettings>
|
||||
#include <QVBoxLayout>
|
||||
@@ -9,6 +10,9 @@
|
||||
#include <QDir>
|
||||
#include <QMessageBox>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QProgressDialog>
|
||||
#include <QCheckBox>
|
||||
#include <QApplication>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
@@ -201,5 +205,73 @@ bool IntroDialog::pickDataDirectory()
|
||||
return false;
|
||||
}
|
||||
|
||||
// Offer bootstrap download on each startup (unless user checked "don't ask again")
|
||||
fs::path dataDirPath(dataDir.toStdString());
|
||||
if (!settings.value("bootstrapDontAsk", false).toBool())
|
||||
{
|
||||
QMessageBox msgBox;
|
||||
msgBox.setWindowTitle("Triangles");
|
||||
msgBox.setText(
|
||||
"Would you like to download the latest blockchain snapshot?\n\n"
|
||||
"This will download the blockchain data from the Triangles network "
|
||||
"and replace any existing chain data in your data directory.\n\n"
|
||||
"Click Yes to download, or No to sync from the network.");
|
||||
msgBox.setIcon(QMessageBox::Question);
|
||||
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
|
||||
msgBox.setDefaultButton(QMessageBox::Yes);
|
||||
QCheckBox *dontAskBox = new QCheckBox("Don't show this again");
|
||||
msgBox.setCheckBox(dontAskBox);
|
||||
|
||||
int ret = msgBox.exec();
|
||||
|
||||
if (dontAskBox->isChecked())
|
||||
settings.setValue("bootstrapDontAsk", true);
|
||||
|
||||
if (ret == QMessageBox::Yes)
|
||||
{
|
||||
std::string host = Bootstrap::DEFAULT_HOST;
|
||||
std::string strError;
|
||||
|
||||
QProgressDialog progress("Downloading blockchain snapshot...", "Cancel",
|
||||
0, 100, 0);
|
||||
progress.setWindowTitle("Triangles - Bootstrap");
|
||||
progress.setWindowModality(Qt::ApplicationModal);
|
||||
progress.setMinimumDuration(0);
|
||||
progress.setValue(0);
|
||||
|
||||
auto progressFn = [&progress](int64_t bytesDownloaded, int64_t totalBytes) {
|
||||
if (totalBytes > 0) {
|
||||
int pct = (int)((bytesDownloaded * 100) / totalBytes);
|
||||
progress.setValue(pct);
|
||||
progress.setLabelText(
|
||||
QString("Downloading blockchain snapshot... %1 MB / %2 MB")
|
||||
.arg(bytesDownloaded / (1024*1024))
|
||||
.arg(totalBytes / (1024*1024)));
|
||||
} else {
|
||||
progress.setLabelText(
|
||||
QString("Downloading blockchain snapshot... %1 MB")
|
||||
.arg(bytesDownloaded / (1024*1024)));
|
||||
}
|
||||
QApplication::processEvents();
|
||||
};
|
||||
|
||||
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"
|
||||
"The wallet will sync from the network instead.")
|
||||
.arg(QString::fromStdString(strError)));
|
||||
} else {
|
||||
progress.setValue(100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -126,6 +126,8 @@ OverviewPage::~OverviewPage()
|
||||
|
||||
void OverviewPage::setBalance(qint64 balance, qint64 stake, qint64 unconfirmedBalance, qint64 immatureBalance)
|
||||
{
|
||||
if (!model || !model->getOptionsModel())
|
||||
return;
|
||||
int unit = model->getOptionsModel()->getDisplayUnit();
|
||||
currentBalance = balance;
|
||||
currentStake = stake;
|
||||
|
||||
@@ -71,7 +71,13 @@ public:
|
||||
OutputDebugStringF("refreshWallet\n");
|
||||
cachedWallet.clear();
|
||||
{
|
||||
LOCK(wallet->cs_wallet);
|
||||
TRY_LOCK(wallet->cs_wallet, lockWallet);
|
||||
if(!lockWallet)
|
||||
{
|
||||
// Lock busy (block processing), retry in 500ms
|
||||
QTimer::singleShot(500, parent, SLOT(refreshWallet()));
|
||||
return;
|
||||
}
|
||||
for(std::map<uint256, CWalletTx>::iterator it = wallet->mapWallet.begin(); it != wallet->mapWallet.end(); ++it)
|
||||
{
|
||||
if(TransactionRecord::showTransaction(it->second))
|
||||
@@ -89,7 +95,9 @@ public:
|
||||
{
|
||||
OutputDebugStringF("updateWallet %s %i\n", hash.ToString().c_str(), status);
|
||||
{
|
||||
LOCK(wallet->cs_wallet);
|
||||
TRY_LOCK(wallet->cs_wallet, lockWallet);
|
||||
if (!lockWallet)
|
||||
return;
|
||||
|
||||
// Find transaction in wallet
|
||||
std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(hash);
|
||||
|
||||
+43
-24
@@ -799,19 +799,23 @@ void TrianglesGUI::setNumConnections(int count)
|
||||
|
||||
void TrianglesGUI::setNumBlocks(int count, int nTotalBlocks)
|
||||
{
|
||||
// don't show / hide progress bar and its label if we have no connection to the network
|
||||
if (!clientModel || clientModel->getNumConnections() == 0)
|
||||
if (!clientModel)
|
||||
return;
|
||||
|
||||
int nConnections = clientModel->getNumConnections();
|
||||
|
||||
// Hide progress bar when disconnected, but don't return early -
|
||||
// we still need to update sync state and the out-of-sync warning
|
||||
if (nConnections == 0)
|
||||
{
|
||||
progressBarLabel->setVisible(false);
|
||||
progressBar->setVisible(false);
|
||||
ui->label_blocks->setVisible(false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
QString tooltip;
|
||||
|
||||
if(count < nTotalBlocks)
|
||||
if(nConnections > 0 && count < nTotalBlocks)
|
||||
{
|
||||
// Calculate blocks/sec - only update rate when new blocks arrive
|
||||
static int lastCount = 0;
|
||||
@@ -899,20 +903,20 @@ void TrianglesGUI::setNumBlocks(int count, int nTotalBlocks)
|
||||
text = tr("%n day(s) ago","",secs/(60*60*24));
|
||||
}
|
||||
|
||||
// Set icon state: spinning if catching up, tick otherwise
|
||||
if(secs < 90*60 && count >= nTotalBlocks)
|
||||
// Set icon state: spinning if catching up, tick otherwise.
|
||||
// For PoS chains with few stakers, blocks can be hours or days apart.
|
||||
// Sync status is based purely on block count - NOT block timestamp.
|
||||
// A stale chain (no recent blocks) is still "synced" if we have all blocks.
|
||||
if(count >= nTotalBlocks)
|
||||
{
|
||||
tooltip = tr("Up to date") + QString(".<br>") + tooltip;
|
||||
labelBlocksIcon->setPixmap(QIcon(":/icons/synced").pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE));
|
||||
|
||||
|
||||
overviewPage->showOutOfSyncWarning(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
tooltip = tr("Catching up...") + QString("<br>") + tooltip;
|
||||
//syncIconMovie doesn't work for some reason - using fallback png
|
||||
//labelBlocksIcon->setMovie(syncIconMovie);
|
||||
//syncIconMovie->start();
|
||||
labelBlocksIcon->setPixmap(QIcon(":/icons/notsynced").pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE));
|
||||
|
||||
overviewPage->showOutOfSyncWarning(true);
|
||||
@@ -1590,32 +1594,47 @@ void TrianglesGUI::toggleHidden()
|
||||
|
||||
void TrianglesGUI::updateStakingIcon()
|
||||
{
|
||||
uint64_t nMinWeight = 0, nMaxWeight = 0, nWeight = 0;
|
||||
// Read cached staking info computed by the staking thread.
|
||||
// No locks needed - these are volatile values written by the miner thread
|
||||
// and are display-only. This keeps the UI thread completely non-blocking.
|
||||
|
||||
uint64_t nWeight = 0;
|
||||
bool fWalletLocked = false;
|
||||
bool fHasPeers = false;
|
||||
|
||||
if (pwalletMain)
|
||||
pwalletMain->GetStakeWeight(*pwalletMain, nMinWeight, nMaxWeight, nWeight);
|
||||
{
|
||||
TRY_LOCK(pwalletMain->cs_wallet, lockWallet);
|
||||
if (lockWallet)
|
||||
fWalletLocked = pwalletMain->IsLocked();
|
||||
else
|
||||
return; // Skip this cycle, try again in 30 seconds
|
||||
|
||||
// Use cached weight from the staking thread instead of computing on UI thread.
|
||||
// The staking thread updates this every ~500ms-1s loop iteration.
|
||||
nWeight = pwalletMain->nCachedStakeWeight;
|
||||
}
|
||||
|
||||
{
|
||||
TRY_LOCK(cs_vNodes, lockNodes);
|
||||
if (lockNodes)
|
||||
fHasPeers = !vNodes.empty();
|
||||
}
|
||||
|
||||
if (nLastCoinStakeSearchInterval && nWeight)
|
||||
{
|
||||
uint64_t nNetworkWeight = GetPoSKernelPS();
|
||||
unsigned nEstimateTime = nTargetSpacing * nNetworkWeight / nWeight;
|
||||
unsigned nEstimateTime = nWeight > 0 ? nTargetSpacing * nNetworkWeight / nWeight : 0;
|
||||
|
||||
QString text;
|
||||
if (nEstimateTime < 60)
|
||||
{
|
||||
text = tr("%n second(s)", "", nEstimateTime);
|
||||
}
|
||||
else if (nEstimateTime < 60*60)
|
||||
{
|
||||
text = tr("%n minute(s)", "", nEstimateTime/60);
|
||||
}
|
||||
else if (nEstimateTime < 24*60*60)
|
||||
{
|
||||
text = tr("%n hour(s)", "", nEstimateTime/(60*60));
|
||||
}
|
||||
else
|
||||
{
|
||||
text = tr("%n day(s)", "", nEstimateTime/(60*60*24));
|
||||
}
|
||||
|
||||
labelStakingIcon->setPixmap(QIcon(":/icons/staking_on").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE));
|
||||
labelStakingIcon->setToolTip(tr("Staking.<br>Your weight is %1<br>Network weight is %2<br>Expected time to earn reward is %3").arg(nWeight).arg(nNetworkWeight).arg(text));
|
||||
@@ -1623,9 +1642,9 @@ void TrianglesGUI::updateStakingIcon()
|
||||
else
|
||||
{
|
||||
labelStakingIcon->setPixmap(QIcon(":/icons/staking_off").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE));
|
||||
if (pwalletMain && pwalletMain->IsLocked())
|
||||
if (fWalletLocked)
|
||||
labelStakingIcon->setToolTip(tr("Not staking because wallet is locked"));
|
||||
else if (vNodes.empty())
|
||||
else if (!fHasPeers)
|
||||
labelStakingIcon->setToolTip(tr("Not staking because wallet is offline"));
|
||||
else if (IsInitialBlockDownload())
|
||||
labelStakingIcon->setToolTip(tr("Not staking because wallet is syncing"));
|
||||
|
||||
+22
-11
@@ -86,10 +86,12 @@ void WalletModel::pollBalanceChanged()
|
||||
|
||||
void WalletModel::checkBalanceChanged()
|
||||
{
|
||||
qint64 newBalance = getBalance();
|
||||
qint64 newStake = getStake();
|
||||
qint64 newUnconfirmedBalance = getUnconfirmedBalance();
|
||||
qint64 newImmatureBalance = getImmatureBalance();
|
||||
// Get all balances in a single lock acquisition + single pass.
|
||||
// Uses TRY_LOCK internally - if cs_wallet is busy (block processing),
|
||||
// skip this cycle. The timer will retry in 2.5 seconds.
|
||||
int64_t newBalance = 0, newStake = 0, newUnconfirmedBalance = 0, newImmatureBalance = 0;
|
||||
if (!wallet->GetAllBalances(newBalance, newStake, newUnconfirmedBalance, newImmatureBalance))
|
||||
return;
|
||||
|
||||
if(cachedBalance != newBalance || cachedStake != newStake || cachedUnconfirmedBalance != newUnconfirmedBalance || cachedImmatureBalance != newImmatureBalance)
|
||||
{
|
||||
@@ -106,14 +108,20 @@ void WalletModel::updateTransaction(const QString &hash, int status)
|
||||
if(transactionTableModel)
|
||||
transactionTableModel->updateTransaction(hash, status);
|
||||
|
||||
// Balance and number of transactions might have changed
|
||||
checkBalanceChanged();
|
||||
// Don't call checkBalanceChanged() here - it does LOCK(cs_wallet) + iterates
|
||||
// all wallet transactions, blocking the UI thread. The pollBalanceChanged()
|
||||
// timer already handles balance updates every 2.5 seconds with TRY_LOCK.
|
||||
|
||||
int newNumTransactions = getNumTransactions();
|
||||
if(cachedNumTransactions != newNumTransactions)
|
||||
// Same for getNumTransactions() - use cached count from the transaction model
|
||||
// to avoid another LOCK(cs_wallet) on the UI thread.
|
||||
if(transactionTableModel)
|
||||
{
|
||||
cachedNumTransactions = newNumTransactions;
|
||||
emit numTransactionsChanged(newNumTransactions);
|
||||
int newNumTransactions = transactionTableModel->rowCount(QModelIndex());
|
||||
if(cachedNumTransactions != newNumTransactions)
|
||||
{
|
||||
cachedNumTransactions = newNumTransactions;
|
||||
emit numTransactionsChanged(newNumTransactions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,7 +248,10 @@ WalletModel::SendCoinsReturn WalletModel::sendCoins(const QList<SendCoinsRecipie
|
||||
|
||||
if(!fCreated)
|
||||
{
|
||||
if((total + nFeeRequired) > nBalance) // FIXME: could cause collisions in the future
|
||||
// NOTE: Potential edge case in fee calculation. The term "collisions" is unclear
|
||||
// from original comment - may refer to transaction conflicts or UTXO selection issues.
|
||||
// Consider reviewing Bitcoin Core's current implementation of this balance check.
|
||||
if((total + nFeeRequired) > nBalance)
|
||||
{
|
||||
return SendCoinsReturn(AmountWithFeeExceedsBalance, nFeeRequired);
|
||||
}
|
||||
|
||||
+6
-2
@@ -229,7 +229,8 @@ Value getworkex(const Array& params, bool fHelp)
|
||||
if(coinbase.size() == 0)
|
||||
pblock->vtx[0].vin[0].scriptSig = mapNewBlock[pdata->hashMerkleRoot].second;
|
||||
else
|
||||
CDataStream(coinbase, SER_NETWORK, PROTOCOL_VERSION) >> pblock->vtx[0]; // FIXME - DRM!
|
||||
// Deserialize custom coinbase transaction from miner
|
||||
CDataStream(coinbase, SER_NETWORK, PROTOCOL_VERSION) >> pblock->vtx[0];
|
||||
|
||||
pblock->hashMerkleRoot = pblock->BuildMerkleTree();
|
||||
|
||||
@@ -260,7 +261,10 @@ Value getwork(const Array& params, bool fHelp)
|
||||
throw JSONRPCError(RPC_MISC_ERROR, "No more PoW blocks");
|
||||
|
||||
typedef map<uint256, pair<CBlock*, CScript> > mapNewBlock_t;
|
||||
static mapNewBlock_t mapNewBlock; // FIXME: thread safety
|
||||
// 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);
|
||||
|
||||
|
||||
+45
-1
@@ -8,19 +8,62 @@
|
||||
#include "wallet.h"
|
||||
#include "db.h"
|
||||
#include "walletdb.h"
|
||||
#include "net_bootstrap.h"
|
||||
|
||||
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)
|
||||
throw runtime_error(
|
||||
"getnetworkinfo\n"
|
||||
"Returns an object containing various state info regarding P2P networking.");
|
||||
"Returns an object containing various state info regarding P2P networking,\n"
|
||||
"including peer mix, bootstrap mode, and basic sync health.");
|
||||
|
||||
proxyType proxy;
|
||||
GetProxy(NET_IPV4, proxy);
|
||||
const NetBootstrap::NetworkHealth health = NetBootstrap::GetNetworkHealth();
|
||||
|
||||
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())));
|
||||
|
||||
Object obj;
|
||||
obj.push_back(Pair("version", FormatFullVersion()));
|
||||
@@ -30,6 +73,7 @@ Value getnetworkinfo(const Array& params, bool fHelp)
|
||||
obj.push_back(Pair("ip", addrSeenByPeer.ToStringIP()));
|
||||
obj.push_back(Pair("localservices", strprintf("%016"PRIx64, nLocalServices)));
|
||||
obj.push_back(Pair("testnet", fTestNet));
|
||||
obj.push_back(Pair("networkhealth", healthObj));
|
||||
obj.push_back(Pair("errors", GetWarnings("statusbar")));
|
||||
return obj;
|
||||
}
|
||||
|
||||
@@ -1,32 +1,33 @@
|
||||
//
|
||||
// Unit tests for block-chain checkpoints
|
||||
//
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include "../checkpoints.h"
|
||||
#include "../util.h"
|
||||
|
||||
using namespace std;
|
||||
#include "../uint256.h"
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(Checkpoints_tests)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(sanity)
|
||||
BOOST_AUTO_TEST_CASE(hardened_checkpoints_match_current_chain)
|
||||
{
|
||||
uint256 p11111 = uint256("0x05cbc42d0421207a39427f1b44610a30380e0e3dd593c154276d7c8d4ae8c9b2");
|
||||
uint256 p16666 = uint256("0x682d99d096441e046dae8dd86d5bdfaa32d4e29dea6cf38273bb2b261905f1e2");
|
||||
BOOST_CHECK(Checkpoints::CheckBlock(11111, p11111));
|
||||
BOOST_CHECK(Checkpoints::CheckBlock(16666, p16666));
|
||||
BOOST_CHECK(Checkpoints::CheckHardened(0, uint256("0x7e7a6e4dd5fe895106fca912dfbacaeaf2a89e76c6a588df8ff96e0e18b96021")));
|
||||
BOOST_CHECK(Checkpoints::CheckHardened(9000, uint256("0x00000000019ef6b2f5e7c324c7d083ee94502305aabc7e9cd73a7fb2a57bb8db")));
|
||||
BOOST_CHECK(Checkpoints::CheckHardened(9001, uint256("0x6d5c6c5f201cc9e59659ee0da30d1430dc6bf3b12a8ff4c3864ab8d6286b0007")));
|
||||
BOOST_CHECK(Checkpoints::CheckHardened(2186940, uint256("0xbd952e8d4a612e336d840ad924a7e09395e36bcd9d929b302e47e60b5c3098c0")));
|
||||
}
|
||||
|
||||
|
||||
// Wrong hashes at checkpoints should fail:
|
||||
BOOST_CHECK(!Checkpoints::CheckBlock(11111, p16666));
|
||||
BOOST_CHECK(!Checkpoints::CheckBlock(16666, p11111));
|
||||
BOOST_AUTO_TEST_CASE(hardened_checkpoints_reject_wrong_hashes_and_allow_unknown_heights)
|
||||
{
|
||||
const uint256 wrongHash("0x0000000000000000000000000000000000000000000000000000000000000001");
|
||||
|
||||
// ... but any hash not at a checkpoint should succeed:
|
||||
BOOST_CHECK(Checkpoints::CheckBlock(11111+1, p16666));
|
||||
BOOST_CHECK(Checkpoints::CheckBlock(16666+1, p11111));
|
||||
BOOST_CHECK(!Checkpoints::CheckHardened(9000, wrongHash));
|
||||
BOOST_CHECK(!Checkpoints::CheckHardened(9001, wrongHash));
|
||||
BOOST_CHECK(!Checkpoints::CheckHardened(2186940, wrongHash));
|
||||
|
||||
BOOST_CHECK(Checkpoints::GetTotalBlocksEstimate() >= 16666);
|
||||
}
|
||||
BOOST_CHECK(Checkpoints::CheckHardened(2186941, wrongHash));
|
||||
BOOST_CHECK(Checkpoints::CheckHardened(42, wrongHash));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(total_blocks_estimate_tracks_latest_hardened_checkpoint)
|
||||
{
|
||||
BOOST_CHECK(Checkpoints::GetTotalBlocksEstimate() >= 2186940);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
@@ -64,7 +64,7 @@ BOOST_AUTO_TEST_CASE(DoS_banscore)
|
||||
BOOST_AUTO_TEST_CASE(DoS_bantime)
|
||||
{
|
||||
CNode::ClearBanned();
|
||||
int64 nStartTime = GetTime();
|
||||
int64_t nStartTime = GetTime();
|
||||
SetMockTime(nStartTime); // Overrides future calls to GetTime()
|
||||
|
||||
CAddress addr(ip(0xa0b0c001));
|
||||
@@ -80,11 +80,11 @@ BOOST_AUTO_TEST_CASE(DoS_bantime)
|
||||
BOOST_CHECK(!CNode::IsBanned(addr));
|
||||
}
|
||||
|
||||
static bool CheckNBits(unsigned int nbits1, int64 time1, unsigned int nbits2, int64 time2)\
|
||||
static bool CheckNBits(unsigned int nbits1, int64_t time1, unsigned int nbits2, int64_t time2)\
|
||||
{
|
||||
if (time1 > time2)
|
||||
return CheckNBits(nbits2, time2, nbits1, time1);
|
||||
int64 deltaTime = time2-time1;
|
||||
int64_t deltaTime = time2-time1;
|
||||
|
||||
CBigNum required;
|
||||
required.SetCompact(ComputeMinWork(nbits1, deltaTime));
|
||||
@@ -97,7 +97,7 @@ BOOST_AUTO_TEST_CASE(DoS_checknbits)
|
||||
{
|
||||
// Timestamps,nBits from the Triangles blockchain.
|
||||
// These are the block-chain checkpoint blocks
|
||||
typedef std::map<int64, unsigned int> BlockData;
|
||||
typedef std::map<int64_t, unsigned int> BlockData;
|
||||
BlockData chainData = {
|
||||
{1239852051,486604799},{1262749024,486594666},
|
||||
{1279305360,469854461},{1280200847,469830746},{1281678674,469809688},
|
||||
|
||||
+11
-1
@@ -34,6 +34,12 @@ static leveldb::Options GetOptions() {
|
||||
int nCacheSizeMB = GetArg("-dbcache", 2048);
|
||||
options.block_cache = leveldb::NewLRUCache(nCacheSizeMB * 1048576);
|
||||
options.filter_policy = leveldb::NewBloomFilterPolicy(10);
|
||||
// Larger write buffer (64MB vs default 4MB) reduces the frequency of
|
||||
// memtable flushes and compactions, which is a big win during IBD
|
||||
// when millions of tx index entries are written sequentially.
|
||||
options.write_buffer_size = 64 * 1048576;
|
||||
// Allow more open files for better read performance on large chains
|
||||
options.max_open_files = 1000;
|
||||
return options;
|
||||
}
|
||||
|
||||
@@ -138,7 +144,11 @@ void CTxDB::Close()
|
||||
|
||||
bool CTxDB::TxnBegin()
|
||||
{
|
||||
assert(!activeBatch);
|
||||
// Allow calling TxnBegin when a batch is already active (no-op).
|
||||
// This lets callers like SetBestChain share a batch that was opened
|
||||
// earlier by AddToBlockIndex, merging two commits into one.
|
||||
if (activeBatch)
|
||||
return true;
|
||||
activeBatch = new leveldb::WriteBatch();
|
||||
return true;
|
||||
}
|
||||
|
||||
+1
-1
@@ -237,7 +237,7 @@ void runCommand(std::string strCommand);
|
||||
|
||||
inline std::string i64tostr(int64_t n)
|
||||
{
|
||||
return strprintf("%"PRId64, n);
|
||||
return strprintf("%" PRId64, n);
|
||||
}
|
||||
|
||||
inline std::string itostr(int n)
|
||||
|
||||
+1
-1
@@ -53,7 +53,7 @@ static const int MEMPOOL_GD_VERSION = 60002;
|
||||
|
||||
#define DISPLAY_VERSION_MAJOR 5
|
||||
#define DISPLAY_VERSION_MINOR 3
|
||||
#define DISPLAY_VERSION_REVISION 2
|
||||
#define DISPLAY_VERSION_REVISION 6
|
||||
#define DISPLAY_VERSION_BUILD 0
|
||||
|
||||
#endif
|
||||
|
||||
@@ -958,11 +958,21 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate)
|
||||
CBlockIndex* pindex = pindexStart;
|
||||
{
|
||||
LOCK(cs_wallet);
|
||||
int nScanned = 0;
|
||||
int nTotal = nBestHeight - (pindexStart ? pindexStart->nHeight : 0);
|
||||
if (nTotal < 1) nTotal = 1;
|
||||
while (pindex)
|
||||
{
|
||||
if (fShutdown)
|
||||
break;
|
||||
|
||||
// Report progress every 10000 blocks to keep UI responsive
|
||||
if (++nScanned % 10000 == 0)
|
||||
{
|
||||
int nPercent = (nScanned * 100) / nTotal;
|
||||
uiInterface.InitMessage(strprintf(_("Rescanning... %d%%"), nPercent));
|
||||
}
|
||||
|
||||
// no need to read and scan block, if block was created before
|
||||
// our wallet birthday (as adjusted for block time variability)
|
||||
if (nTimeFirstKey && (pindex->nTime < (nTimeFirstKey - 7200))) {
|
||||
@@ -1466,6 +1476,34 @@ int64_t CWallet::GetNewMint() const
|
||||
return nTotal;
|
||||
}
|
||||
|
||||
bool CWallet::GetAllBalances(int64_t& nBalance, int64_t& nStake, int64_t& nUnconfirmed, int64_t& nImmature) const
|
||||
{
|
||||
nBalance = 0;
|
||||
nStake = 0;
|
||||
nUnconfirmed = 0;
|
||||
nImmature = 0;
|
||||
TRY_LOCK(cs_wallet, lockWallet);
|
||||
if (!lockWallet)
|
||||
return false;
|
||||
for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
|
||||
{
|
||||
const CWalletTx& pcoin = (*it).second;
|
||||
|
||||
if (pcoin.IsCoinStake() && pcoin.GetBlocksToMaturity() > 0 && pcoin.GetDepthInMainChain() > 0)
|
||||
nStake += CWallet::GetCredit(pcoin);
|
||||
|
||||
if (pcoin.IsCoinBase() && pcoin.GetBlocksToMaturity() > 0 && pcoin.IsInMainChain())
|
||||
nImmature += GetCredit(pcoin);
|
||||
|
||||
if (pcoin.IsTrusted())
|
||||
nBalance += pcoin.GetAvailableCredit();
|
||||
|
||||
if (!pcoin.IsFinal() || !pcoin.IsTrusted())
|
||||
nUnconfirmed += pcoin.GetAvailableCredit();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CWallet::SelectCoinsMinConf(int64_t nTargetValue, unsigned int nSpendTime, int nConfMine, int nConfTheirs, vector<COutput> vCoins, set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64_t& nValueRet) const
|
||||
{
|
||||
setCoinsRet.clear();
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <atomic>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
@@ -107,6 +108,8 @@ public:
|
||||
nMasterKeyMaxID = 0;
|
||||
pwalletdbEncryption = NULL;
|
||||
nOrderPosNext = 0;
|
||||
nCachedStakeWeight = 0;
|
||||
nCachedStakeWeightTime = 0;
|
||||
}
|
||||
CWallet(std::string strWalletFileIn)
|
||||
{
|
||||
@@ -117,6 +120,8 @@ public:
|
||||
nMasterKeyMaxID = 0;
|
||||
pwalletdbEncryption = NULL;
|
||||
nOrderPosNext = 0;
|
||||
nCachedStakeWeight = 0;
|
||||
nCachedStakeWeightTime = 0;
|
||||
}
|
||||
|
||||
std::map<uint256, CWalletTx> mapWallet;
|
||||
@@ -190,6 +195,8 @@ public:
|
||||
int64_t GetImmatureBalance() const;
|
||||
int64_t GetStake() const;
|
||||
int64_t GetNewMint() const;
|
||||
// Get all balances in a single lock acquisition + single pass (avoids 4x lock + 4x iteration)
|
||||
bool GetAllBalances(int64_t& nBalance, int64_t& nStake, int64_t& nUnconfirmed, int64_t& nImmature) const;
|
||||
bool CreateTransaction(const std::vector<std::pair<CScript, int64_t> >& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, const CCoinControl *coinControl=NULL);
|
||||
bool CreateTransaction(CScript scriptPubKey, int64_t nValue, std::string& sNarr, CWalletTx& wtxNew, CReserveKey& reservekey, int64_t& nFeeRet, const CCoinControl *coinControl=NULL);
|
||||
bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey);
|
||||
@@ -197,6 +204,10 @@ public:
|
||||
bool GetStakeWeight(const CKeyStore& keystore, uint64_t& nMinWeight, uint64_t& nMaxWeight, uint64_t& nWeight);
|
||||
bool CreateCoinStake(const CKeyStore& keystore, unsigned int nBits, int64_t nSearchInterval, int64_t nFees, CTransaction& txNew, CKey& key);
|
||||
|
||||
// Cached staking info - updated by the staking thread, read by the UI thread.
|
||||
std::atomic<uint64_t> nCachedStakeWeight;
|
||||
std::atomic<int64_t> nCachedStakeWeightTime; // GetTime() when last updated
|
||||
|
||||
std::string SendMoney(CScript scriptPubKey, int64_t nValue, std::string& sNarr, CWalletTx& wtxNew, bool fAskFee=false);
|
||||
std::string SendMoneyToDestination(const CTxDestination& address, int64_t nValue, std::string& sNarr, CWalletTx& wtxNew, bool fAskFee=false);
|
||||
|
||||
|
||||
@@ -204,6 +204,7 @@ HEADERS += src/qt/trianglesgui.h \
|
||||
src/qt/addressbookpage.h \
|
||||
src/qt/aboutdialog.h \
|
||||
src/qt/introdialog.h \
|
||||
src/bootstrap.h \
|
||||
src/qt/editaddressdialog.h \
|
||||
src/qt/trianglesaddressvalidator.h \
|
||||
src/alert.h \
|
||||
@@ -322,6 +323,7 @@ SOURCES += src/qt/triangles.cpp src/qt/trianglesgui.cpp \
|
||||
src/qt/addressbookpage.cpp \
|
||||
src/qt/aboutdialog.cpp \
|
||||
src/qt/introdialog.cpp \
|
||||
src/bootstrap.cpp \
|
||||
src/qt/editaddressdialog.cpp \
|
||||
src/qt/trianglesaddressvalidator.cpp \
|
||||
# Old embedded Tor v2 client removed - incompatible with OpenSSL 3.x
|
||||
|
||||
Reference in New Issue
Block a user