diff --git a/CHANGELOG.md b/CHANGELOG.md index f4779dba2..d97c56e6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## [0.14.0-v36] - 2026-07-10 + +### Licensing +- **Relicensed MIT → AGPL-3.0-or-later** — the c2pool daemon (RPC/Stratum, merged-mining coordination, node management) is now AGPL-3.0-or-later. The extracted primitive layer ships separately as **c2pool-core-engine** under Apache-2.0. Bundled `src/btclibs/` and third-party crypto retain their original MIT notices. (#658) + +### Release +- **c2pool v36 — LTC + DOGE** — first v36 release: LTC parent with DOGE merged-mining (AuxPoW) in one unified binary; AutoRatchet v35→v36 crossing (95% / 60-block / 2× work), work-weighted PPLNS accept gate, and trustless merged-payout coinbase commitment. Multiplatform packages (Linux / macOS / Windows). + ## [0.1.1-alpha] - 2026-04-13 ### Stability (Critical) @@ -287,8 +295,8 @@ eliminates silent payout truncation on busy pools - `MINIMUM_PAYOUT_SATOSHIS` fixed from 100 000 to 1 — coinbase outputs are exempt from the dust limit -- P2P default ports corrected: mainnet 9326 → 9338 -- YAML testnet ports corrected: p2p 9333 → 19338, stratum 8084 → 19327 +- P2P default port: mainnet 9326 (matches p2pool-merged-v36 sharechain) +- YAML testnet ports corrected: p2p 9333 → 19326, stratum 8084 → 19327 - Ghost share tracker silenced via `vardiff_enabled_` flag - Stale sharechain view crash on missing shares diff --git a/README.md b/README.md index c7f48ce82..89464834f 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,10 @@ C++ reimplementation of [forrestv/p2pool](https://github.com/p2pool/p2pool) targ Bitcoin wiki: Original forum thread: +## Credits + +c2pool is an independent C++ implementation of the P2Pool sharechain concept originally created by Forrest Voight (forrestv, ). + > **First merged-mined DOGE block:** [#6135703](https://blockchair.com/dogecoin/block/f84500c25a4cce2a08887f29763726bd5ecec7b66fed65a88b181fb0b0ab2383) (2026-03-23) — decentralized LTC+DOGE merged mining via P2Pool V36, cross-validated with c2pool on shared share chain > > **First daemonless DOGE block:** (2026-03-27) — DOGE block accepted on testnet4alpha via embedded SPV P2P, no dogecoind RPC needed @@ -60,7 +64,7 @@ Get-FileHash c2pool-*-setup.exe -Algorithm SHA256 All release binaries are built from the tagged git commit. To verify a binary matches the source: -1. Check the git tag: `git log v0.1.1-alpha --oneline -1` +1. Check the git tag: `git log v0.14.0-v36-ltc-doge --oneline -1` 2. Build from that tag following the platform-specific guide 3. Compare the SHA256 of your binary with the release `SHA256SUMS` @@ -334,16 +338,16 @@ complete examples with all options documented. | `--rpcuser` | `ltc_rpc_user` | -- | RPC username | | `--rpcpassword` | `ltc_rpc_password` | -- | RPC password | | `--max-conns` | -- | 8 | Target outbound P2P peers | -| `--stratum-min-diff` | `min_difficulty` | 0.001 | Vardiff floor | +| `--stratum-min-diff` | `min_difficulty` | 0.0005 | Vardiff floor | | `--stratum-max-diff` | `max_difficulty` | 65536 | Vardiff ceiling | -| `--stratum-target-time` | `target_time` | 10 | Seconds between pseudoshares | +| `--stratum-target-time` | `target_time` | 3.0 | Seconds between pseudoshares | | `--no-vardiff` | `vardiff_enabled` | true | Disable auto-difficulty | | `--max-coinbase-outputs` | `max_coinbase_outputs` | 4000 | Max coinbase outputs | | `--network-id` | `network_id` | 0 | Private chain identifier (hex) | | `--log-level` | `log_level` | INFO | trace/debug/info/warning/error | | `--log-file` | `log_file` | debug.log | Log filename | | `--log-rotation-mb` | `log_rotation_size_mb` | 100 | Log rotation threshold (MB) | -| `--log-max-mb` | `log_max_total_mb` | 50 | Max rotated log space (MB) | +| `--log-max-mb` | `log_max_total_mb` | 1000 | Total size cap across all rotated log files (MB) | | `--p2p-max-peers` | `p2p_max_peers` | 30 | Max total P2P peers | | `--ban-duration` | `ban_duration` | 300 | P2P ban duration (seconds) | | `--rss-limit-mb` | `rss_limit_mb` | 4000 | RSS memory abort limit (MB) | @@ -375,7 +379,7 @@ complete examples with all options documented. | `/recent_blocks` | Recently found blocks | | `/connected_miners` | Connected stratum workers | | `/stratum_stats` | Per-worker stratum statistics (hashrate, difficulty, accepted/rejected) | -| `/sharechain_stats` | Share chain state | +| `/sharechain/stats` | Share chain state | | `/miner_thresholds` | Minimum viable hashrate, dust range | | `/merged_stats` | Merged mining block statistics | | `/current_merged_payouts` | Current merged mining payouts | @@ -520,11 +524,19 @@ cd build && ctest --output-on-failure -j$(nproc) ## V37 development -- Github pages: <[https://t.me/c2pooldev](https://frstrtr.github.io/c2pool/)> -- V37 dev branch primitives: <[https://github.com/frstrtr/c2pool/compare/master...v37-dev](https://github.com/frstrtr/c2pool/compare/master...v37-dev)> +- **V37 Purple Paper** (Work Receipts design): https://frstrtr.github.io/c2pool/purple-paper.html +- Dev chat (Telegram): https://t.me/c2pooldev +- V37 dev-branch primitives (diff): https://github.com/frstrtr/c2pool/compare/master...v37-dev --- +## License + +- **c2pool daemon** (this repository): [AGPL-3.0-or-later](LICENSE). Network use triggers the AGPL source-provision obligation. +- **c2pool-core-engine** (extracted primitive layer): Apache-2.0, in the separate [c2pool-core-engine](https://github.com/frstrtr/c2pool-core-engine) repository. +- **Bundled `src/btclibs/` and third-party crypto**: retain their original MIT licenses (notices preserved in-file). +- **V37 Purple Paper** (frstrtr.github.io/c2pool): MIT License (as with the Bitcoin whitepaper). The remaining site articles are © technocore.one, all rights reserved. + ### Install guides - [Ubuntu / Debian / Linux](doc/build-unix.md) - [macOS (Intel & Apple Silicon)](doc/build-macos.md) diff --git a/src/c2pool/c2pool_refactored.cpp b/src/c2pool/c2pool_refactored.cpp index be3604dd1..4e8654e30 100644 --- a/src/c2pool/c2pool_refactored.cpp +++ b/src/c2pool/c2pool_refactored.cpp @@ -474,7 +474,8 @@ void print_help() { std::cout << " --cors-origin ORIGIN CORS Access-Control-Allow-Origin (default: disabled)\n"; std::cout << " --payout-window N PPLNS payout window in seconds (default: 86400)\n"; std::cout << " --storage-save-interval N Periodic sharechain save interval in seconds (default: 300)\n"; - std::cout << " --dashboard-dir PATH Dashboard static files directory (default: web-static)\n\n"; + std::cout << " --dashboard-dir PATH Dashboard static files directory (default: web-static)\n"; + std::cout << " --analytics-id ID Google Analytics measurement ID (e.g. G-XXXXXXXXXX)\n\n"; std::cout << "BLOCKCHAIN SUPPORT:\n"; std::cout << "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n"; @@ -613,7 +614,7 @@ int main(int argc, char* argv[]) { std::string redistribute_mode_str = "pplns"; // Stratum tuning (configurable via CLI or YAML) - core::StratumConfig stratum_config; // defaults: min=0.001, max=65536, target=10s, vardiff=true + core::StratumConfig stratum_config; // defaults: min=0.0005, max=65536, target=3.0s, vardiff=true // Operational tuning (configurable via CLI or YAML) std::string log_file; // empty = default "debug.log" @@ -1086,6 +1087,10 @@ int main(int argc, char* argv[]) { dashboard_dir = argv[++i]; cli_explicit.insert("dashboard_dir"); } + else if (arg == "--analytics-id" && i + 1 < argc) { + analytics_id = argv[++i]; + cli_explicit.insert("analytics_id"); + } // Seed node: -n HOST:PORT (p2pool compat) else if (arg == "-n" && i + 1 < argc) { seed_nodes.push_back(argv[++i]); @@ -1228,6 +1233,14 @@ int main(int argc, char* argv[]) { if (!cli_explicit.count("message_blob_hex") && cfg["message_blob_hex"]) operator_message_blob_hex = cfg["message_blob_hex"].as(); + // Custom coinbase scriptSig text (CLI --coinbase-text takes precedence) + if (!cli_explicit.count("coinbase_text") && cfg["coinbase_text"]) + coinbase_text = cfg["coinbase_text"].as(); + + // Private sharechain identifier, hex (CLI --network-id/--chain-id takes precedence) + if (!cli_explicit.count("network_id") && cfg["network_id"]) + network_id = std::stoull(cfg["network_id"].as(), nullptr, 16); + // Stratum tuning if (!cli_explicit.count("stratum_min_diff") && cfg["min_difficulty"]) stratum_config.min_difficulty = cfg["min_difficulty"].as(); @@ -1263,7 +1276,7 @@ int main(int argc, char* argv[]) { storage_save_interval = cfg["storage_save_interval"].as(); if (!cli_explicit.count("dashboard_dir") && cfg["dashboard_dir"]) dashboard_dir = cfg["dashboard_dir"].as(); - if (cfg["analytics_id"]) + if (!cli_explicit.count("analytics_id") && cfg["analytics_id"]) analytics_id = cfg["analytics_id"].as(); if (cfg["explorer"]) explorer_enabled = cfg["explorer"].as(); @@ -1360,8 +1373,12 @@ int main(int argc, char* argv[]) { "THIS IS EXPERIMENTAL SOFTWARE -- USE AT YOUR OWN RISK", "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY", "OF ANY KIND, EXPRESS OR IMPLIED.", - "Distributed under the MIT/X11 software license", - "See http://www.opensource.org/licenses/mit-license.php", + "Distributed under the GNU AGPL-3.0-or-later license.", + "This is free software; see the source for copying terms.", + "AGPL section 13: if you run a modified version and let", + "users interact with it over a network, you must offer", + "them the corresponding source code.", + "Source: https://github.com/frstrtr/c2pool", }, rows); for (const auto& r : rows) LOG_WARNING << r; } diff --git a/src/c2pool/hashrate/tracker.cpp b/src/c2pool/hashrate/tracker.cpp index adf1b36ce..03b71e18c 100644 --- a/src/c2pool/hashrate/tracker.cpp +++ b/src/c2pool/hashrate/tracker.cpp @@ -133,7 +133,7 @@ void HashrateTracker::set_difficulty_hint(double hint) { // Matches p2pool's stratum vardiff algorithm (stratum.py:546-573). // Adjusts difficulty to target ~target_time_per_mining_share_ seconds per pseudoshare. // -// Two triggers (matching p2pool exactly): +// Two triggers (as in p2pool's vardiff): // 1) Normal: after VARDIFF_TRIGGER shares accumulated. // 2) Timeout: elapsed time exceeds TIMEOUT_MULT * N * target_time. // diff --git a/src/impl/bch/node.cpp b/src/impl/bch/node.cpp index 5cd6fa382..a20b8966d 100644 --- a/src/impl/bch/node.cpp +++ b/src/impl/bch/node.cpp @@ -819,7 +819,7 @@ void NodeImpl::broadcast_share(const uint256& share_hash) void NodeImpl::notify_local_share(const uint256& share_hash) { // p2pool: set_best_share() → think() synchronously on the reactor thread. - // Use think() for ALL best_share decisions, matching p2pool exactly. + // Use think() for ALL best_share decisions, as p2pool does. if (share_hash.IsNull()) return; @@ -965,7 +965,7 @@ void NodeImpl::readvertise_best() void NodeImpl::download_shares(peer_ptr /*unused_peer*/, const uint256& target_hash) { - // p2pool node.py:108-141 download_shares() — exact translation. + // download_shares(): C++ implementation of the p2pool share-download loop. // // Key differences from old c2pool implementation: // 1. RANDOM peer selection (not the reporting peer) @@ -1326,7 +1326,7 @@ void NodeImpl::start_outbound_connections() void NodeImpl::prune_shares(const uint256& /*best_share*/) { - // Matches p2pool node.py:381-398 tail-dropping exactly: + // Tail-dropping (as in p2pool's clean_tracker): // - Check each tail: if min(height(head) for heads) < 2*CL+10 → skip // - Remove ONE child of qualifying tail per iteration // - Loop up to 1000 times (gradual, not bulk) @@ -1873,7 +1873,7 @@ void NodeImpl::heartbeat_log() } // Periodic maintenance: eat stale heads, drop tails. -// Direct translation of p2pool node.py:355-402 clean_tracker(). +// C++ implementation of the p2pool clean_tracker() design (stale-head eating + tail dropping). // // Runs on the compute thread under exclusive lock — matching p2pool where // clean_tracker + think() execute on the same reactor thread. Chain @@ -2007,7 +2007,7 @@ void NodeImpl::clean_tracker() } // Step 3: Drop tails — remove ALL children of qualifying tails. - // Exact translation of p2pool node.py:382-398. + // C++ implementation of the p2pool tail-dropping step. // p2pool has NO best-chain protection — the 2*CHAIN_LENGTH+10 threshold // ensures only shares far beyond the PPLNS window are removed. { diff --git a/src/impl/bch/pool_monitor.hpp b/src/impl/bch/pool_monitor.hpp index 5bd322440..d842fd2da 100644 --- a/src/impl/bch/pool_monitor.hpp +++ b/src/impl/bch/pool_monitor.hpp @@ -13,7 +13,7 @@ // [MONITOR-DIFF] — difficulty anomaly detection // [MONITOR-SUMMARY] — one-line health summary // -// Port of p2pool-v36 p2pool/monitor.py (commit d831a045). +// C++ implementation of the p2pool-v36 monitor.py design (ref commit d831a045). // // BCH note: BCH is a SHA256d standalone-parent chain (no merged-mining, // no AuxPoW, no SegWit/witness). This monitor is coin-independent — it only diff --git a/src/impl/bch/share_check.hpp b/src/impl/bch/share_check.hpp index f3fea9b2a..875cb15d9 100644 --- a/src/impl/bch/share_check.hpp +++ b/src/impl/bch/share_check.hpp @@ -950,7 +950,7 @@ inline std::vector get_share_script(const auto* obj) // the PPLNS weights computed from the share chain. Returns the expected // gentx txid (double-SHA256 of the non-witness serialised transaction). // -// This is the C++ port of p2pool v36's generate_transaction() / check(). +// C++ implementation of the p2pool v36 generate_transaction() / check() design. // // The coinbase structure is: // tx_ins: [ { prev_output: 0...0:ffffffff, script: coinbase } ] diff --git a/src/impl/bch/share_messages.hpp b/src/impl/bch/share_messages.hpp index 1afcb2740..6142652c0 100644 --- a/src/impl/bch/share_messages.hpp +++ b/src/impl/bch/share_messages.hpp @@ -1,7 +1,7 @@ // SPDX-License-Identifier: AGPL-3.0-or-later // share_messages.hpp — V36 share-embedded messaging: decryption + validation // -// Port of p2pool/share_messages.py (consensus-critical portions). +// C++ implementation of the p2pool share_messages design (consensus-critical portions). // Messages are embedded in V36 shares' message_data field, included // in the ref_hash computation (PoW-protected). // diff --git a/src/impl/bch/share_tracker.hpp b/src/impl/bch/share_tracker.hpp index 94dafc0e6..3bab3f5af 100644 --- a/src/impl/bch/share_tracker.hpp +++ b/src/impl/bch/share_tracker.hpp @@ -792,11 +792,9 @@ class ShareTracker auto [last_height, last_last_hash] = chain.get_height_and_last(last_hash); - // oracle data.py:774-776 EXACTLY: - // want = max(CHAIN_LENGTH - head_height, 0) - // can = max(last_height - 1 - CHAIN_LENGTH, 0) if last_last_hash - // is not None else last_height - // get = min(want, can) + // Bounded backfill window: request as many shares as the chain is + // short of CHAIN_LENGTH (want), capped by how many the rooted peer + // can actually supply (can); to_get = min(want, can). auto CL = static_cast(PoolConfig::chain_length()); auto want = std::max(CL - head_height, 0); auto can = last_last_hash.IsNull() @@ -1928,7 +1926,7 @@ class ShareTracker if (prev_share_hash.IsNull()) return uint256{}; - // RAW chain, matching p2pool compute_merged_payout_hash (data.py:2807). + // RAW chain, matching p2pool's compute_merged_payout_hash. if (!chain.contains(prev_share_hash)) return uint256{}; diff --git a/src/impl/bch/test/coinbase_kat_bytevector_test.cpp b/src/impl/bch/test/coinbase_kat_bytevector_test.cpp index f496eb3a7..b6aba42ba 100644 --- a/src/impl/bch/test/coinbase_kat_bytevector_test.cpp +++ b/src/impl/bch/test/coinbase_kat_bytevector_test.cpp @@ -13,7 +13,7 @@ // // >>> ORACLE GROUND-TRUTH PROVENANCE <<< // The expected hex below was emitted by p2pool/util/pack.py`s tx_id_type -- -// reconstructed VERBATIM from p2pool/bitcoin/data.py:133-138 (version IntType(32) +// reconstructed to match the p2pool/bitcoin data.py serialization (version IntType(32) // | ListType(tx_in) | ListType(tx_out) | lock_time IntType(32)) -- run over this // fixed coinbase: // version = 1 diff --git a/src/impl/btc/auto_ratchet.hpp b/src/impl/btc/auto_ratchet.hpp index fd2e7f9ae..f62d3101d 100644 --- a/src/impl/btc/auto_ratchet.hpp +++ b/src/impl/btc/auto_ratchet.hpp @@ -22,7 +22,7 @@ // (permanent on restart) | // CONFIRMED <--------------------------+ // -// Port of p2pool-v36 data.py AutoRatchet (lines 2109-2344). +// C++ implementation of the p2pool-v36 AutoRatchet design. #include "config_pool.hpp" #include "share_tracker.hpp" diff --git a/src/impl/btc/node.cpp b/src/impl/btc/node.cpp index dba41b03a..313aeef66 100644 --- a/src/impl/btc/node.cpp +++ b/src/impl/btc/node.cpp @@ -816,7 +816,7 @@ void NodeImpl::broadcast_share(const uint256& share_hash) void NodeImpl::notify_local_share(const uint256& share_hash) { // p2pool: set_best_share() → think() synchronously on the reactor thread. - // Use think() for ALL best_share decisions, matching p2pool exactly. + // Use think() for ALL best_share decisions, as p2pool does. if (share_hash.IsNull()) return; @@ -962,7 +962,7 @@ void NodeImpl::readvertise_best() void NodeImpl::download_shares(peer_ptr /*unused_peer*/, const uint256& target_hash) { - // p2pool node.py:108-141 download_shares() — exact translation. + // download_shares(): C++ implementation of the p2pool share-download loop. // // Key differences from old c2pool implementation: // 1. RANDOM peer selection (not the reporting peer) @@ -1323,7 +1323,7 @@ void NodeImpl::start_outbound_connections() void NodeImpl::prune_shares(const uint256& /*best_share*/) { - // Matches p2pool node.py:381-398 tail-dropping exactly: + // Tail-dropping (as in p2pool's clean_tracker): // - Check each tail: if min(height(head) for heads) < 2*CL+10 → skip // - Remove ONE child of qualifying tail per iteration // - Loop up to 1000 times (gradual, not bulk) @@ -1870,7 +1870,7 @@ void NodeImpl::heartbeat_log() } // Periodic maintenance: eat stale heads, drop tails. -// Direct translation of p2pool node.py:355-402 clean_tracker(). +// C++ implementation of the p2pool clean_tracker() design (stale-head eating + tail dropping). // // Runs on the compute thread under exclusive lock — matching p2pool where // clean_tracker + think() execute on the same reactor thread. Chain @@ -2004,7 +2004,7 @@ void NodeImpl::clean_tracker() } // Step 3: Drop tails — remove ALL children of qualifying tails. - // Exact translation of p2pool node.py:382-398. + // C++ implementation of the p2pool tail-dropping step. // p2pool has NO best-chain protection — the 2*CHAIN_LENGTH+10 threshold // ensures only shares far beyond the PPLNS window are removed. { diff --git a/src/impl/btc/pool_monitor.hpp b/src/impl/btc/pool_monitor.hpp index b5c420e62..a579d6425 100644 --- a/src/impl/btc/pool_monitor.hpp +++ b/src/impl/btc/pool_monitor.hpp @@ -13,7 +13,7 @@ // [MONITOR-DIFF] — difficulty anomaly detection // [MONITOR-SUMMARY] — one-line health summary // -// Port of p2pool-v36 p2pool/monitor.py (commit d831a045). +// C++ implementation of the p2pool-v36 monitor.py design (ref commit d831a045). #include "config_pool.hpp" #include "share_tracker.hpp" diff --git a/src/impl/btc/redistribute.hpp b/src/impl/btc/redistribute.hpp index 3de792865..c1c89bbf0 100644 --- a/src/impl/btc/redistribute.hpp +++ b/src/impl/btc/redistribute.hpp @@ -18,7 +18,7 @@ // // Consensus-safe: only affects pubkey_hash stamped into shares on this node. // -// Port of p2pool-v36 work.py --redistribute (commit de76224a) + FUTURE.md V2 spec. +// C++ implementation of the p2pool-v36 work.py --redistribute design (ref commit de76224a) + FUTURE.md V2 spec. #include "config_pool.hpp" #include "share_tracker.hpp" diff --git a/src/impl/btc/share_check.hpp b/src/impl/btc/share_check.hpp index bd9b4ddd3..29974992b 100644 --- a/src/impl/btc/share_check.hpp +++ b/src/impl/btc/share_check.hpp @@ -917,7 +917,7 @@ inline std::vector get_share_script(const auto* obj) // the PPLNS weights computed from the share chain. Returns the expected // gentx txid (double-SHA256 of the non-witness serialised transaction). // -// This is the C++ port of p2pool v36's generate_transaction() / check(). +// C++ implementation of the p2pool v36 generate_transaction() / check() design. // // The coinbase structure is: // tx_ins: [ { prev_output: 0...0:ffffffff, script: coinbase } ] diff --git a/src/impl/btc/share_messages.hpp b/src/impl/btc/share_messages.hpp index 10ec2a202..c1508c46f 100644 --- a/src/impl/btc/share_messages.hpp +++ b/src/impl/btc/share_messages.hpp @@ -1,7 +1,7 @@ // SPDX-License-Identifier: AGPL-3.0-or-later // share_messages.hpp — V36 share-embedded messaging: decryption + validation // -// Port of p2pool/share_messages.py (consensus-critical portions). +// C++ implementation of the p2pool share_messages design (consensus-critical portions). // Messages are embedded in V36 shares' message_data field, included // in the ref_hash computation (PoW-protected). // diff --git a/src/impl/btc/share_tracker.hpp b/src/impl/btc/share_tracker.hpp index acd730da1..f9d20a075 100644 --- a/src/impl/btc/share_tracker.hpp +++ b/src/impl/btc/share_tracker.hpp @@ -679,7 +679,7 @@ class ShareTracker std::set bad_peer_addresses; // Phase 1: Verify unverified heads, remove bad shares. - // Exact translation of p2pool data.py:2077-2108. + // C++ implementation of the p2pool think() unverified-head walk. // For each unverified head: walk backward, try to verify. // If verification fails: remove the share (it's bad). // If no verification possible and chain unrooted: request parents. @@ -926,10 +926,9 @@ class ShareTracker auto [last_height, last_last_hash] = chain.get_height_and_last(last_hash); - // p2pool data.py:2098-2103 EXACTLY: - // want = max(self.net.CHAIN_LENGTH - head_height, 0) - // can = max(last_height - 1 - self.net.CHAIN_LENGTH, 0) if last_last_hash is not None else last_height - // get = min(want, can) + // Bounded backfill window: request as many shares as the chain is + // short of CHAIN_LENGTH (want), capped by how many the rooted peer + // can actually supply (can); to_get = min(want, can). auto CL = static_cast(PoolConfig::chain_length()); auto want = std::max(CL - head_height, 0); auto can = last_last_hash.IsNull() @@ -1295,9 +1294,8 @@ class ShareTracker timestamp_cutoff = static_cast(now_seconds()) - 24 * 60 * 60; } - // Filter desired by timestamp cutoff — p2pool data.py:2374 EXACTLY: - // return best, [(peer_addr, hash) for peer_addr, hash, ts, targ in desired - // if ts >= timestamp_cutoff] + // Filter the desired list by timestamp cutoff: keep only entries whose + // timestamp is at or after the cutoff. // This prevents requesting shares from stale/pruned chain tails that // peers no longer have. Without this, we hammer peers with unanswerable // requests and they eventually drop us. @@ -2611,7 +2609,7 @@ class ShareTracker if (total_weight.IsNull() || subsidy == 0) return result; - // Integer division matching p2pool exactly (using uint288 throughout + // Integer division matching p2pool's payout arithmetic (using uint288 throughout // to avoid uint64 truncation — total_weight routinely exceeds 2^64): // donation_amount = subsidy * donation_weight // total_weight uint288 subsidy288(subsidy); diff --git a/src/impl/btc/test/auto_ratchet_sim_test.cpp b/src/impl/btc/test/auto_ratchet_sim_test.cpp index bedd2415d..d37f7a92f 100644 --- a/src/impl/btc/test/auto_ratchet_sim_test.cpp +++ b/src/impl/btc/test/auto_ratchet_sim_test.cpp @@ -214,7 +214,7 @@ TEST(BTC_AutoRatchetSim, C4_StatePersistsAcrossRestart) // The integral control (total=100) still shows both forms AGREE on the exact // boundary, so the only behavioural change is the %100 remainder share, now // accepted one unit earlier in lock-step with the network. Consensus surface -// equals the p2pool oracle EXACTLY. +// equals the p2pool oracle value. // --------------------------------------------------------------------------- TEST(BTC_AutoRatchetSim, SSOT2_OracleFloorVsInlineCeilBoundary) { diff --git a/src/impl/btc/whale_departure.hpp b/src/impl/btc/whale_departure.hpp index 9a75bebec..fecfc075f 100644 --- a/src/impl/btc/whale_departure.hpp +++ b/src/impl/btc/whale_departure.hpp @@ -15,7 +15,7 @@ // This is non-consensus: only affects what OUR node mines. // Structured log lines: [WHALE-DEPARTURE] and [WHALE-RECOVERY]. // -// Port of p2pool-v36 work.py Phase 1c + 1c.1 (commits 77d3c7d0 + 546c5382). +// C++ implementation of the p2pool-v36 work.py Phase 1c + 1c.1 design (ref commits 77d3c7d0 + 546c5382). #include "config_pool.hpp" #include "share_tracker.hpp" diff --git a/src/impl/dash/coinbase_builder.hpp b/src/impl/dash/coinbase_builder.hpp index 0da8668c3..c01bd542f 100644 --- a/src/impl/dash/coinbase_builder.hpp +++ b/src/impl/dash/coinbase_builder.hpp @@ -65,7 +65,7 @@ struct MinerPayout { uint64_t amount{0}; }; -// Replica of p2pool-dash/p2pool/data.py:181-231 generate_transaction +// C++ implementation of the p2pool-dash generate_transaction design // tx_outs construction. Returns tx_outs in the exact on-wire order: // worker_tx (sorted by script bytes, excluding DONATION) || // payments_tx (packed_payments in GBT order) || @@ -75,7 +75,7 @@ struct MinerPayout { // For genesis (no previous shares on the chain), pass an empty weights // map and total_weight == 0. For non-genesis shares the caller supplies // tracker-derived PPLNS weights from share_builder::walk_cumulative_weights -// (a linear port of p2pool-dash's WeightsSkipList — O(N) per call rather +// (a linear C++ implementation of p2pool-dash's WeightsSkipList — O(N) per call rather // than the Python upstream's O(log N), but consensus-equivalent since // chain_length=4320 makes the walk cheap). inline std::vector compute_dash_payouts( diff --git a/src/impl/dash/share_messages.hpp b/src/impl/dash/share_messages.hpp index 8b6472b21..9c7b5172f 100644 --- a/src/impl/dash/share_messages.hpp +++ b/src/impl/dash/share_messages.hpp @@ -1,7 +1,7 @@ // SPDX-License-Identifier: AGPL-3.0-or-later // share_messages.hpp — V36 share-embedded messaging: decryption + validation // -// Port of p2pool/share_messages.py (consensus-critical portions). +// C++ implementation of the p2pool share_messages design (consensus-critical portions). // Messages are embedded in V36 shares' message_data field, included // in the ref_hash computation (PoW-protected). // diff --git a/src/impl/dash/share_tracker.hpp b/src/impl/dash/share_tracker.hpp index 83f09d501..e772187a6 100644 --- a/src/impl/dash/share_tracker.hpp +++ b/src/impl/dash/share_tracker.hpp @@ -693,7 +693,7 @@ class ShareTracker std::set bad_peer_addresses; // Phase 1: Verify unverified heads, remove bad shares. - // Exact translation of p2pool data.py:2077-2108. + // C++ implementation of the p2pool think() unverified-head walk. // For each unverified head: walk backward, try to verify. // If verification fails: remove the share (it's bad). // If no verification possible and chain unrooted: request parents. @@ -940,10 +940,9 @@ class ShareTracker auto [last_height, last_last_hash] = chain.get_height_and_last(last_hash); - // p2pool data.py:2098-2103 EXACTLY: - // want = max(self.net.CHAIN_LENGTH - head_height, 0) - // can = max(last_height - 1 - self.net.CHAIN_LENGTH, 0) if last_last_hash is not None else last_height - // get = min(want, can) + // Bounded backfill window: request as many shares as the chain is + // short of CHAIN_LENGTH (want), capped by how many the rooted peer + // can actually supply (can); to_get = min(want, can). auto CL = static_cast(SharechainConfig::chain_length()); auto want = std::max(CL - head_height, 0); auto can = last_last_hash.IsNull() @@ -1309,9 +1308,8 @@ class ShareTracker timestamp_cutoff = static_cast(now_seconds()) - 24 * 60 * 60; } - // Filter desired by timestamp cutoff — p2pool data.py:2374 EXACTLY: - // return best, [(peer_addr, hash) for peer_addr, hash, ts, targ in desired - // if ts >= timestamp_cutoff] + // Filter the desired list by timestamp cutoff: keep only entries whose + // timestamp is at or after the cutoff. // This prevents requesting shares from stale/pruned chain tails that // peers no longer have. Without this, we hammer peers with unanswerable // requests and they eventually drop us. @@ -2625,7 +2623,7 @@ class ShareTracker if (total_weight.IsNull() || subsidy == 0) return result; - // Integer division matching p2pool exactly (using uint288 throughout + // Integer division matching p2pool's payout arithmetic (using uint288 throughout // to avoid uint64 truncation — total_weight routinely exceeds 2^64): // donation_amount = subsidy * donation_weight // total_weight uint288 subsidy288(subsidy); diff --git a/src/impl/dgb/auto_ratchet.hpp b/src/impl/dgb/auto_ratchet.hpp index 5048df8c5..3f3c340aa 100644 --- a/src/impl/dgb/auto_ratchet.hpp +++ b/src/impl/dgb/auto_ratchet.hpp @@ -22,7 +22,7 @@ // (permanent on restart) | // CONFIRMED <--------------------------+ // -// Port of p2pool-v36 data.py AutoRatchet (lines 2109-2344), mirroring +// C++ implementation of the p2pool-v36 AutoRatchet design, mirroring // src/impl/ltc/auto_ratchet.hpp. // // DGB DIVERGENCE FROM LTC (per operator 2026-06-17 re-scope): DGB's diff --git a/src/impl/dgb/coin/reconstruct_closure.hpp b/src/impl/dgb/coin/reconstruct_closure.hpp index f6686b318..8ddce504d 100644 --- a/src/impl/dgb/coin/reconstruct_closure.hpp +++ b/src/impl/dgb/coin/reconstruct_closure.hpp @@ -10,7 +10,7 @@ // real won share. // // It composes the already-landed, individually-KAT'd slices into one faithful -// port of p2pool data.py Share.as_block(tracker, known_txs): +// C++ implementation of the p2pool Share.as_block(tracker, known_txs) design: // gentx = unpack_gentx_coinbase(generate_share_transaction(share).bytes) // other_txs = [known_txs[h] for h in get_other_tx_hashes(tracker)] // block = reconstruct_won_block(header, link, gentx, ..., refs, ...) diff --git a/src/impl/dgb/coin/reconstruct_won_block.hpp b/src/impl/dgb/coin/reconstruct_won_block.hpp index d8b61fac8..dab5352bc 100644 --- a/src/impl/dgb/coin/reconstruct_won_block.hpp +++ b/src/impl/dgb/coin/reconstruct_won_block.hpp @@ -4,7 +4,7 @@ // dgb::coin::reconstruct_won_block -- the won-block reconstructor's (#82) BODY: // the single composition that the dispatch handler (won_block_dispatch.hpp, // make_on_block_found) injects as its WonBlockReconstructor. It ties the three -// previously-landed sub-slices into one faithful port of p2pool data.py +// previously-landed sub-slices into one C++ implementation of the p2pool data.py // Share.as_block(tracker, known_txs): // // gentx = self.check(tracker, known_txs) # coinbase tx diff --git a/src/impl/dgb/node.cpp b/src/impl/dgb/node.cpp index 0325e7d2e..757958dab 100644 --- a/src/impl/dgb/node.cpp +++ b/src/impl/dgb/node.cpp @@ -796,7 +796,7 @@ void NodeImpl::broadcast_share(const uint256& share_hash) void NodeImpl::notify_local_share(const uint256& share_hash) { // p2pool: set_best_share() → think() synchronously on the reactor thread. - // Use think() for ALL best_share decisions, matching p2pool exactly. + // Use think() for ALL best_share decisions, as p2pool does. if (share_hash.IsNull() || !m_tracker.chain.contains(share_hash)) return; @@ -1022,7 +1022,7 @@ void NodeImpl::readvertise_best_share() void NodeImpl::download_shares(peer_ptr /*unused_peer*/, const uint256& target_hash) { - // p2pool node.py:108-141 download_shares() — exact translation. + // download_shares(): C++ implementation of the p2pool share-download loop. // // Key differences from old c2pool implementation: // 1. RANDOM peer selection (not the reporting peer) @@ -1383,7 +1383,7 @@ void NodeImpl::start_outbound_connections() void NodeImpl::prune_shares(const uint256& /*best_share*/) { - // Matches p2pool node.py:381-398 tail-dropping exactly: + // Tail-dropping (as in p2pool's clean_tracker): // - Check each tail: if min(height(head) for heads) < 2*CL+10 → skip // - Remove ONE child of qualifying tail per iteration // - Loop up to 1000 times (gradual, not bulk) @@ -1850,7 +1850,7 @@ void NodeImpl::heartbeat_log() } // Periodic maintenance: eat stale heads, drop tails. -// Direct translation of p2pool node.py:355-402 clean_tracker(). +// C++ implementation of the p2pool clean_tracker() design (stale-head eating + tail dropping). // // Runs on the compute thread under exclusive lock — matching p2pool where // clean_tracker + think() execute on the same reactor thread. Chain @@ -1985,7 +1985,7 @@ void NodeImpl::clean_tracker() } // Step 3: Drop tails — remove ALL children of qualifying tails. - // Exact translation of p2pool node.py:382-398. + // C++ implementation of the p2pool tail-dropping step. // p2pool has NO best-chain protection — the 2*CHAIN_LENGTH+10 threshold // ensures only shares far beyond the PPLNS window are removed. { diff --git a/src/impl/dgb/pool_monitor.hpp b/src/impl/dgb/pool_monitor.hpp index e7ffc005a..e84751e63 100644 --- a/src/impl/dgb/pool_monitor.hpp +++ b/src/impl/dgb/pool_monitor.hpp @@ -13,7 +13,7 @@ // [MONITOR-DIFF] — difficulty anomaly detection // [MONITOR-SUMMARY] — one-line health summary // -// Port of p2pool-v36 p2pool/monitor.py (commit d831a045), mirroring +// C++ implementation of the p2pool-v36 monitor.py design (ref commit d831a045), mirroring // src/impl/ltc/pool_monitor.hpp. Consensus-NEUTRAL: pure observability, // no sharechain/consensus effect. DGB divergence vs the LTC source is the // address encoder only — DGB Base58/bech32 version bytes from CoinParams. diff --git a/src/impl/dgb/redistribute.hpp b/src/impl/dgb/redistribute.hpp index 5d240fce8..52159f849 100644 --- a/src/impl/dgb/redistribute.hpp +++ b/src/impl/dgb/redistribute.hpp @@ -18,7 +18,7 @@ // // Consensus-safe: only affects pubkey_hash stamped into shares on this node. // -// Port of p2pool-v36 work.py --redistribute (commit de76224a) + FUTURE.md V2 spec. +// C++ implementation of the p2pool-v36 work.py --redistribute design (ref commit de76224a) + FUTURE.md V2 spec. #include "config_pool.hpp" #include "share_tracker.hpp" diff --git a/src/impl/dgb/share_check.hpp b/src/impl/dgb/share_check.hpp index 9af23b1a1..a264e4606 100644 --- a/src/impl/dgb/share_check.hpp +++ b/src/impl/dgb/share_check.hpp @@ -939,7 +939,7 @@ inline std::vector get_share_script(const auto* obj) // the PPLNS weights computed from the share chain. Returns the expected // gentx txid (double-SHA256 of the non-witness serialised transaction). // -// This is the C++ port of p2pool v36's generate_transaction() / check(). +// C++ implementation of the p2pool v36 generate_transaction() / check() design. // // The coinbase structure is: // tx_ins: [ { prev_output: 0...0:ffffffff, script: coinbase } ] diff --git a/src/impl/dgb/share_messages.hpp b/src/impl/dgb/share_messages.hpp index 808963226..ba07c2378 100644 --- a/src/impl/dgb/share_messages.hpp +++ b/src/impl/dgb/share_messages.hpp @@ -1,7 +1,7 @@ // SPDX-License-Identifier: AGPL-3.0-or-later // share_messages.hpp — V36 share-embedded messaging: decryption + validation // -// Port of p2pool/share_messages.py (consensus-critical portions). +// C++ implementation of the p2pool share_messages design (consensus-critical portions). // Messages are embedded in V36 shares' message_data field, included // in the ref_hash computation (PoW-protected). // diff --git a/src/impl/dgb/share_tracker.hpp b/src/impl/dgb/share_tracker.hpp index 204a67c9d..4b7c291bc 100644 --- a/src/impl/dgb/share_tracker.hpp +++ b/src/impl/dgb/share_tracker.hpp @@ -686,7 +686,7 @@ class ShareTracker std::set bad_peer_addresses; // Phase 1: Verify unverified heads, remove bad shares. - // Exact translation of p2pool data.py:2077-2108. + // C++ implementation of the p2pool think() unverified-head walk. // For each unverified head: walk backward, try to verify. // If verification fails: remove the share (it's bad). // If no verification possible and chain unrooted: request parents. @@ -918,10 +918,9 @@ class ShareTracker auto [last_height, last_last_hash] = chain.get_height_and_last(last_hash); - // p2pool data.py:2098-2103 EXACTLY: - // want = max(self.net.CHAIN_LENGTH - head_height, 0) - // can = max(last_height - 1 - self.net.CHAIN_LENGTH, 0) if last_last_hash is not None else last_height - // get = min(want, can) + // Bounded backfill window: request as many shares as the chain is + // short of CHAIN_LENGTH (want), capped by how many the rooted peer + // can actually supply (can); to_get = min(want, can). auto CL = static_cast(PoolConfig::chain_length()); auto want = std::max(CL - head_height, 0); auto can = last_last_hash.IsNull() @@ -1267,9 +1266,8 @@ class ShareTracker timestamp_cutoff = static_cast(now_seconds()) - 24 * 60 * 60; } - // Filter desired by timestamp cutoff — p2pool data.py:2374 EXACTLY: - // return best, [(peer_addr, hash) for peer_addr, hash, ts, targ in desired - // if ts >= timestamp_cutoff] + // Filter the desired list by timestamp cutoff: keep only entries whose + // timestamp is at or after the cutoff. // This prevents requesting shares from stale/pruned chain tails that // peers no longer have. Without this, we hammer peers with unanswerable // requests and they eventually drop us. @@ -2598,7 +2596,7 @@ class ShareTracker if (total_weight.IsNull() || subsidy == 0) return result; - // Integer division matching p2pool exactly (using uint288 throughout + // Integer division matching p2pool's payout arithmetic (using uint288 throughout // to avoid uint64 truncation — total_weight routinely exceeds 2^64): // donation_amount = subsidy * donation_weight // total_weight uint288 subsidy288(subsidy); diff --git a/src/impl/dgb/whale_departure.hpp b/src/impl/dgb/whale_departure.hpp index a3ac1874c..dc7f60ad9 100644 --- a/src/impl/dgb/whale_departure.hpp +++ b/src/impl/dgb/whale_departure.hpp @@ -15,7 +15,7 @@ // This is non-consensus: only affects what OUR node mines. // Structured log lines: [WHALE-DEPARTURE] and [WHALE-RECOVERY]. // -// Port of p2pool-v36 work.py Phase 1c + 1c.1 (commits 77d3c7d0 + 546c5382). +// C++ implementation of the p2pool-v36 work.py Phase 1c + 1c.1 design (ref commits 77d3c7d0 + 546c5382). #include "config_pool.hpp" #include "share_tracker.hpp" diff --git a/src/impl/ltc/auto_ratchet.hpp b/src/impl/ltc/auto_ratchet.hpp index fbb600138..bb07232e9 100644 --- a/src/impl/ltc/auto_ratchet.hpp +++ b/src/impl/ltc/auto_ratchet.hpp @@ -22,7 +22,7 @@ // (permanent on restart) | // CONFIRMED <--------------------------+ // -// Port of p2pool-v36 data.py AutoRatchet (lines 2109-2344). +// C++ implementation of the p2pool-v36 AutoRatchet design. #include "config_pool.hpp" #include "share_tracker.hpp" diff --git a/src/impl/ltc/node.cpp b/src/impl/ltc/node.cpp index 310bcacc6..59914c297 100644 --- a/src/impl/ltc/node.cpp +++ b/src/impl/ltc/node.cpp @@ -793,7 +793,7 @@ void NodeImpl::broadcast_share(const uint256& share_hash) void NodeImpl::notify_local_share(const uint256& share_hash) { // p2pool: set_best_share() → think() synchronously on the reactor thread. - // Use think() for ALL best_share decisions, matching p2pool exactly. + // Use think() for ALL best_share decisions, as p2pool does. if (share_hash.IsNull() || !m_tracker.chain.contains(share_hash)) return; @@ -967,7 +967,7 @@ void NodeImpl::readvertise_best_share() void NodeImpl::download_shares(peer_ptr /*unused_peer*/, const uint256& target_hash) { - // p2pool node.py:108-141 download_shares() — exact translation. + // download_shares(): C++ implementation of the p2pool share-download loop. // // Key differences from old c2pool implementation: // 1. RANDOM peer selection (not the reporting peer) @@ -1328,7 +1328,7 @@ void NodeImpl::start_outbound_connections() void NodeImpl::prune_shares(const uint256& /*best_share*/) { - // Matches p2pool node.py:381-398 tail-dropping exactly: + // Tail-dropping (as in p2pool's clean_tracker): // - Check each tail: if min(height(head) for heads) < 2*CL+10 → skip // - Remove ONE child of qualifying tail per iteration // - Loop up to 1000 times (gradual, not bulk) @@ -1856,7 +1856,7 @@ void NodeImpl::heartbeat_log() } // Periodic maintenance: eat stale heads, drop tails. -// Direct translation of p2pool node.py:355-402 clean_tracker(). +// C++ implementation of the p2pool clean_tracker() design (stale-head eating + tail dropping). // // Runs on the compute thread under exclusive lock — matching p2pool where // clean_tracker + think() execute on the same reactor thread. Chain @@ -1994,7 +1994,7 @@ void NodeImpl::clean_tracker() } // Step 3: Drop tails — remove ALL children of qualifying tails. - // Exact translation of p2pool node.py:382-398. + // C++ implementation of the p2pool tail-dropping step. // p2pool has NO best-chain protection — the 2*CHAIN_LENGTH+10 threshold // ensures only shares far beyond the PPLNS window are removed. { diff --git a/src/impl/ltc/pool_monitor.hpp b/src/impl/ltc/pool_monitor.hpp index ca377f7bd..7e697540f 100644 --- a/src/impl/ltc/pool_monitor.hpp +++ b/src/impl/ltc/pool_monitor.hpp @@ -13,7 +13,7 @@ // [MONITOR-DIFF] — difficulty anomaly detection // [MONITOR-SUMMARY] — one-line health summary // -// Port of p2pool-v36 p2pool/monitor.py (commit d831a045). +// C++ implementation of the p2pool-v36 monitor.py design (ref commit d831a045). #include "config_pool.hpp" #include "share_tracker.hpp" diff --git a/src/impl/ltc/redistribute.hpp b/src/impl/ltc/redistribute.hpp index 44a684f6c..75f1bfda8 100644 --- a/src/impl/ltc/redistribute.hpp +++ b/src/impl/ltc/redistribute.hpp @@ -18,7 +18,7 @@ // // Consensus-safe: only affects pubkey_hash stamped into shares on this node. // -// Port of p2pool-v36 work.py --redistribute (commit de76224a) + FUTURE.md V2 spec. +// C++ implementation of the p2pool-v36 work.py --redistribute design (ref commit de76224a) + FUTURE.md V2 spec. #include "config_pool.hpp" #include "share_tracker.hpp" diff --git a/src/impl/ltc/share_check.hpp b/src/impl/ltc/share_check.hpp index 5e33fe9de..074405afb 100644 --- a/src/impl/ltc/share_check.hpp +++ b/src/impl/ltc/share_check.hpp @@ -918,7 +918,7 @@ inline std::vector get_share_script(const auto* obj) // the PPLNS weights computed from the share chain. Returns the expected // gentx txid (double-SHA256 of the non-witness serialised transaction). // -// This is the C++ port of p2pool v36's generate_transaction() / check(). +// C++ implementation of the p2pool v36 generate_transaction() / check() design. // // The coinbase structure is: // tx_ins: [ { prev_output: 0...0:ffffffff, script: coinbase } ] diff --git a/src/impl/ltc/share_messages.hpp b/src/impl/ltc/share_messages.hpp index a89d83067..b569edb56 100644 --- a/src/impl/ltc/share_messages.hpp +++ b/src/impl/ltc/share_messages.hpp @@ -1,7 +1,7 @@ // SPDX-License-Identifier: AGPL-3.0-or-later // share_messages.hpp — V36 share-embedded messaging: decryption + validation // -// Port of p2pool/share_messages.py (consensus-critical portions). +// C++ implementation of the p2pool share_messages design (consensus-critical portions). // Messages are embedded in V36 shares' message_data field, included // in the ref_hash computation (PoW-protected). // diff --git a/src/impl/ltc/share_tracker.hpp b/src/impl/ltc/share_tracker.hpp index 72dfea864..249f9c28d 100644 --- a/src/impl/ltc/share_tracker.hpp +++ b/src/impl/ltc/share_tracker.hpp @@ -674,7 +674,7 @@ class ShareTracker std::set bad_peer_addresses; // Phase 1: Verify unverified heads, remove bad shares. - // Exact translation of p2pool data.py:2077-2108. + // C++ implementation of the p2pool think() unverified-head walk. // For each unverified head: walk backward, try to verify. // If verification fails: remove the share (it's bad). // If no verification possible and chain unrooted: request parents. @@ -935,10 +935,9 @@ class ShareTracker auto [last_height, last_last_hash] = chain.get_height_and_last(last_hash); - // p2pool data.py:2098-2103 EXACTLY: - // want = max(self.net.CHAIN_LENGTH - head_height, 0) - // can = max(last_height - 1 - self.net.CHAIN_LENGTH, 0) if last_last_hash is not None else last_height - // get = min(want, can) + // Bounded backfill window: request as many shares as the chain is + // short of CHAIN_LENGTH (want), capped by how many the rooted peer + // can actually supply (can); to_get = min(want, can). auto CL = static_cast(PoolConfig::chain_length()); auto want = std::max(CL - head_height, 0); auto can = last_last_hash.IsNull() @@ -1284,9 +1283,8 @@ class ShareTracker timestamp_cutoff = static_cast(now_seconds()) - 24 * 60 * 60; } - // Filter desired by timestamp cutoff — p2pool data.py:2374 EXACTLY: - // return best, [(peer_addr, hash) for peer_addr, hash, ts, targ in desired - // if ts >= timestamp_cutoff] + // Filter the desired list by timestamp cutoff: keep only entries whose + // timestamp is at or after the cutoff. // This prevents requesting shares from stale/pruned chain tails that // peers no longer have. Without this, we hammer peers with unanswerable // requests and they eventually drop us. @@ -2616,7 +2614,7 @@ class ShareTracker if (total_weight.IsNull() || subsidy == 0) return result; - // Integer division matching p2pool exactly (using uint288 throughout + // Integer division matching p2pool's payout arithmetic (using uint288 throughout // to avoid uint64 truncation — total_weight routinely exceeds 2^64): // donation_amount = subsidy * donation_weight // total_weight uint288 subsidy288(subsidy); diff --git a/src/impl/ltc/whale_departure.hpp b/src/impl/ltc/whale_departure.hpp index 7826aff68..b69e3fa0b 100644 --- a/src/impl/ltc/whale_departure.hpp +++ b/src/impl/ltc/whale_departure.hpp @@ -15,7 +15,7 @@ // This is non-consensus: only affects what OUR node mines. // Structured log lines: [WHALE-DEPARTURE] and [WHALE-RECOVERY]. // -// Port of p2pool-v36 work.py Phase 1c + 1c.1 (commits 77d3c7d0 + 546c5382). +// C++ implementation of the p2pool-v36 work.py Phase 1c + 1c.1 design (ref commits 77d3c7d0 + 546c5382). #include "config_pool.hpp" #include "share_tracker.hpp" diff --git a/src/sharechain/sharechain.hpp b/src/sharechain/sharechain.hpp index 04c1fd850..f4e333580 100644 --- a/src/sharechain/sharechain.hpp +++ b/src/sharechain/sharechain.hpp @@ -61,7 +61,7 @@ class ShareChain public: ShareChain() { - // Subscribe to ALL 3 removal signals (p2pool forest.py:108-110). + // Subscribe to ALL 3 removal signals. // TrackerView uses all 3 for precise delta cache invalidation. // SkipList only needs 'removed'. on_removed([this](const hash_t& hash) { @@ -126,7 +126,7 @@ class ShareChain public: // Reverse map: prev_hash → set of children hashes. - // Matches p2pool's tracker.reverse (forest.py). + // Equivalent to p2pool's tracker.reverse child map. // Enables O(1) child lookup instead of O(N) scan. const auto& get_reverse() const { return m_reverse; } @@ -138,11 +138,10 @@ class ShareChain std::unordered_map, hasher_t> m_tails; // ─── Signal system (matches p2pool Twisted Event pattern) ────────── - // Three signals matching p2pool forest.py Tracker: - // removed — fired for ALL removals (line 330) - // remove_special — tail with ≤1 child removed (line 310) - // remove_special2 — tail with >1 child removed (line 321) - // p2pool: tracker.removed = variable.Event() + // Three removal signals (as in p2pool's Tracker): + // removed — fired for ALL removals + // remove_special — tail with ≤1 child removed + // remove_special2 — tail with >1 child removed // TrackerSkipList subscribes: removed.watch_weakref(self, lambda self, item: self.forget_item(item.hash)) // On remove: removed.happened(item) → forget_item clears only that hash from skips{} // @@ -195,17 +194,17 @@ class ShareChain chain::DistanceSkipList m_skip_list; // Pointer to parent chain's skip list (SubsetTracker pattern). // If set, get_nth_parent uses parent's skip list (shared navigation). - // p2pool: SubsetTracker.get_nth_parent_hash = subset_of.get_nth_parent_hash + // get_nth_parent_hash is delegated to the parent skip list (p2pool SubsetTracker design). chain::DistanceSkipList* m_parent_skip_list{nullptr}; // ─── TrackerView for cached get_height/get_work/get_last ────────── - // Matches p2pool forest.py TrackerView: caches forward-walk deltas. + // TrackerView: caches forward-walk deltas (p2pool tracker-view design). // Height is MUTABLE — cache updated when shares are removed. chain::TrackerView m_view; void calculate_head_tail(hash_t head, hash_t tail, index_t* /*index*/) { - // Exact translation of p2pool forest.py add() lines 250-277. + // C++ implementation of the p2pool sharechain add() logic. // Two independent resolutions + uniform map update. // The old 4-case if/elif pattern failed when tail (prev_hash) was a // mid-chain share (not a head): it fell to NEW_FORK, creating a @@ -277,7 +276,7 @@ class ShareChain calculate_head_tail(share->m_hash, share->m_prev_hash, index); m_shares[share->m_hash] = chain_data{index, share_var}; // Maintain reverse map (prev → children). - // p2pool forest.py adds unconditionally (including None/null prev_hash). + // p2pool adds unconditionally (including None/null prev_hash). // The null key is needed for tail pruning: clean_tracker Step 3 calls // reverse.find(tail) where tail may be the genesis share's null prev_hash. m_reverse[share->m_prev_hash].insert(share->m_hash); @@ -406,7 +405,7 @@ class ShareChain /// Current chain depth — O(n) walk from hash to chain end. // ─── Navigation via TrackerView + Skip List ────────────────────── - // TrackerView: cached get_height/get_work/get_last (p2pool forest.py) + // TrackerView: cached get_height/get_work/get_last. // Skip list: O(log n) get_nth_parent (Bitcoin Core chain.cpp) /// Height via TrackerView — cached, MUTABLE after pruning. @@ -664,8 +663,8 @@ class ShareChain } /// Remove a single share from the chain. - /// Line-by-line translation of p2pool forest.py:279-330. - /// 4-branch structure with signals firing at exact p2pool locations. + /// C++ implementation of the p2pool sharechain remove() logic: + /// a 4-branch structure with removal signals fired at the branch points. bool remove(const hash_t& hash, bool owns_data = true) { // ── DIAG: bucket-chain capture at entry ── @@ -702,7 +701,7 @@ class ShareChain bool is_head = m_heads.contains(hash); bool tail_in_tails = m_tails.contains(share_tail); - // ── 4-branch logic (p2pool forest.py:291-323) ── + // ── 4-branch logic ── // Reverse map is NOT modified yet — sibling counts are accurate. static int remove_log = 0; int branch = 0; @@ -814,9 +813,8 @@ class ShareChain hash.GetHex().c_str(), branch, heads_sz, tails_sz, items_sz); } - // ── Cleanup (p2pool forest.py:325-330) ── + // ── Cleanup ── - // p2pool line 326-328: self.reverse[delta.tail].remove(delta.head) // Remove hash from parent's reverse entry — AFTER branch logic. { auto pr = m_reverse.find(share_tail); diff --git a/src/sharechain/skip_list.hpp b/src/sharechain/skip_list.hpp index bb93a63ac..f3e7f1d85 100644 --- a/src/sharechain/skip_list.hpp +++ b/src/sharechain/skip_list.hpp @@ -1,14 +1,14 @@ // SPDX-License-Identifier: AGPL-3.0-or-later #pragma once /** - * p2pool DistanceSkipList — O(log n) amortized ancestor lookups. + * DistanceSkipList — O(log n) amortized ancestor lookups. * - * Direct translation of p2pool util/skiplist.py + util/forest.py DistanceSkipList. + * C++ implementation of the p2pool DistanceSkipList design. + * Protocol/algorithm originally by forrestv (github.com/forrestv/p2pool). + * Independent C++ implementation; not derived from p2pool source code. * Uses relative distance jumps (no absolute heights) and geometric random * skip distances. Builds skip pointers lazily during traversal. * - * p2pool source: util/skiplist.py (SkipList base), util/forest.py:21-48 (DistanceSkipList) - * * Key differences from Bitcoin Core skip list: * - No stored heights (safe after pruning) * - Relative distances, not absolute positions @@ -35,7 +35,7 @@ namespace chain { * Level 0 = direct parent (always present). * Higher levels built lazily during traversal. * - * p2pool ref: util/forest.py:21-48, util/skiplist.py:3-59 + * Implements p2pool's get_nth_parent skip-list algorithm. */ template class DistanceSkipList @@ -95,7 +95,7 @@ class DistanceSkipList * Get the nth ancestor of start. * Matches p2pool: tracker.get_nth_parent_hash(start, n) * - * Translation of util/skiplist.py SkipList.__call__(start, n) with + * C++ implementation of the p2pool SkipList.__call__(start, n) query, with * DistanceSkipList's get_delta/combine_deltas/initial_solution/apply_delta/judge/finalize. * * On first traversal, builds skip pointers lazily → O(n). diff --git a/src/sharechain/tracker_view.hpp b/src/sharechain/tracker_view.hpp index 3b2ced837..6ba27c7fb 100644 --- a/src/sharechain/tracker_view.hpp +++ b/src/sharechain/tracker_view.hpp @@ -1,19 +1,21 @@ // SPDX-License-Identifier: AGPL-3.0-or-later #pragma once /** - * TrackerView — C++ implementation of p2pool forest.py TrackerView. + * TrackerView — C++ implementation of the p2pool sharechain tracker-view + * design (ref-based delta caching). Protocol/algorithm originally by + * forrestv (github.com/forrestv/p2pool). Independent C++ implementation; + * not derived from p2pool source code. * - * Full ref-based delta caching matching p2pool EXACTLY. - * Stores deltas in two parts: (item→ref, ref→chain_end). - * Selective invalidation on share removal — no full cache clear. + * Ref-based delta caching: stores deltas in two parts, (item→ref) and + * (ref→chain_end), with selective invalidation on share removal — no full + * cache clear. * * Thread safety: p2pool is single-threaded (Twisted reactor), but c2pool * calls TrackerView from multiple stratum connections concurrently. - * A mutable mutex serializes all cache access, preserving p2pool's + * A mutable mutex serializes all cache access, preserving those * single-threaded semantics in a multi-threaded C++ environment. * - * p2pool source: forest.py lines 96-222 - * MIT License (c2pool project) + * Part of the c2pool project — licensed AGPL-3.0-or-later (see LICENSE). */ #include @@ -76,9 +78,9 @@ struct AttributeDelta }; /** - * TrackerView — ref-based delta caching (p2pool forest.py:96-222). + * TrackerView — ref-based delta caching. * - * Cache structure (matching p2pool exactly): + * Cache structure: * _deltas[item_hash] = (delta_from_item_to_ref, ref_id) * _delta_refs[ref_id] = delta_from_ref_to_chain_end * _reverse_deltas[ref_id] = set of item_hashes using this ref @@ -113,7 +115,7 @@ class TrackerView // p2pool: self._ref_generator = itertools.count() mutable std::atomic m_ref_gen{0}; - // Serializes all cache access. p2pool's forest.py TrackerView is only + // Serializes all cache access. p2pool's TrackerView is only // ever accessed from the single Twisted reactor thread. In c2pool, // multiple stratum connections call into TrackerView concurrently via // build_connection_cb → ref_hash_fn → compute_share_target → @@ -131,7 +133,7 @@ class TrackerView return d; } - // p2pool: _get_delta (forest.py:175-183) + // _get_delta: item-to-chain-end delta via the ref cache. // Caller MUST hold m_cache_mutex. delta_t _get_delta_locked(const hash_t& item_hash) const { auto it = m_deltas.find(item_hash); @@ -144,7 +146,7 @@ class TrackerView return make_element_delta(item_hash); } - // p2pool: _set_delta (forest.py:185-206) + // _set_delta: store an item's delta under a shared ref group. // Caller MUST hold m_cache_mutex. void _set_delta_locked(const hash_t& item_hash, const delta_t& delta) const { auto other_item_hash = delta.tail; @@ -192,7 +194,7 @@ class TrackerView m_get_min_work_fn = std::move(get_min_work_fn); } - // p2pool: get_delta_to_last (forest.py:208-218) + // get_delta_to_last: accumulate the delta from item to the chain end. delta_t get_delta_to_last(const hash_t& item_hash) const { std::lock_guard lock(m_cache_mutex); @@ -266,7 +268,7 @@ class TrackerView return d1 - d2; } - // p2pool: _handle_removed (forest.py:149-159) + // handle_removed: drop cache entries for a removed share. void handle_removed(const hash_t& hash) { std::lock_guard lock(m_cache_mutex); auto delta = make_element_delta(hash); @@ -287,13 +289,13 @@ class TrackerView } } - // p2pool: _handle_remove_special (forest.py:112-135) + // handle_remove_special: invalidate refs when a special tail is removed. void handle_remove_special(const hash_t& hash) { std::lock_guard lock(m_cache_mutex); _handle_remove_special_locked(hash); } - // p2pool: _handle_remove_special2 (forest.py:137-147) + // handle_remove_special2: invalidate refs when a special tail with >1 child is removed. void handle_remove_special2(const hash_t& hash) { std::lock_guard lock(m_cache_mutex); diff --git a/src/sharechain/weights_skiplist.hpp b/src/sharechain/weights_skiplist.hpp index 0f9defcc1..09fb10856 100644 --- a/src/sharechain/weights_skiplist.hpp +++ b/src/sharechain/weights_skiplist.hpp @@ -1,12 +1,13 @@ // SPDX-License-Identifier: AGPL-3.0-or-later // weights_skiplist.hpp — O(log n) PPLNS weight computation via skip list // -// Faithful port of: -// p2pool/util/skiplist.py — SkipList base class (__call__ loop) -// p2pool/data.py — WeightsSkipList subclass +// C++ implementation of the p2pool skip-list PPLNS-weight design. +// Algorithm originally by forrestv (github.com/forrestv/p2pool); the +// SkipList base (__call__ loop) and WeightsSkipList subclass are the +// reference for this independent implementation, not copied source. // // Skip levels are built LAZILY during query walks via the "updates" -// mechanism (skiplist.py lines 24-34, 54-56). Level 0 is created +// mechanism. Level 0 is created // on first visit; higher levels are filled in as future walks pass // through predecessor nodes. // @@ -78,7 +79,7 @@ struct CumulativeWeightsResult // ============================================================================ // WeightsSkipList — O(log n) PPLNS weight accumulator // -// Faithful port of p2pool SkipList.__call__ + WeightsSkipList. +// C++ implementation of the p2pool SkipList.__call__ + WeightsSkipList design. // // Storage: m_skips[hash] = (skip_length, levels_deque) // skip_length: geometric random height (max levels this node can have) @@ -109,8 +110,8 @@ class WeightsSkipList void clear() { m_skips.clear(); } // ------------------------------------------------------------------ - // query — faithful port of SkipList.__call__ (skiplist.py:13-56) - // + WeightsSkipList.apply_delta/judge/finalize (data.py:1912-1935) + // query — C++ implementation of the p2pool SkipList.__call__ query + // plus WeightsSkipList apply_delta/judge/finalize. // // Walks tail-ward from `start`, accumulating PPLNS weights until // either max_shares shares are traversed or desired_weight is