Skip to content

Commit a121045

Browse files
committed
bch(finding3): regtest as distinct third net -- serve diff-1 0x207fffff
--pool --regtest previously collapsed to testnet params (anchor@955700, P2P->.110) and served unwinnable testnet difficulty, so a single CPU could never win a block. Thread regtest as a third net distinct from mainnet/testnet: - asert.hpp: asert_regtest() + bch_pow_limit_regtest() (fPowNoRetargeting, fixed target at powLimit nBits 0x207fffff; ASERT bypassed on regtest) - header_chain.hpp: BCHChainParams::regtest() (P2P 18444, cold-start tip seeded so served nbits == 0x207fffff before first P2P regtest block) - embedded_daemon.hpp / pool_entrypoint.hpp / main_bch.cpp: is_regtest dispatch threaded through the --pool entrypoint GBT-from-local-regtest-BCHN (regtest_block::build_from_gbt) deferred to a follow-on sub-slice. Consensus-neutral: regtest-only behavior, no mainnet path change. Fenced to src/impl/bch/ + main_bch.cpp dispatch.
1 parent a08b37d commit a121045

5 files changed

Lines changed: 62 additions & 6 deletions

File tree

src/c2pool/main_bch.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -642,7 +642,7 @@ int run_pool(const std::string& peer_host, uint16_t peer_port, bool testnet,
642642

643643
try {
644644
bch::standup_pool_run(ioc, config, anchor_height,
645-
stratum_addr, stratum_port, testnet || regtest);
645+
stratum_addr, stratum_port, testnet || regtest, regtest);
646646
} catch (const std::exception& e) {
647647
std::cout << "[pool] FATAL: " << e.what() << "\n";
648648
return 1;

src/impl/bch/coin/asert.hpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,31 @@ inline ASERTParams asert_testnet3() {
7474
};
7575
}
7676

77+
inline uint256 bch_pow_limit_regtest() {
78+
// BCH regtest powLimit (BCHN CRegTestParams). NOTE: top byte 0x7f does NOT
79+
// satisfy CalculateASERT's 32-leading-zero-bits invariant -- which is why
80+
// regtest runs with fPowNoRetargeting (ASERT is never invoked on regtest).
81+
uint256 v;
82+
v.SetHex("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
83+
return v;
84+
}
85+
86+
/// BCH regtest difficulty params (BCHN CRegTestParams). fPowNoRetargeting=true,
87+
/// so the required target is fixed at the powLimit nBits (0x207fffff) for every
88+
/// block -- ASERT is bypassed (header_chain honours BCHChainParams::no_retargeting).
89+
/// The anchor here is nominal (genesis) and never feeds CalculateASERT.
90+
/// allow_min_difficulty mirrors BCHN regtest. (FINDING3: regtest must be its own
91+
/// net so --pool --regtest serves diff-1 0x207fffff, not the testnet anchor.)
92+
inline ASERTParams asert_regtest() {
93+
return ASERTParams{
94+
ASERTAnchor{0, 0x207fffff, 1296688602}, // genesis nBits/time (nominal)
95+
2 * 24 * 60 * 60,
96+
BCH_TARGET_SPACING,
97+
true,
98+
bch_pow_limit_regtest(),
99+
};
100+
}
101+
77102
inline ASERTParams asert_testnet4() {
78103
return ASERTParams{
79104
ASERTAnchor{16844, 0x1d00ffff, 1605451779},

src/impl/bch/coin/embedded_daemon.hpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,12 @@ class EmbeddedDaemon {
8181
/// Cold-start ctor: floor-anchored ABLA at `anchor_height` (safe default
8282
/// when no VM300 BCHN pin is available yet). `context`/`config` outlive the
8383
/// daemon (owned by the binary entrypoint, same contract as coin::Node).
84-
EmbeddedDaemon(auto* context, config_t* config, uint32_t anchor_height)
84+
EmbeddedDaemon(auto* context, config_t* config, uint32_t anchor_height,
85+
bool is_regtest = false)
8586
: m_config(config),
86-
m_chain(config->m_testnet ? BCHChainParams::testnet()
87-
: BCHChainParams::mainnet()),
87+
m_chain(is_regtest ? BCHChainParams::regtest()
88+
: (config->m_testnet ? BCHChainParams::testnet()
89+
: BCHChainParams::mainnet())),
8890
m_pool(),
8991
m_embedded(m_chain, m_pool, config->m_testnet),
9092
m_node(context, config),

src/impl/bch/coin/header_chain.hpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ struct BCHChainParams {
150150
uint256 pow_limit; // == asert.pow_limit (mirror for check_pow)
151151
uint256 genesis_hash; // SHA256d genesis block hash (identification)
152152
bool allow_min_difficulty{false}; // == asert.allow_min_difficulty (testnet)
153+
bool no_retargeting{false}; // == BCHN fPowNoRetargeting (regtest)
153154

154155
// Fast-start checkpoint: skip syncing from genesis, start from a recent height.
155156
// The header chain seeds this checkpoint as if it were the genesis block.
@@ -196,6 +197,25 @@ struct BCHChainParams {
196197
p.fast_start_checkpoint = Checkpoint{0, p.genesis_hash};
197198
return p;
198199
}
200+
201+
/// BCH regtest params (P2P port 18444). BCHN CRegTestParams:
202+
/// fPowNoRetargeting + fPowAllowMinDifficultyBlocks -- difficulty is fixed at
203+
/// the genesis nBits 0x207fffff (powLimit), so a single CPU can mine it.
204+
/// Genesis is the shared Satoshi regtest genesis. Seeds the cold-start tip so
205+
/// served work nbits == 0x207fffff BEFORE the first P2P regtest block arrives
206+
/// (FINDING3: --pool --regtest previously collapsed to testnet params and
207+
/// served unwinnable testnet difficulty). GBT-from-local-regtest-BCHN
208+
/// (regtest_block::build_from_gbt) is a deferred follow-on slice.
209+
static BCHChainParams regtest() {
210+
BCHChainParams p;
211+
p.asert = asert_regtest();
212+
p.pow_limit = p.asert.pow_limit;
213+
p.allow_min_difficulty = p.asert.allow_min_difficulty;
214+
p.no_retargeting = true;
215+
p.genesis_hash.SetHex("0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206");
216+
p.fast_start_checkpoint = Checkpoint{0, p.genesis_hash};
217+
return p;
218+
}
199219
};
200220

201221
// ─── PoW Functions ──────────────────────────────────────────────────────────
@@ -705,6 +725,14 @@ class HeaderChain {
705725
bool validate_difficulty(const BlockHeaderType& header, uint32_t new_height) {
706726
if (new_height < 2) return true; // genesis + first block
707727

728+
// Regtest (BCHN fPowNoRetargeting): difficulty never adjusts -- every
729+
// block carries the powLimit nBits (0x207fffff). ASERT is bypassed here:
730+
// CalculateASERT would assert on the regtest powLimit (top byte 0x7f
731+
// violates its 32-leading-zero-bits invariant). The bits/target are
732+
// already gated against pow_limit by check_pow at the accept site, so
733+
// accept. (FINDING3.)
734+
if (m_params.no_retargeting) return true;
735+
708736
// Get tip (the block we're building on).
709737
auto prev_opt = lookup_header_internal(header.m_previous_block);
710738
if (!prev_opt) return false;

src/impl/bch/pool_entrypoint.hpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,11 +85,12 @@ inline void standup_pool_run(boost::asio::io_context& ioc,
8585
uint32_t anchor_height,
8686
const std::string& stratum_addr = "0.0.0.0",
8787
uint16_t stratum_port = 0,
88-
bool is_testnet = false)
88+
bool is_testnet = false,
89+
bool is_regtest = false)
8990
{
9091
// 1+2: embedded daemon up first -- it owns the work source + RPC fallback
9192
// the pool node consumes, and is the broadcast sink the node wires into.
92-
coin::EmbeddedDaemon<Config> daemon(&ioc, &config, anchor_height);
93+
coin::EmbeddedDaemon<Config> daemon(&ioc, &config, anchor_height, is_regtest);
9394
daemon.run();
9495

9596
// 3: the pool node (sharechain, LevelDB, P2P, Stratum).

0 commit comments

Comments
 (0)