From 76d1efcf57194197c8a16d5cd2c8fada1057dd4b Mon Sep 17 00:00:00 2001 From: Matt Hess Date: Thu, 19 Feb 2026 20:30:23 +0000 Subject: [PATCH] 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 --- src/p2p_server.cpp | 97 +++++++++++++++++++++++++++++++++++++++------- src/p2p_server.h | 2 +- src/side_chain.cpp | 9 ++++- src/side_chain.h | 2 +- 4 files changed, 92 insertions(+), 18 deletions(-) diff --git a/src/p2p_server.cpp b/src/p2p_server.cpp index cfd3816..dc2ab39 100644 --- a/src/p2p_server.cpp +++ b/src/p2p_server.cpp @@ -3108,7 +3108,10 @@ bool P2PServer::P2PClient::on_block_batch_request(const uint8_t* buf, uint32_t s // Collect all requested blocks std::vector> 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 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 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 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(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(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(MessageId::BLOCK_BATCH_REQUEST); + *(p++) = static_cast(to_request.size()); - *(p++) = static_cast(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(m_addrString)); - m_blockPendingRequests.push_back(*id.u64()); + if (buf_size < 1 + HASH_SIZE) { + return 0; + } + + uint8_t* p = buf; + *(p++) = static_cast(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()); + } } } diff --git a/src/p2p_server.h b/src/p2p_server.h index 75ea6d8..0f4b533 100644 --- a/src/p2p_server.h +++ b/src/p2p_server.h @@ -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 { diff --git a/src/side_chain.cpp b/src/side_chain.cpp index d55bef9..33f38ef 100644 --- a/src/side_chain.cpp +++ b/src/side_chain.cpp @@ -653,8 +653,9 @@ bool SideChain::add_external_block(PoolBlock& block, std::vector& 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& blob) const +const PoolBlock* SideChain::get_block_blob(const hash& id, std::vector& 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& const std::vector 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; } diff --git a/src/side_chain.h b/src/side_chain.h index 6057559..c0324e8 100644 --- a/src/side_chain.h +++ b/src/side_chain.h @@ -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& blob) const; + [[nodiscard]] const PoolBlock* get_block_blob(const hash& id, std::vector& blob, hash* parent_hash_out = nullptr) const; [[nodiscard]] bool get_outputs_blob(PoolBlock* block, uint64_t total_reward, std::vector& blob, uv_loop_t* loop) const; void print_status(bool obtain_sidechain_lock = true) const;