Implement batch block transfer with server side ancestor fill, BLOCK_BATCH_MAX_COUNT reduced from 50 to 10 to avoid contention warnings, fix get_seed failure in add_external_block returning false

This commit is contained in:
Matt Hess
2026-02-19 20:30:23 +00:00
parent 867047f897
commit 76d1efcf57
4 changed files with 92 additions and 18 deletions

View File

@@ -3108,7 +3108,10 @@ bool P2PServer::P2PClient::on_block_batch_request(const uint8_t* buf, uint32_t s
// Collect all requested blocks
std::vector<std::vector<uint8_t>> blobs;
blobs.reserve(count);
blobs.reserve(BLOCK_BATCH_MAX_COUNT);
hash last_parent;
size_t total_response_size = 2; // msg id + count byte
const uint8_t* p = buf + 1;
for (uint8_t i = 0; i < count; ++i) {
@@ -3117,15 +3120,41 @@ bool P2PServer::P2PClient::on_block_batch_request(const uint8_t* buf, uint32_t s
p += HASH_SIZE;
std::vector<uint8_t> blob;
const PoolBlock* block = sidechain.get_block_blob(id, blob);
hash parent;
const PoolBlock* block = sidechain.get_block_blob(id, blob, &parent);
if (!block && !id.empty()) {
LOGWARN(6, "batch request: block " << id << " not found");
}
if (block) {
last_parent = parent;
}
total_response_size += sizeof(uint32_t) + blob.size();
blobs.push_back(std::move(blob));
}
// Fill remaining slots with ancestor blocks so the peer can sync a full chain segment per request
while (blobs.size() < BLOCK_BATCH_MAX_COUNT && !last_parent.empty()) {
std::vector<uint8_t> blob;
hash parent;
const PoolBlock* ancestor = sidechain.get_block_blob(last_parent, blob, &parent);
if (!ancestor || blob.empty()) {
break;
}
const size_t entry_size = sizeof(uint32_t) + blob.size();
if (total_response_size + entry_size > P2P_BUF_SIZE) {
break;
}
total_response_size += entry_size;
blobs.push_back(std::move(blob));
last_parent = parent;
}
// Send batch response
return server->send(this,
[this, &blobs](uint8_t* buf, size_t buf_size) -> size_t
@@ -4333,6 +4362,8 @@ void P2PServer::P2PClient::post_handle_incoming_block(p2pool* pool, const PoolBl
ReadLock lock(server->m_cachedBlocksLock);
// Collect non-cached missing block IDs to request
std::vector<hash> to_request;
for (const hash& id : missing_blocks) {
if (server->m_cachedBlocks) {
auto it = server->m_cachedBlocks->find(id);
@@ -4349,30 +4380,68 @@ void P2PServer::P2PClient::post_handle_incoming_block(p2pool* pool, const PoolBl
continue;
}
const bool send_result = server->send(this,
[this, &id](uint8_t* buf, size_t buf_size) -> size_t
{
LOGINFO(5, "[post_handle_incoming_block] sending BLOCK_REQUEST for id = " << id << " to " << static_cast<char*>(m_addrString));
to_request.push_back(id);
}
if (buf_size < 1 + HASH_SIZE) {
if (to_request.empty()) {
return;
}
// Use batch requests for 1.6+ peers so the server can fill in ancestors
if (m_protocolVersion >= PROTOCOL_VERSION_1_6) {
const bool send_result = server->send(this,
[this, &to_request](uint8_t* buf, size_t buf_size) -> size_t
{
LOGINFO(5, "[post_handle_incoming_block] sending BLOCK_BATCH_REQUEST for " << to_request.size() << " block(s) to " << static_cast<char*>(m_addrString));
const size_t needed = 2 + to_request.size() * HASH_SIZE;
if (buf_size < needed) {
return 0;
}
uint8_t* p = buf;
*(p++) = static_cast<uint8_t>(MessageId::BLOCK_BATCH_REQUEST);
*(p++) = static_cast<uint8_t>(to_request.size());
*(p++) = static_cast<uint8_t>(MessageId::BLOCK_REQUEST);
memcpy(p, id.h, HASH_SIZE);
p += HASH_SIZE;
for (const hash& h : to_request) {
memcpy(p, h.h, HASH_SIZE);
p += HASH_SIZE;
}
return p - buf;
});
if (!send_result) {
return;
if (send_result) {
for (const hash& h : to_request) {
m_blockPendingRequests.push_back(*h.u64());
}
}
} else {
// Legacy: individual BLOCK_REQUEST for older peers
for (const hash& id : to_request) {
const bool send_result = server->send(this,
[this, &id](uint8_t* buf, size_t buf_size) -> size_t
{
LOGINFO(5, "[post_handle_incoming_block] sending BLOCK_REQUEST for id = " << id << " to " << static_cast<char*>(m_addrString));
m_blockPendingRequests.push_back(*id.u64());
if (buf_size < 1 + HASH_SIZE) {
return 0;
}
uint8_t* p = buf;
*(p++) = static_cast<uint8_t>(MessageId::BLOCK_REQUEST);
memcpy(p, id.h, HASH_SIZE);
p += HASH_SIZE;
return p - buf;
});
if (!send_result) {
return;
}
m_blockPendingRequests.push_back(*id.u64());
}
}
}

View File

@@ -49,7 +49,7 @@ static constexpr uint32_t PROTOCOL_VERSION_1_6 = 0x00010006UL;
static constexpr uint32_t SUPPORTED_PROTOCOL_VERSION = PROTOCOL_VERSION_1_6;
// Batch block request limits
static constexpr uint8_t BLOCK_BATCH_MAX_COUNT = 50;
static constexpr uint8_t BLOCK_BATCH_MAX_COUNT = 10;
class P2PServer : public TCPServer
{

View File

@@ -653,8 +653,9 @@ bool SideChain::add_external_block(PoolBlock& block, std::vector<hash>& missing_
m_pool->fetch_mainchain_block(seed_height);
}
// Always forget so block can be re-processed when mainchain data arrives
// Return true: missing seed is a local problem, not the peer's fault - don't ban them
forget_incoming_block(block);
return false;
return true;
}
if (!block.get_pow_hash(m_pool->hasher(), block.m_txinGenHeight, block.m_seed, block.m_powHash)) {
@@ -878,7 +879,7 @@ void SideChain::watch_mainchain_block(const ChainMain& data, const hash& possibl
m_watchBlockMerkleRoot = possible_merkle_root;
}
const PoolBlock* SideChain::get_block_blob(const hash& id, std::vector<uint8_t>& blob) const
const PoolBlock* SideChain::get_block_blob(const hash& id, std::vector<uint8_t>& blob, hash* parent_hash_out) const
{
ReadLock lock(m_sidechainLock);
@@ -908,6 +909,10 @@ const PoolBlock* SideChain::get_block_blob(const hash& id, std::vector<uint8_t>&
const std::vector<uint8_t> sidechain_data = block->serialize_sidechain_data();
blob.insert(blob.end(), sidechain_data.begin(), sidechain_data.end());
if (parent_hash_out) {
*parent_hash_out = block->m_parent;
}
return block;
}

View File

@@ -81,7 +81,7 @@ public:
bool get_genesis_info(hash& id, uint64_t& timestamp, uint64_t& height) const;
void purge_sidechain();
[[nodiscard]] const PoolBlock* get_block_blob(const hash& id, std::vector<uint8_t>& blob) const;
[[nodiscard]] const PoolBlock* get_block_blob(const hash& id, std::vector<uint8_t>& blob, hash* parent_hash_out = nullptr) const;
[[nodiscard]] bool get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::vector<uint8_t>& blob, uv_loop_t* loop) const;
void print_status(bool obtain_sidechain_lock = true) const;