Skip to content

Commit b5c3d0b

Browse files
committed
Startup modes: --genesis, --wait-for-peers, --startup-mode auto|genesis|wait
Three sharechain startup behaviors: auto (default): Wait --startup-timeout seconds (60) for peers to send shares. If none arrive, enter genesis mode. Prevents accidental forks during brief network outages while allowing new chains to bootstrap. genesis: Create new chain immediately. First miner solution becomes the genesis share (previous_share_hash=null). Use when starting a private chain or when you are the first node. wait: Never create genesis. Wait indefinitely for peers with shares. Use when you know the network exists but peers are temporarily down. Prevents creating a competing fork during extended outages. Clear startup logging: [Sharechain] No shares loaded from storage [Sharechain] Startup mode: auto [Sharechain] AUTO MODE — waiting 60s for peers, then genesis if none [Sharechain] Received shares from peer! best=abc123... OR: [Sharechain] No peers found after 60s — entering GENESIS MODE Qt PageLaunch: startup mode dropdown (auto/genesis/wait) in private sharechain section. Wired into command preview. CLI shortcuts: --genesis (alias for --startup-mode genesis), --wait-for-peers (alias for --startup-mode wait).
1 parent 8864897 commit b5c3d0b

3 files changed

Lines changed: 115 additions & 1 deletion

File tree

src/c2pool/c2pool_refactored.cpp

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,12 @@ void print_help() {
225225
std::cout << " Default: 0 (public p2pool network)\n";
226226
std::cout << " Nonzero: creates a private sharechain. P2P prefix\n";
227227
std::cout << " and THE metadata will carry this ID on the blockchain.\n";
228-
std::cout << " Genesis shares are created automatically when chain is empty.\n\n";
228+
std::cout << " Genesis shares are created automatically when chain is empty.\n";
229+
std::cout << " --startup-mode MODE Sharechain startup behavior:\n";
230+
std::cout << " auto — wait for peers (60s), then genesis if none (default)\n";
231+
std::cout << " genesis — create new chain immediately, don't wait for peers\n";
232+
std::cout << " wait — never create genesis, wait indefinitely for peers\n";
233+
std::cout << " --startup-timeout N Seconds to wait for peers in auto mode (default: 60)\n\n";
229234

230235
std::cout << "V36 SHARE MESSAGE BLOB (CLI operator control):\n";
231236
std::cout << " --message-blob-hex HEX Encrypted authority-signed message_data blob\n";
@@ -402,6 +407,11 @@ int main(int argc, char* argv[]) {
402407
// Private sharechain
403408
uint32_t network_id = 0; // 0 = public p2pool network, nonzero = private
404409

410+
// Startup mode: auto (default), genesis, wait
411+
enum class StartupMode { AUTO, GENESIS, WAIT };
412+
StartupMode startup_mode = StartupMode::AUTO;
413+
int startup_timeout = 60; // seconds to wait for peers in auto mode
414+
405415
// Track which options were explicitly set via CLI so that --config file
406416
// values only fill in gaps (CLI always wins).
407417
std::set<std::string> cli_explicit;
@@ -663,6 +673,25 @@ int main(int argc, char* argv[]) {
663673
network_id = static_cast<uint32_t>(std::stoul(argv[++i], nullptr, 16));
664674
cli_explicit.insert("network_id");
665675
}
676+
else if (arg == "--startup-mode" && i + 1 < argc) {
677+
std::string mode = argv[++i];
678+
if (mode == "genesis") startup_mode = StartupMode::GENESIS;
679+
else if (mode == "wait") startup_mode = StartupMode::WAIT;
680+
else startup_mode = StartupMode::AUTO;
681+
cli_explicit.insert("startup_mode");
682+
}
683+
else if (arg == "--genesis") {
684+
startup_mode = StartupMode::GENESIS;
685+
cli_explicit.insert("startup_mode");
686+
}
687+
else if (arg == "--wait-for-peers") {
688+
startup_mode = StartupMode::WAIT;
689+
cli_explicit.insert("startup_mode");
690+
}
691+
else if (arg == "--startup-timeout" && i + 1 < argc) {
692+
startup_timeout = std::stoi(argv[++i]);
693+
cli_explicit.insert("startup_timeout");
694+
}
666695
// Legacy support for old --port option
667696
else if (arg == "--port" && i + 1 < argc) {
668697
p2p_port = std::stoi(argv[++i]);
@@ -2639,6 +2668,69 @@ int main(int argc, char* argv[]) {
26392668
LOG_INFO << " ✓ Real-time hashrate tracking";
26402669
LOG_INFO << " ✓ Persistent storage";
26412670

2671+
// ── Startup mode: genesis / auto / wait ──────────────────────────
2672+
// Determines behavior when sharechain is empty (no shares from
2673+
// LevelDB or peers). Logged clearly so operator knows what's happening.
2674+
{
2675+
auto best = p2p_node->best_share_hash();
2676+
bool chain_empty = best.IsNull();
2677+
const char* mode_str = startup_mode == StartupMode::GENESIS ? "genesis"
2678+
: startup_mode == StartupMode::WAIT ? "wait" : "auto";
2679+
2680+
if (chain_empty) {
2681+
LOG_INFO << "[Sharechain] No shares loaded from storage";
2682+
LOG_INFO << "[Sharechain] Startup mode: " << mode_str;
2683+
2684+
if (startup_mode == StartupMode::GENESIS) {
2685+
LOG_INFO << "[Sharechain] GENESIS MODE — creating new chain immediately";
2686+
LOG_INFO << "[Sharechain] First share will have previous_share_hash=null";
2687+
if (network_id != 0)
2688+
LOG_INFO << "[Sharechain] Private chain network_id="
2689+
<< std::hex << network_id << std::dec;
2690+
}
2691+
else if (startup_mode == StartupMode::WAIT) {
2692+
LOG_INFO << "[Sharechain] WAIT MODE — waiting indefinitely for peers with shares";
2693+
LOG_INFO << "[Sharechain] Will NOT create genesis. Use --genesis to override.";
2694+
// Pump ioc until shares arrive or shutdown
2695+
while (!g_shutdown_requested) {
2696+
ioc.restart();
2697+
ioc.run_for(std::chrono::seconds(1));
2698+
best = p2p_node->best_share_hash();
2699+
if (!best.IsNull()) {
2700+
LOG_INFO << "[Sharechain] Received shares from peer! best="
2701+
<< best.GetHex().substr(0, 16) << "...";
2702+
break;
2703+
}
2704+
}
2705+
}
2706+
else {
2707+
// AUTO mode: wait startup_timeout seconds for peers
2708+
LOG_INFO << "[Sharechain] AUTO MODE — waiting " << startup_timeout
2709+
<< "s for peers, then genesis if none";
2710+
auto deadline = std::chrono::steady_clock::now()
2711+
+ std::chrono::seconds(startup_timeout);
2712+
while (!g_shutdown_requested
2713+
&& std::chrono::steady_clock::now() < deadline) {
2714+
ioc.restart();
2715+
ioc.run_for(std::chrono::seconds(1));
2716+
best = p2p_node->best_share_hash();
2717+
if (!best.IsNull()) {
2718+
LOG_INFO << "[Sharechain] Received shares from peer! best="
2719+
<< best.GetHex().substr(0, 16) << "...";
2720+
break;
2721+
}
2722+
}
2723+
if (best.IsNull() && !g_shutdown_requested) {
2724+
LOG_INFO << "[Sharechain] No peers found after "
2725+
<< startup_timeout << "s — entering GENESIS MODE";
2726+
}
2727+
}
2728+
} else {
2729+
LOG_INFO << "[Sharechain] Shares available, best="
2730+
<< best.GetHex().substr(0, 16) << "...";
2731+
}
2732+
}
2733+
26422734
// Work guard prevents ioc from draining when all async handlers
26432735
// complete (e.g., all peers disconnect). Timers and accept loops
26442736
// continue firing on schedule. Matches Litecoin Core's approach

ui/c2pool-qt/src/PageLaunch.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,20 @@ void PageLaunch::setupUi()
377377
privateStatusLabel_->setStyleSheet("color: green; font-weight: bold;");
378378
form->addRow("Status:", privateStatusLabel_);
379379

380+
// Startup mode
381+
startupModeCombo_ = new QComboBox;
382+
startupModeCombo_->addItem("Auto (wait 60s, then genesis)", "auto");
383+
startupModeCombo_->addItem("Genesis (new chain immediately)", "genesis");
384+
startupModeCombo_->addItem("Wait for peers (never genesis)", "wait");
385+
startupModeCombo_->setToolTip(
386+
"--startup-mode\n\n"
387+
"auto: Wait for peers (60s timeout), create genesis if none found\n"
388+
"genesis: Create new chain immediately, don't wait for peers\n"
389+
"wait: Never create genesis, wait indefinitely for peers to sync");
390+
connect(startupModeCombo_, QOverload<int>::of(&QComboBox::currentIndexChanged),
391+
this, [this](int) { updateCommandPreview(); });
392+
form->addRow("Startup mode:", startupModeCombo_);
393+
380394
connect(privateChainCheck_, &QCheckBox::stateChanged, this, [this](int state) {
381395
bool enabled = (state == Qt::Checked);
382396
networkIdEdit_->setEnabled(enabled);
@@ -590,6 +604,13 @@ QString PageLaunch::buildCommand() const
590604
parts << "--network-id" << nid;
591605
}
592606

607+
// Startup mode
608+
const QString smode = startupModeCombo_->currentData().toString();
609+
if (smode == "genesis")
610+
parts << "--genesis";
611+
else if (smode == "wait")
612+
parts << "--wait-for-peers";
613+
593614
return parts.join(" ");
594615
}
595616

ui/c2pool-qt/src/PageLaunch.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ private slots:
117117
QLineEdit* networkIdEdit_; ///< --network-id (hex)
118118
QPushButton* generateIdBtn_; ///< Generate random network ID
119119
QLabel* privateStatusLabel_; ///< "Public network" / "Private chain"
120+
QComboBox* startupModeCombo_; ///< auto / genesis / wait
120121

121122
// ── Advanced ────────────────────────────────────────────────────────────
122123
QLineEdit* configFileEdit_; ///< --config (YAML file)

0 commit comments

Comments
 (0)