From fb2990c33ee77629934450e0ad28eeb1e99fce50 Mon Sep 17 00:00:00 2001 From: frstrtr Date: Mon, 15 Jun 2026 08:39:08 +0000 Subject: [PATCH] ltc: fix private-net IDENTIFIER truncation + independent PREFIX (V36 ship-blocker) Two defects prevented c2pool from joining any p2pool private net: (i) --network-id truncated to 32 bits: parsed via static_cast(std::stoul(...)) and formatted with setw(8), so an 8-byte id (e.g. 9765c71ad4c00467) collapsed to its low 4 bytes (d4c00467). Parse as uint64_t (std::stoull) and format with setw(16); drop the now-redundant right-pad. (ii) PREFIX was XOR-derived from IDENTIFIER in set_network_id(), so two nodes with the same id but a different prefix source got "prefix doesn't match" -> Peers:0. PREFIX is now an independent per-network constant, overridable via a new --prefix flag; set_network_id() no longer derives it. chain_fingerprint_u64() now hashes the effective prefix_hex(). Adds set_prefix() + --prefix CLI flag and help text. ltc shape only; btc/dgb/dash/bch receive the identical fix via their stewards. --- src/c2pool/c2pool_refactored.cpp | 23 ++++++++++++++++++----- src/impl/ltc/config_pool.hpp | 28 +++++++++++++--------------- 2 files changed, 31 insertions(+), 20 deletions(-) diff --git a/src/c2pool/c2pool_refactored.cpp b/src/c2pool/c2pool_refactored.cpp index 4a438e624..d3be97281 100644 --- a/src/c2pool/c2pool_refactored.cpp +++ b/src/c2pool/c2pool_refactored.cpp @@ -442,6 +442,10 @@ void print_help() { std::cout << " Nonzero: creates a private sharechain. P2P prefix\n"; std::cout << " and THE metadata will carry this ID on the blockchain.\n"; std::cout << " Genesis shares are created automatically when chain is empty.\n"; + std::cout << " Full 8 bytes (up to 16 hex chars) are used, not truncated.\n"; + std::cout << " --prefix HEX Explicit P2P transport-framing prefix (hex, up to 8 bytes).\n"; + std::cout << " Independent of --network-id; defaults to the compiled\n"; + std::cout << " per-network constant. Must match across all peers.\n"; std::cout << " --startup-mode MODE Sharechain startup behavior:\n"; std::cout << " auto — wait for peers (60s), then genesis if none (default)\n"; std::cout << " genesis — create new chain immediately, don't wait for peers\n"; @@ -642,7 +646,8 @@ int main(int argc, char* argv[]) { std::string coinbase_text; // --coinbase-text (replaces /c2pool/ tag) // Private sharechain - uint32_t network_id = 0; // 0 = public p2pool network, nonzero = private + uint64_t network_id = 0; // 0 = public p2pool network, nonzero = private (full 8 bytes) + std::string prefix_override_hex; // explicit --prefix; empty = compiled per-network default // Startup mode: wait (default, p2pool persist=true), genesis, auto enum class StartupMode { AUTO, GENESIS, WAIT }; @@ -960,9 +965,14 @@ int main(int argc, char* argv[]) { cli_explicit.insert("coinbase_text"); } else if ((arg == "--network-id" || arg == "--chain-id") && i + 1 < argc) { - network_id = static_cast(std::stoul(argv[++i], nullptr, 16)); + // Full 8-byte identifier: parse as 64-bit, never truncate to 32 bits + network_id = std::stoull(argv[++i], nullptr, 16); cli_explicit.insert("network_id"); } + else if (arg == "--prefix" && i + 1 < argc) { + prefix_override_hex = argv[++i]; + cli_explicit.insert("prefix"); + } else if (arg == "--startup-mode" && i + 1 < argc) { std::string mode = argv[++i]; if (mode == "genesis") startup_mode = StartupMode::GENESIS; @@ -2530,11 +2540,14 @@ int main(int argc, char* argv[]) { // Private chain: override IDENTIFIER and PREFIX before any P2P or share ops if (network_id != 0) { std::ostringstream hex; - hex << std::hex << std::setfill('0') << std::setw(8) << network_id; - // Pad to 16 hex chars (8 bytes) for full IDENTIFIER + hex << std::hex << std::setfill('0') << std::setw(16) << network_id; // full 8-byte IDENTIFIER, no truncation + // setw(16) already produced the full 8-byte IDENTIFIER std::string id_hex = hex.str(); - while (id_hex.size() < 16) id_hex += "00"; ltc::PoolConfig::set_network_id(id_hex); + // PREFIX is independent: compiled per-network default unless + // explicitly overridden via --prefix (never derived from the ID) + if (!prefix_override_hex.empty()) + ltc::PoolConfig::set_prefix(prefix_override_hex); LOG_INFO << "[Private] Network ID: " << ltc::PoolConfig::identifier_hex() << " (PREFIX: " << ltc::PoolConfig::prefix_hex() << ")"; } diff --git a/src/impl/ltc/config_pool.hpp b/src/impl/ltc/config_pool.hpp index 28140868c..97be3296b 100644 --- a/src/impl/ltc/config_pool.hpp +++ b/src/impl/ltc/config_pool.hpp @@ -142,7 +142,7 @@ class PoolConfig : protected core::Fileconfig static inline std::string override_prefix_hex; /// Set private network identity. IDENTIFIER is the consensus secret - /// (hashed into ref_hash). PREFIX is derived from it for transport framing. + /// (hashed into ref_hash). PREFIX is NOT derived from it (see set_prefix). /// Call once at startup before any P2P or share operations. static void set_network_id(const std::string& network_id_hex) { if (network_id_hex.empty() || network_id_hex == "0" || network_id_hex == "00000000") @@ -154,20 +154,18 @@ class PoolConfig : protected core::Fileconfig if (padded.size() > 16) padded = padded.substr(0, 16); override_identifier_hex = padded; + } - // Derive PREFIX from IDENTIFIER using simple XOR mixing - // PREFIX = IDENTIFIER bytes XOR-rotated (fast, deterministic, non-reversible enough - // for transport framing — the real security is in IDENTIFIER via ref_hash) - auto id_bytes = ParseHex(padded); - static const char* HEX = "0123456789abcdef"; - override_prefix_hex.clear(); - override_prefix_hex.reserve(16); - for (size_t i = 0; i < 8 && i < id_bytes.size(); ++i) { - // XOR with rotated byte + constant to ensure PREFIX != IDENTIFIER - uint8_t b = id_bytes[i] ^ id_bytes[(i + 3) % id_bytes.size()] ^ 0x5A; - override_prefix_hex += HEX[b >> 4]; - override_prefix_hex += HEX[b & 0x0f]; - } + /// Set an explicit transport-framing PREFIX for a private chain (--prefix). + /// Independent of IDENTIFIER; when unset, prefix_hex() falls back to the + /// compiled per-network constant. All peers on the chain must agree on it. + static void set_prefix(const std::string& prefix_hex_in) { + if (prefix_hex_in.empty() || prefix_hex_in == "0") + return; + std::string padded = prefix_hex_in; + while (padded.size() < 16) padded = "0" + padded; + if (padded.size() > 16) padded = padded.substr(padded.size() - 16); + override_prefix_hex = padded; } static const std::string& identifier_hex() { @@ -191,7 +189,7 @@ class PoolConfig : protected core::Fileconfig if (override_identifier_hex.empty()) return 0; // public network - auto pfx_bytes = ParseHex(override_prefix_hex); + auto pfx_bytes = ParseHex(prefix_hex()); // effective prefix (override or compiled default) auto id_bytes = ParseHex(override_identifier_hex); std::vector preimage; preimage.reserve(pfx_bytes.size() + id_bytes.size());