Gate block template on protocol_tx RPC
Wait for getblocktemplate RPC response before delivering templates to miners, ensuring the protocol_tx hash is correct in the PoW merkle root. All error paths fall back to empty protocol_tx so mining is never permanently stalled. Also bump ver to 4.16 and include upstream whitespace fix in tcp_server
This commit is contained in:
+49
-25
@@ -1346,15 +1346,18 @@ void p2pool::update_block_template()
|
||||
if (m_updateSeed.exchange(false)) {
|
||||
m_hasher->set_seed_async(data.seed_hash);
|
||||
}
|
||||
|
||||
// If we don't have the real protocol_tx yet, fetch it first.
|
||||
// The RPC callback will trigger update_block_template again once it has the data.
|
||||
if (!data.protocol_tx_loaded) {
|
||||
fetch_block_template();
|
||||
return;
|
||||
}
|
||||
|
||||
m_blockTemplate->update(data, *m_mempool, m_params, in_donation_mode(data.height));
|
||||
stratum_on_block();
|
||||
api_update_pool_stats();
|
||||
|
||||
// Fetch real protocol_tx from daemon if we don't have it yet for this height
|
||||
if (!data.protocol_tx_loaded) {
|
||||
fetch_block_template();
|
||||
}
|
||||
|
||||
#if defined(WITH_RANDOMX) && !defined(P2POOL_UNIT_TESTS)
|
||||
if (m_isAlternativeBlock.exchange(false)) {
|
||||
MutexLock lock(m_minerLock);
|
||||
@@ -2154,11 +2157,18 @@ void p2pool::fetch_block_template()
|
||||
{
|
||||
parse_block_template_rpc(data, size, height);
|
||||
},
|
||||
[](const char* data, size_t size, double)
|
||||
[this](const char* data, size_t size, double)
|
||||
{
|
||||
if (size > 0) {
|
||||
LOGWARN(1, "getblocktemplate RPC request failed: " << log::const_buf(data, size));
|
||||
}
|
||||
// RPC failed - fall back to building template without real protocol_tx
|
||||
// Mark as loaded (with empty blob) so update_block_template proceeds
|
||||
{
|
||||
WriteLock lock(m_minerDataLock);
|
||||
m_minerData.protocol_tx_loaded = true;
|
||||
}
|
||||
update_block_template_async();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2166,11 +2176,21 @@ void p2pool::parse_block_template_rpc(const char* data, size_t size, uint64_t ex
|
||||
{
|
||||
if (m_stopped) return;
|
||||
|
||||
// On any failure, fall back to empty protocol_tx and proceed with template build
|
||||
auto fallback = [this]() {
|
||||
{
|
||||
WriteLock lock(m_minerDataLock);
|
||||
m_minerData.protocol_tx_loaded = true;
|
||||
}
|
||||
update_block_template_async();
|
||||
};
|
||||
|
||||
rapidjson::Document doc;
|
||||
doc.Parse(data, size);
|
||||
|
||||
if (doc.HasParseError() || !doc.IsObject()) {
|
||||
LOGWARN(1, "getblocktemplate RPC response is not valid JSON");
|
||||
fallback();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2181,6 +2201,7 @@ void p2pool::parse_block_template_rpc(const char* data, size_t size, uint64_t ex
|
||||
} else {
|
||||
LOGWARN(1, "getblocktemplate RPC response has no result");
|
||||
}
|
||||
fallback();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2189,18 +2210,21 @@ void p2pool::parse_block_template_rpc(const char* data, size_t size, uint64_t ex
|
||||
auto it_blob = result.FindMember("blocktemplate_blob");
|
||||
if (it_blob == result.MemberEnd() || !it_blob->value.IsString()) {
|
||||
LOGWARN(1, "getblocktemplate RPC response missing blocktemplate_blob");
|
||||
fallback();
|
||||
return;
|
||||
}
|
||||
|
||||
auto it_height = result.FindMember("height");
|
||||
if (it_height == result.MemberEnd() || !it_height->value.IsUint64()) {
|
||||
LOGWARN(1, "getblocktemplate RPC response missing height");
|
||||
fallback();
|
||||
return;
|
||||
}
|
||||
|
||||
const uint64_t height = it_height->value.GetUint64();
|
||||
if (height != expected_height) {
|
||||
LOGINFO(5, "getblocktemplate height " << height << " doesn't match expected " << expected_height << ", ignoring");
|
||||
fallback();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2210,6 +2234,7 @@ void p2pool::parse_block_template_rpc(const char* data, size_t size, uint64_t ex
|
||||
std::vector<uint8_t> blob;
|
||||
if (!from_hex(hex_str, hex_len, blob)) {
|
||||
LOGWARN(1, "getblocktemplate: failed to decode blocktemplate_blob hex");
|
||||
fallback();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2217,36 +2242,35 @@ void p2pool::parse_block_template_rpc(const char* data, size_t size, uint64_t ex
|
||||
std::vector<uint8_t> protocol_tx_blob;
|
||||
if (!extract_protocol_tx_from_blob(blob.data(), blob.size(), protocol_tx_blob)) {
|
||||
LOGWARN(1, "getblocktemplate: failed to extract protocol_tx from blob");
|
||||
fallback();
|
||||
return;
|
||||
}
|
||||
|
||||
// Count vout in the protocol_tx to determine if it has outputs
|
||||
// The protocol_tx starts with: version(varint) + unlock_time(varint) + vin_count(varint) + vin_data + vout_count(varint)
|
||||
// We need to read vout_count to know if there are outputs
|
||||
// Count vout in the protocol_tx
|
||||
const uint8_t* p = protocol_tx_blob.data();
|
||||
const uint8_t* p_end = p + protocol_tx_blob.size();
|
||||
uint64_t dummy;
|
||||
p = readVarint(p, p_end, dummy); // version
|
||||
if (!p) return;
|
||||
if (!p) { fallback(); return; }
|
||||
p = readVarint(p, p_end, dummy); // unlock_time
|
||||
if (!p) return;
|
||||
if (!p) { fallback(); return; }
|
||||
uint64_t vin_count;
|
||||
p = readVarint(p, p_end, vin_count); // vin count
|
||||
if (!p) return;
|
||||
// Skip vin entries
|
||||
p = readVarint(p, p_end, vin_count);
|
||||
if (!p) { fallback(); return; }
|
||||
for (uint64_t i = 0; i < vin_count; ++i) {
|
||||
if (p >= p_end) return;
|
||||
if (p >= p_end) { fallback(); return; }
|
||||
uint8_t tag = *(p++);
|
||||
if (tag == 0xff) {
|
||||
p = readVarint(p, p_end, dummy); // txin_gen height
|
||||
if (!p) return;
|
||||
p = readVarint(p, p_end, dummy);
|
||||
if (!p) { fallback(); return; }
|
||||
} else {
|
||||
return; // Unexpected vin type in protocol_tx
|
||||
fallback();
|
||||
return;
|
||||
}
|
||||
}
|
||||
uint64_t vout_count;
|
||||
p = readVarint(p, p_end, vout_count);
|
||||
if (!p) return;
|
||||
if (!p) { fallback(); return; }
|
||||
|
||||
// Compute protocol_tx hash
|
||||
hash protocol_tx_hash;
|
||||
@@ -2254,7 +2278,11 @@ void p2pool::parse_block_template_rpc(const char* data, size_t size, uint64_t ex
|
||||
|
||||
LOGINFO(4, "getblocktemplate: protocol_tx for height " << height << " has " << vout_count << " outputs, hash " << protocol_tx_hash);
|
||||
|
||||
// Check if current miner data is still for this height
|
||||
if (vout_count > 0) {
|
||||
LOGINFO(2, "Protocol TX for height " << height << " has " << vout_count << " outputs");
|
||||
}
|
||||
|
||||
// Store protocol_tx and trigger template build
|
||||
{
|
||||
WriteLock lock(m_minerDataLock);
|
||||
if (m_minerData.height != height) {
|
||||
@@ -2266,11 +2294,7 @@ void p2pool::parse_block_template_rpc(const char* data, size_t size, uint64_t ex
|
||||
m_minerData.protocol_tx_loaded = true;
|
||||
}
|
||||
|
||||
// If protocol_tx has outputs, trigger a template update so miners get the correct block
|
||||
if (vout_count > 0) {
|
||||
LOGINFO(2, "Protocol TX for height " << height << " has " << vout_count << " outputs, updating block template");
|
||||
update_block_template_async();
|
||||
}
|
||||
update_block_template_async();
|
||||
}
|
||||
|
||||
bool p2pool::parse_block_header(const char* data, size_t size, ChainMain& c)
|
||||
|
||||
+4
-4
@@ -319,9 +319,9 @@ bool TCPServer::connect_to_peer(bool is_v6, const raw_ip& ip, int port)
|
||||
bool TCPServer::connect_to_peer(const std::string& domain, int port)
|
||||
{
|
||||
if (m_socks5Proxy.empty()) {
|
||||
LOGERR(1, "Can't connect to " << domain << ": SOCKS5 proxy is required");
|
||||
return false;
|
||||
}
|
||||
LOGERR(1, "Can't connect to " << domain << ": SOCKS5 proxy is required");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_finished.load()) {
|
||||
return false;
|
||||
@@ -337,7 +337,7 @@ bool TCPServer::connect_to_peer(const std::string& domain, int port)
|
||||
s << domain << ':' << port << '\0';
|
||||
|
||||
if (s.m_spilled) {
|
||||
LOGERR(1, "Can't connect to " << domain << ": too long domain name");
|
||||
LOGERR(1, "Can't connect to " << domain << ": too long domain name");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@
|
||||
namespace p2pool {
|
||||
|
||||
#define P2POOL_VERSION_MAJOR 4
|
||||
#define P2POOL_VERSION_MINOR 15
|
||||
#define P2POOL_VERSION_MINOR 16
|
||||
#define P2POOL_VERSION_PATCH 0
|
||||
|
||||
constexpr uint32_t P2POOL_VERSION = (P2POOL_VERSION_MAJOR << 16) | (P2POOL_VERSION_MINOR << 8) | P2POOL_VERSION_PATCH;
|
||||
|
||||
Reference in New Issue
Block a user