Skip to content

Commit 0b15ffe

Browse files
authored
Merge pull request #105 from frstrtr/btc/root2-genesis-null-advert
btc(v36): advertise raw head at handshake + re-advertise on best-change (root-2)
2 parents 68c993f + 659b95c commit 0b15ffe

2 files changed

Lines changed: 66 additions & 2 deletions

File tree

src/impl/btc/node.cpp

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ void NodeImpl::send_version(peer_ptr peer)
256256
m_nonce,
257257
m_software_version,
258258
1, // mode (always 1 for legacy compat)
259-
best_share_hash() // advertise our tallest chain head
259+
advertised_best_share() // verified head, or raw head pre-sync (ROOT-2)
260260
);
261261
peer->write(std::move(rmsg));
262262
}
@@ -901,6 +901,49 @@ uint256 NodeImpl::best_share_hash()
901901
return best;
902902
}
903903

904+
// Head advertised to peers in the version handshake and re-advertisement ONLY.
905+
// Unlike best_share_hash() -- which is verified-only and collapses to ZERO once
906+
// peers exist so work/template never builds on a MAX_TARGET head -- this returns
907+
// our tallest RAW chain head. Root-2: on a fresh (--genesis) node the verified
908+
// set is empty at handshake, so best_share_hash() advertised ZERO and the peer
909+
// never issued download_shares() for our shares. Advertising the raw head makes
910+
// a connecting peer always learn we have a chain and pull it, while work/template
911+
// stays on the verified head via best_share_hash().
912+
uint256 NodeImpl::advertised_best_share()
913+
{
914+
// Prefer think()s current best (verified or not) -- our canonical head.
915+
if (!m_best_share_hash.IsNull())
916+
return m_best_share_hash;
917+
918+
// Otherwise the tallest raw chain head (mirrors the genesis fallback above).
919+
if (m_chain && m_chain->size() > 0) {
920+
uint256 best;
921+
int32_t best_height = -1;
922+
for (const auto& [head_hash, tail_hash] : m_chain->get_heads()) {
923+
auto h = m_chain->get_height(head_hash);
924+
if (h > best_height) {
925+
best = head_hash;
926+
best_height = h;
927+
}
928+
}
929+
if (!best.IsNull())
930+
return best;
931+
}
932+
933+
// True genesis: no shares at all yet -- advertise ZERO exactly as before.
934+
return uint256::ZERO;
935+
}
936+
937+
void NodeImpl::readvertise_best()
938+
{
939+
if (m_peers.empty())
940+
return;
941+
uint256 adv = advertised_best_share();
942+
if (adv.IsNull() || adv == uint256::ZERO)
943+
return;
944+
broadcast_share(adv);
945+
}
946+
904947
void NodeImpl::download_shares(peer_ptr /*unused_peer*/, const uint256& target_hash)
905948
{
906949
// p2pool node.py:108-141 download_shares() — exact translation.
@@ -1571,6 +1614,10 @@ void NodeImpl::run_think()
15711614
auto wr_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
15721615
std::chrono::steady_clock::now() - wr_t0).count();
15731616
LOG_INFO << "[ASYNC-THINK] IO-phase: work refresh done in " << wr_ms << "ms";
1617+
// Root-2: re-advertise our new head. A peer that handshook
1618+
// before we had a chain saw a ZERO advert and never issued
1619+
// download_shares -- re-announce so it pulls our shares now.
1620+
readvertise_best();
15741621
} else if (result.best.IsNull()) {
15751622
LOG_WARNING << "[ASYNC-THINK] IO-phase: result.best is NULL — verified_tails="
15761623
<< m_tracker.verified.get_tails().size()
@@ -2080,6 +2127,7 @@ void NodeImpl::clean_tracker()
20802127
if (clean_best_changed && m_on_best_share_changed) {
20812128
LOG_INFO << "[CLEAN] IO-phase: work refresh (best changed)";
20822129
m_on_best_share_changed();
2130+
readvertise_best(); // root-2: re-announce new head to peers
20832131
}
20842132
drain_pending_adds();
20852133
m_think_running.store(false);

src/impl/btc/node.hpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,13 +323,28 @@ class NodeImpl : public pool::BaseNode<btc::Config, btc::ShareChain, btc::Peer>
323323
/// Broadcast a locally-generated (or newly-received) share to all peers.
324324
void broadcast_share(const uint256& share_hash);
325325

326+
/// Re-announce our current advertised head (advertised_best_share()) to all
327+
/// connected peers. Invoked when think()/clean updates the best share so a
328+
/// peer that completed the version handshake before we had any shares (when
329+
/// our advert was ZERO) still learns our head and pulls via download_shares.
330+
void readvertise_best();
331+
326332
/// Start downloading shares from a peer, beginning at `target_hash`.
327333
/// Recursively fetches parents until the chain is connected or CHAIN_LENGTH reached.
328334
void download_shares(peer_ptr peer, const uint256& target_hash);
329335

330-
/// Return the hash of our tallest chain head, or uint256::ZERO if empty.
336+
/// Return the best VERIFIED share head for work/template selection, or
337+
/// uint256::ZERO when no verified chain exists yet and peers are connected
338+
/// (so work never builds on a MAX_TARGET head). NOT for the wire advert —
339+
/// use advertised_best_share() there.
331340
uint256 best_share_hash();
332341

342+
/// Head advertised to peers (version handshake + re-advertisement ONLY).
343+
/// Returns our tallest RAW chain head so a connecting peer always learns we
344+
/// have a chain and pulls it; unlike best_share_hash() it does not collapse
345+
/// to ZERO once peers exist. Root-2 genesis-null-advert fix.
346+
uint256 advertised_best_share();
347+
333348
/// Load persisted shares from LevelDB storage into the tracker.
334349
void load_persisted_shares();
335350
void flush_verified_to_leveldb();
@@ -537,6 +552,7 @@ class NodeImpl : public pool::BaseNode<btc::Config, btc::ShareChain, btc::Peer>
537552
// Cached best share hash from the most recent think() cycle
538553
uint256 m_best_share_hash;
539554

555+
540556
// Cache of original raw serialized bytes keyed by share hash.
541557
// Used for relay so we send the exact bytes we received, avoiding
542558
// any round-trip serialization differences.

0 commit comments

Comments
 (0)