Compare commits

...

12 Commits

Author SHA1 Message Date
sami7777 22de8630cd Fix int64 -> int64_t in DoS_tests.cpp for modern compilers
Build All Platforms / build-windows-qt (push) Waiting to run
Build All Platforms / build-windows-daemon (push) Waiting to run
Build All Platforms / build-linux-qt (push) Waiting to run
Build All Platforms / build-linux-daemon (push) Waiting to run
Build All Platforms / build-macos (push) Waiting to run
Build All Platforms / release (push) Blocked by required conditions
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 23:23:23 -07:00
sami7777 da5e5f9a8a Bump version to 5.3.6 - IBD sync optimizations and Linux build fix
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 23:15:14 -07:00
sami7777 998bd51425 Fix Linux headless build (makefile.unix)
- Fix $(system) -> $(shell) GNU Make syntax error that broke ARCH detection
- Add obj/ and obj-test/ directory creation rules for fresh clones
- Remove duplicate -levent linkage
- Add order-only prerequisites (| obj) to pattern rules

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 23:14:30 -07:00
sami7777 7d0b2806e0 IBD sync optimizations: header planner, parallel download, LevelDB tuning
Major sync performance improvements while preserving consensus:

- Header-first sync planner: receives and caches headers ahead of block
  downloads, building a verified chain-trust map. Uses a sliding download
  window (128 blocks in-flight, 30s timeout) to request blocks in order
  from the best known header chain.
- Merged DB transactions: AddToBlockIndex and SetBestChain now share a
  single LevelDB WriteBatch, halving the per-block commit count.
- Multi-peer block requests: pipeline refill and stall recovery now send
  getblocks+getheaders to ALL connected full-node peers, not just one.
- LevelDB tuning: 64MB write buffer (vs 4MB default), 1000 max open files
  for reduced memtable flush frequency during IBD.
- Larger getdata batches: 4000 items during IBD (vs 1000) to reduce
  round-trip overhead with small PoS blocks.
- Tighter stall detection: 5-second timeout (vs 10s) for faster rotation
  away from slow peers.
- Higher orphan limit during IBD: 4000 (vs 750) to prevent eviction and
  re-download when blocks arrive out-of-order from parallel peers.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 23:03:28 -07:00
sami7777 73c183d1c0 Add CI unit tests, network health RPC, and fix checkpoint tests
- Add unit test build+run steps to both Qt and headless Linux CI jobs
- Enhance getnetworkinfo RPC with networkhealth object (peer mix, bootstrap mode, sync status)
- Rewrite Checkpoints_tests to validate actual chain checkpoints (0, 9000, 9001, 2186940)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 21:53:47 -07:00
sami7777 65b9417c28 Eliminate all blocking LOCK(cs_wallet) calls from UI thread
Build All Platforms / build-windows-qt (push) Waiting to run
Build All Platforms / build-windows-daemon (push) Waiting to run
Build All Platforms / build-linux-qt (push) Waiting to run
Build All Platforms / build-linux-daemon (push) Waiting to run
Build All Platforms / build-macos (push) Waiting to run
Build All Platforms / release (push) Blocked by required conditions
During sync, NotifyTransactionChanged fires for every wallet tx in
every block, each triggering 3 blocking LOCK(cs_wallet) calls on
the UI thread: updateWallet, GetAllBalances, getNumTransactions.
With the block processing thread holding cs_wallet almost continuously,
the UI thread blocks waiting for the lock - causing "not responding".

Fixes:
- GetAllBalances: LOCK → TRY_LOCK, returns false if busy
- updateWallet (tx table): LOCK → TRY_LOCK, skips if busy
- updateTransaction: removed checkBalanceChanged() call entirely
  (pollBalanceChanged timer handles it every 2.5s with TRY_LOCK)
- getNumTransactions: replaced with rowCount() from cached model

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 19:32:04 -07:00
sami7777 ed87543153 Fix Linux Qt build: int64_t/qint64 type mismatch
On Linux, int64_t is long but qint64 is long long - different types
that can't bind to the same reference. Use int64_t locals to match
the GetAllBalances signature.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 18:05:43 -07:00
sami7777 6e9dbb1aa9 Bump version to 5.3.5 - fix out-of-sync display for PoS chains
Remove time-based sync check that showed "out of sync" when blocks
were >6 hours old. For PoS chains with few stakers, blocks can be
hours apart - that's idle, not out of sync. Now uses block count
only. Also adds periodic UI refresh every 30s and switches cached
stake weight from volatile to std::atomic.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-22 17:59:18 -07:00
SamiAhmed7777 a6ec711cfa Merge pull request #1 from SamiAhmed7777/cleanup/desloppify
Code cleanup: Documentation and C++11 compliance fixes
2026-03-22 15:43:35 -07:00
Krystie 6877aeaddb chore: Update .gitignore for build artifacts 2026-03-22 22:54:21 +01:00
Krystie 60067e1a88 fix: Add space between string literals and PRId64 macros
Fixes C++11 literal-suffix warnings in util.h, net.h, and alert.cpp.
Required space between string literal and macro per C++11 standard.

No functional changes - formatting only.
2026-03-22 22:46:48 +01:00
Krystie e91ccd8786 docs: Document critical TODOs/FIXMEs with context
- Add CLEANUP_NOTES.md documenting cleanup strategy
- Add TODO_DOCUMENTATION.md with detailed context for all TODOs
- Improve inline comments for thread safety issue in rpcmining.cpp
- Clarify potential collision note in walletmodel.cpp
- Remove unclear 'DRM' comment, replace with descriptive text

No functional changes - documentation only.
2026-03-22 22:26:52 +01:00
25 changed files with 825 additions and 102 deletions
+2 -1
View File
@@ -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"
+93 -1
View File
@@ -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:*)"
]
}
}
+13 -1
View File
@@ -9,7 +9,7 @@ on:
workflow_dispatch:
env:
VERSION: "5.3.4"
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
+3
View File
@@ -52,3 +52,6 @@ triangles.conf
*.key
*.cert
*.gpg
*.o
src/trianglesd
src/obj/
+94
View File
@@ -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
+124
View File
@@ -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
View File
@@ -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"
+1 -1
View File
@@ -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 4
#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.
+343 -30
View File
@@ -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());
@@ -3513,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
@@ -3857,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())
@@ -3869,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;
@@ -3886,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));
}
}
@@ -4008,9 +4302,24 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
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);
}
}
}
@@ -4461,7 +4770,8 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
//
// Stall detection: if IBD and no new blocks for 10 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)
{
@@ -4471,7 +4781,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
if (nBestHeight > nLastHeight) {
nLastHeight = nBestHeight;
nLastBlockReceived = GetTime();
} else if (nLastBlockReceived > 0 && GetTime() - nLastBlockReceived > 10) {
} 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),
@@ -4501,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;
@@ -4509,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();
+1
View File
@@ -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;
+10 -5
View File
@@ -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
@@ -200,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
@@ -231,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/ *\\$$//' \
@@ -299,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/ *\\$$//' \
+1 -1
View File
@@ -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;
+11 -3
View File
@@ -70,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);
}
+3 -1
View File
@@ -95,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);
+4 -3
View File
@@ -904,9 +904,10 @@ void TrianglesGUI::setNumBlocks(int count, int nTotalBlocks)
}
// Set icon state: spinning if catching up, tick otherwise.
// Use a generous threshold (6 hours) for PoS chains where block intervals
// can be long during difficulty adjustment with few stakers.
if(secs < 6*60*60 && count >= nTotalBlocks)
// 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));
+22 -17
View File
@@ -78,12 +78,6 @@ void WalletModel::pollBalanceChanged()
{
if(nBestHeight != cachedNumBlocks)
{
// Don't block the UI thread waiting for cs_wallet - skip this
// update cycle if the lock is held by the block processing thread
TRY_LOCK(wallet->cs_wallet, lockWallet);
if(!lockWallet)
return;
// Balance and number of transactions might have changed
cachedNumBlocks = nBestHeight;
checkBalanceChanged();
@@ -92,10 +86,12 @@ void WalletModel::pollBalanceChanged()
void WalletModel::checkBalanceChanged()
{
// Get all balances in a single lock acquisition + single pass
// instead of 4 separate lock+iterate cycles
qint64 newBalance = 0, newStake = 0, newUnconfirmedBalance = 0, newImmatureBalance = 0;
wallet->GetAllBalances(newBalance, newStake, newUnconfirmedBalance, newImmatureBalance);
// 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)
{
@@ -112,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);
}
}
}
@@ -246,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
View File
@@ -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
View File
@@ -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;
}
+21 -20
View File
@@ -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()
+4 -4
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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 4
#define DISPLAY_VERSION_REVISION 6
#define DISPLAY_VERSION_BUILD 0
#endif
+5 -2
View File
@@ -1476,13 +1476,15 @@ int64_t CWallet::GetNewMint() const
return nTotal;
}
void CWallet::GetAllBalances(int64_t& nBalance, int64_t& nStake, int64_t& nUnconfirmed, int64_t& nImmature) const
bool CWallet::GetAllBalances(int64_t& nBalance, int64_t& nStake, int64_t& nUnconfirmed, int64_t& nImmature) const
{
nBalance = 0;
nStake = 0;
nUnconfirmed = 0;
nImmature = 0;
LOCK(cs_wallet);
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;
@@ -1499,6 +1501,7 @@ void CWallet::GetAllBalances(int64_t& nBalance, int64_t& nStake, int64_t& nUncon
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
+4 -4
View File
@@ -7,6 +7,7 @@
#include <string>
#include <vector>
#include <atomic>
#include <stdlib.h>
@@ -195,7 +196,7 @@ public:
int64_t GetStake() const;
int64_t GetNewMint() const;
// Get all balances in a single lock acquisition + single pass (avoids 4x lock + 4x iteration)
void GetAllBalances(int64_t& nBalance, int64_t& nStake, int64_t& nUnconfirmed, int64_t& nImmature) const;
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);
@@ -204,9 +205,8 @@ public:
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.
// Access is safe without locks: written atomically by the miner, read by UI for display only.
volatile uint64_t nCachedStakeWeight;
volatile int64_t nCachedStakeWeightTime; // GetTime() when last updated
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);