From 940f1cabe8b847cd3b8f7cba133fa19732b37dcc Mon Sep 17 00:00:00 2001 From: Matt Hess Date: Thu, 11 Dec 2025 02:26:30 +0000 Subject: [PATCH] Revert "Remove compact/pruned blob code - it displeases the carrot" This reverts commit d5a8bbcc4ac3fa5ce3b96560001d0f37b1f3b01c. --- src/block_cache.cpp | 2 +- src/block_template.cpp | 4 +- src/p2p_server.cpp | 271 +++++++++++++++++++++++++++----------- src/p2p_server.h | 11 +- src/pool_block.h | 2 +- src/pool_block_parser.inl | 75 +++++++++-- src/side_chain.cpp | 4 +- 7 files changed, 271 insertions(+), 98 deletions(-) diff --git a/src/block_cache.cpp b/src/block_cache.cpp index 89aaf08..84868ec 100644 --- a/src/block_cache.cpp +++ b/src/block_cache.cpp @@ -234,7 +234,7 @@ void BlockCache::load_all(const SideChain& side_chain, P2PServer& server) continue; } - if (block.deserialize(data + sizeof(uint32_t), n, side_chain, uv_default_loop_checked()) == 0) { + if (block.deserialize(data + sizeof(uint32_t), n, side_chain, uv_default_loop_checked(), false) == 0) { server.add_cached_block(block); ++blocks_loaded; } diff --git a/src/block_template.cpp b/src/block_template.cpp index 4db746c..949f475 100644 --- a/src/block_template.cpp +++ b/src/block_template.cpp @@ -810,7 +810,7 @@ void BlockTemplate::update(const MinerData& data, const Mempool& mempool, const } } PoolBlock check; - const int result = check.deserialize(m_fullDataBlob.data(), m_fullDataBlob.size(), *m_sidechain, nullptr); + const int result = check.deserialize(m_fullDataBlob.data(), m_fullDataBlob.size(), *m_sidechain, nullptr, false); if (result != 0) { LOGERR(1, "pool block blob generation and/or parsing is broken, error " << result); } @@ -1802,7 +1802,7 @@ bool BlockTemplate::submit_sidechain_block(uint32_t template_id, uint32_t nonce, buf.insert(buf.end(), sidechain_data.begin(), sidechain_data.end()); PoolBlock check; - const int result = check.deserialize(buf.data(), buf.size(), *m_sidechain, nullptr); + const int result = check.deserialize(buf.data(), buf.size(), *m_sidechain, nullptr, false); if (result != 0) { LOGERR(1, "pool block blob generation and/or parsing is broken, error " << result); } diff --git a/src/p2p_server.cpp b/src/p2p_server.cpp index 2619c54..7be444e 100644 --- a/src/p2p_server.cpp +++ b/src/p2p_server.cpp @@ -961,24 +961,85 @@ void P2PServer::Peer::normalize() } } -P2PServer::Broadcast::Broadcast(const PoolBlock& block) - : id(block.m_sidechainId) - , received_timestamp(block.m_receivedTimestamp) +P2PServer::Broadcast::Broadcast(const PoolBlock& block, const PoolBlock* parent) + : id(block.m_sidechainId) + , received_timestamp(block.m_receivedTimestamp) { - Broadcast* data = this; - int outputs_offset, outputs_blob_size; - const std::vector mainchain_data = block.serialize_mainchain_data(nullptr, nullptr, &outputs_offset, &outputs_blob_size); - const std::vector sidechain_data = block.serialize_sidechain_data(); - data->blob.reserve(mainchain_data.size() + sidechain_data.size()); - data->blob = mainchain_data; - data->blob.insert(data->blob.end(), sidechain_data.begin(), sidechain_data.end()); + Broadcast* data = this; - data->ancestor_hashes.reserve(block.m_uncles.size() + 1); - data->ancestor_hashes = block.m_uncles; - data->ancestor_hashes.push_back(block.m_parent); + int outputs_offset, outputs_blob_size; + const std::vector mainchain_data = block.serialize_mainchain_data(nullptr, nullptr, &outputs_offset, &outputs_blob_size); + const std::vector sidechain_data = block.serialize_sidechain_data(); + + data->blob.reserve(mainchain_data.size() + sidechain_data.size()); + data->blob = mainchain_data; + data->blob.insert(data->blob.end(), sidechain_data.begin(), sidechain_data.end()); + + data->pruned_blob.reserve(mainchain_data.size() + sidechain_data.size() + 16 - outputs_blob_size); + data->pruned_blob.assign(mainchain_data.begin(), mainchain_data.begin() + outputs_offset); + + // 0 outputs in the pruned blob + data->pruned_blob.push_back(0); + + const uint64_t total_reward = std::accumulate(block.m_outputAmounts.begin(), block.m_outputAmounts.end(), 0ULL, + [](uint64_t a, const PoolBlock::TxOutput& b) + { + return a + b.m_reward; + }); + + writeVarint(total_reward, data->pruned_blob); + writeVarint(outputs_blob_size, data->pruned_blob); + + data->pruned_blob.insert(data->pruned_blob.end(), block.m_sidechainId.h, block.m_sidechainId.h + HASH_SIZE); + data->pruned_blob.insert(data->pruned_blob.end(), mainchain_data.begin() + outputs_offset + outputs_blob_size, mainchain_data.end()); + + const size_t N = block.m_transactions.size(); + if ((N > 1) && parent && (parent->m_transactions.size() > 1)) { + unordered_map parent_transactions; + parent_transactions.reserve(parent->m_transactions.size()); + + for (size_t i = 1; i < parent->m_transactions.size(); ++i) { + parent_transactions.emplace(parent->m_transactions[i], i); + } + + // Reserve 1 additional byte per transaction to be ready for the worst case (all transactions are different in the parent block) + data->compact_blob.reserve(data->pruned_blob.capacity() + (N - 1)); + + // Copy pruned_blob without the transaction list + data->compact_blob.assign(data->pruned_blob.begin(), data->pruned_blob.end() - static_cast((N - 1) * HASH_SIZE)); + + // Process transaction hashes one by one + size_t num_found = 0; + for (size_t i = 1; i < N; ++i) { + const hash& tx = block.m_transactions[i]; + auto it = parent_transactions.find(tx); + if (it != parent_transactions.end()) { + writeVarint(it->second, data->compact_blob); + ++num_found; + } + else { + data->compact_blob.push_back(0); + data->compact_blob.insert(data->compact_blob.end(), tx.h, tx.h + HASH_SIZE); + } + } + LOGINFO(6, "compact blob: " << num_found << '/' << (N - 1) << " transactions were found in the parent block"); + + data->compact_blob.insert(data->compact_blob.end(), sidechain_data.begin(), sidechain_data.end()); + } + + data->pruned_blob.insert(data->pruned_blob.end(), sidechain_data.begin(), sidechain_data.end()); + + // Carrot v1 blocks have encrypted anchors that can't be deterministically reconstructed + // Force full blob broadcasts by clearing pruned/compact blobs + data->pruned_blob.clear(); + data->compact_blob.clear(); + + data->ancestor_hashes.reserve(block.m_uncles.size() + 1); + data->ancestor_hashes = block.m_uncles; + data->ancestor_hashes.push_back(block.m_parent); } -void P2PServer::broadcast(const PoolBlock& block) +void P2PServer::broadcast(const PoolBlock& block, const PoolBlock* parent) { // Don't broadcast blocks when shutting down if (m_finished.load()) { @@ -997,9 +1058,9 @@ void P2PServer::broadcast(const PoolBlock& block) return; } - Broadcast* data = new Broadcast(block); + Broadcast* data = new Broadcast(block, parent); - LOGINFO(5, "Broadcasting block " << block.m_sidechainId << " (height " << block.m_sidechainHeight << "): " << data->blob.size() << " bytes"); + LOGINFO(5, "Broadcasting block " << block.m_sidechainId << " (height " << block.m_sidechainHeight << "): " << data->compact_blob.size() << '/' << data->pruned_blob.size() << '/' << data->blob.size() << " bytes (compact/pruned/full)"); MutexLock lock(m_broadcastLock); @@ -1036,15 +1097,31 @@ void P2PServer::on_broadcast() return; } - if (pool_block_debug()) { - for (Broadcast* data : broadcast_queue) { - PoolBlock check; - const int result = check.deserialize(data->blob.data(), data->blob.size(), m_pool->side_chain(), nullptr); - if (result != 0) { - LOGERR(1, "full blob broadcast is broken, error " << result); - } - } - } + if (pool_block_debug()) { + for (Broadcast* data : broadcast_queue) { + if (!data->compact_blob.empty()) { + PoolBlock check; + const int result = check.deserialize(data->compact_blob.data(), data->compact_blob.size(), m_pool->side_chain(), nullptr, true); + if (result != 0) { + LOGERR(1, "compact blob broadcast is broken, error " << result); + } + } + { + PoolBlock check; + const int result = check.deserialize(data->pruned_blob.data(), data->pruned_blob.size(), m_pool->side_chain(), nullptr, false); + if (result != 0) { + LOGERR(1, "pruned blob broadcast is broken, error " << result); + } + } + { + PoolBlock check; + const int result = check.deserialize(data->blob.data(), data->blob.size(), m_pool->side_chain(), nullptr, false); + if (result != 0) { + LOGERR(1, "full blob broadcast is broken, error " << result); + } + } + } + } for (P2PClient* client = static_cast(m_connectedClientsList->m_next); client != m_connectedClientsList; client = static_cast(client->m_next)) { if (!client->is_good()) { @@ -1052,43 +1129,80 @@ void P2PServer::on_broadcast() } for (Broadcast* data : broadcast_queue) { - const bool result = send(client, [client, data](uint8_t* buf, size_t buf_size) -> size_t - { - uint8_t* p = buf; + const bool result = send(client, [client, data](uint8_t* buf, size_t buf_size) -> size_t + { + uint8_t* p = buf; - const hash* a = client->m_broadcastedHashes; - const hash* b = client->m_broadcastedHashes + array_size(&P2PClient::m_broadcastedHashes); + const hash* a = client->m_broadcastedHashes; + const hash* b = client->m_broadcastedHashes + array_size(&P2PClient::m_broadcastedHashes); - // If this peer already broadcasted this block to us, just notify - if ((client->m_protocolVersion >= PROTOCOL_VERSION_1_2) && (std::find(a, b, data->id) != b)) { - LOGINFO(6, "sending BLOCK_NOTIFY to " << log::Gray() << static_cast(client->m_addrString)); + // If this peer already broadcasted this block to us, we don't need to broadcast it back, we just need to notify the peer + if ((client->m_protocolVersion >= PROTOCOL_VERSION_1_2) && (std::find(a, b, data->id) != b)) { + LOGINFO(6, "sending BLOCK_NOTIFY to " << log::Gray() << static_cast(client->m_addrString)); - if (buf_size < 1 + HASH_SIZE) { - return 0; - } + if (buf_size < 1 + HASH_SIZE) { + return 0; + } - *(p++) = static_cast(MessageId::BLOCK_NOTIFY); - memcpy(p, data->id.h, HASH_SIZE); - p += HASH_SIZE; + *(p++) = static_cast(MessageId::BLOCK_NOTIFY); - return p - buf; - } + memcpy(p, data->id.h, HASH_SIZE); + p += HASH_SIZE; - // Always send full blob (Carrot v1 requires it) - LOGINFO(5, "sending BLOCK_BROADCAST to " << log::Gray() << static_cast(client->m_addrString)); - const uint32_t len = static_cast(data->blob.size()); - if (buf_size < 1 + sizeof(uint32_t) + len) { - return 0; - } - *(p++) = static_cast(MessageId::BLOCK_BROADCAST); - memcpy(p, &len, sizeof(uint32_t)); - p += sizeof(uint32_t); - if (len) { - memcpy(p, data->blob.data(), len); - p += len; - } - return p - buf; - }); + return p - buf; + } + + bool send_pruned = !data->pruned_blob.empty(); + bool send_compact = (client->m_protocolVersion >= PROTOCOL_VERSION_1_1) && !data->compact_blob.empty() && (data->compact_blob.size() < data->pruned_blob.size()); + + for (const hash& id : data->ancestor_hashes) { + if (std::find(a, b, id) == b) { + send_pruned = false; + send_compact = false; + break; + } + } + + if (send_pruned) { + LOGINFO(6, "sending BLOCK_BROADCAST " << (send_compact ? "(compact)" : "(pruned) ") << " to " << log::Gray() << static_cast(client->m_addrString)); + const std::vector& blob = send_compact ? data->compact_blob : data->pruned_blob; + + const uint32_t len = static_cast(blob.size()); + if (buf_size < 1 + sizeof(uint32_t) + len) { + return 0; + } + + *(p++) = static_cast(send_compact ? MessageId::BLOCK_BROADCAST_COMPACT : MessageId::BLOCK_BROADCAST); + + memcpy(p, &len, sizeof(uint32_t)); + p += sizeof(uint32_t); + + if (len) { + memcpy(p, blob.data(), len); + p += len; + } + } + else { + LOGINFO(5, "sending BLOCK_BROADCAST (full) to " << log::Gray() << static_cast(client->m_addrString)); + + const uint32_t len = static_cast(data->blob.size()); + if (buf_size < 1 + sizeof(uint32_t) + len) { + return 0; + } + + *(p++) = static_cast(MessageId::BLOCK_BROADCAST); + + memcpy(p, &len, sizeof(uint32_t)); + p += sizeof(uint32_t); + + if (len) { + memcpy(p, data->blob.data(), len); + p += len; + } + } + + return p - buf; + }); if (!result) { LOGWARN(5, "failed to broadcast a block to " << static_cast(client->m_addrString) << ", disconnecting"); client->close(); @@ -1176,7 +1290,7 @@ int P2PServer::external_listen_port() const return params.m_p2pExternalPort ? params.m_p2pExternalPort : m_listenPort; } -int P2PServer::deserialize_block(const uint8_t* buf, uint32_t size, uint64_t received_timestamp) +int P2PServer::deserialize_block(const uint8_t* buf, uint32_t size, bool compact, uint64_t received_timestamp) { int result; @@ -1185,7 +1299,7 @@ int P2PServer::deserialize_block(const uint8_t* buf, uint32_t size, uint64_t rec result = m_blockDeserializeResult; } else { - result = m_block->deserialize(buf, size, m_pool->side_chain(), &m_loop); + result = m_block->deserialize(buf, size, m_pool->side_chain(), &m_loop, compact); m_blockDeserializeBuf.assign(buf, buf + size); m_blockDeserializeResult = result; m_lookForMissingBlocks = true; @@ -2031,22 +2145,25 @@ bool P2PServer::P2PClient::on_read(const char* data, uint32_t size) } break; - case MessageId::BLOCK_BROADCAST: - { - LOGINFO(6, "peer " << log::Gray() << static_cast(m_addrString) << log::NoColor() << " sent BLOCK_BROADCAST"); - if (bytes_left >= 1 + sizeof(uint32_t)) { - const uint32_t block_size = read_unaligned(reinterpret_cast(buf + 1)); - if (bytes_left >= 1 + sizeof(uint32_t) + block_size) { - bytes_read = 1 + sizeof(uint32_t) + block_size; - if (!on_block_broadcast(buf + 1 + sizeof(uint32_t), block_size)) { - ban(DEFAULT_BAN_TIME); - server->remove_peer_from_list(this); - return false; - } - } - } - } - break; + case MessageId::BLOCK_BROADCAST: + case MessageId::BLOCK_BROADCAST_COMPACT: + { + const bool compact = (id == MessageId::BLOCK_BROADCAST_COMPACT); + LOGINFO(6, "peer " << log::Gray() << static_cast(m_addrString) << log::NoColor() << " sent " << (compact ? "BLOCK_BROADCAST_COMPACT" : "BLOCK_BROADCAST")); + + if (bytes_left >= 1 + sizeof(uint32_t)) { + const uint32_t block_size = read_unaligned(reinterpret_cast(buf + 1)); + if (bytes_left >= 1 + sizeof(uint32_t) + block_size) { + bytes_read = 1 + sizeof(uint32_t) + block_size; + if (!on_block_broadcast(buf + 1 + sizeof(uint32_t), block_size, compact)) { + ban(DEFAULT_BAN_TIME); + server->remove_peer_from_list(this); + return false; + } + } + } + } + break; case MessageId::PEER_LIST_REQUEST: LOGINFO(6, "peer " << log::Gray() << static_cast(m_addrString) << log::NoColor() << " sent PEER_LIST_REQUEST"); @@ -2625,7 +2742,7 @@ bool P2PServer::P2PClient::on_block_response(const uint8_t* buf, uint32_t size, MutexLock lock(server->m_blockLock); - const int result = server->deserialize_block(buf, size, received_timestamp); + const int result = server->deserialize_block(buf, size, false, received_timestamp); if (result != 0) { LOGWARN(3, "peer " << static_cast(m_addrString) << " sent an invalid block, error " << result); return false; @@ -2661,7 +2778,7 @@ bool P2PServer::P2PClient::on_block_response(const uint8_t* buf, uint32_t size, return handle_incoming_block_async(block, max_time_delta); } -bool P2PServer::P2PClient::on_block_broadcast(const uint8_t* buf, uint32_t size) +bool P2PServer::P2PClient::on_block_broadcast(const uint8_t* buf, uint32_t size, bool compact) { if (!size) { LOGWARN(3, "peer " << static_cast(m_addrString) << " broadcasted an empty block"); @@ -2674,7 +2791,7 @@ bool P2PServer::P2PClient::on_block_broadcast(const uint8_t* buf, uint32_t size) MutexLock lock(server->m_blockLock); - const int result = server->deserialize_block(buf, size, received_timestamp); + const int result = server->deserialize_block(buf, size, compact, received_timestamp); if (result != 0) { LOGWARN(3, "peer " << static_cast(m_addrString) << " sent an invalid block, error " << result); return false; diff --git a/src/p2p_server.h b/src/p2p_server.h index a97d794..618b3f7 100644 --- a/src/p2p_server.h +++ b/src/p2p_server.h @@ -58,6 +58,7 @@ public: BLOCK_BROADCAST, PEER_LIST_REQUEST, PEER_LIST_RESPONSE, + BLOCK_BROADCAST_COMPACT, BLOCK_NOTIFY, // Donation messages are signed by author's private keys to prevent their abuse/misuse. AUX_JOB_DONATION, @@ -120,7 +121,7 @@ public: [[nodiscard]] bool on_listen_port(const uint8_t* buf); [[nodiscard]] bool on_block_request(const uint8_t* buf); [[nodiscard]] bool on_block_response(const uint8_t* buf, uint32_t size, uint64_t expected_id); - [[nodiscard]] bool on_block_broadcast(const uint8_t* buf, uint32_t size); + [[nodiscard]] bool on_block_broadcast(const uint8_t* buf, uint32_t size, bool compact); [[nodiscard]] bool on_peer_list_request(const uint8_t* buf); void on_peer_list_response(const uint8_t* buf); void on_block_notify(const uint8_t* buf); @@ -181,16 +182,18 @@ public: struct Broadcast { - Broadcast(const PoolBlock& block); + Broadcast(const PoolBlock& block, const PoolBlock* parent); hash id; uint64_t received_timestamp; std::vector blob; + std::vector pruned_blob; + std::vector compact_blob; std::vector ancestor_hashes; }; - void broadcast(const PoolBlock& block); + void broadcast(const PoolBlock& block, const PoolBlock* parent); [[nodiscard]] uint64_t get_random64(); [[nodiscard]] uint64_t get_peerId(bool is_tor) const { return is_tor ? m_peerId_TOR : m_peerId; } @@ -206,7 +209,7 @@ public: void set_max_outgoing_peers(uint32_t n) { m_maxOutgoingPeers = std::min(std::max(n, 10U), 450U); } void set_max_incoming_peers(uint32_t n) { m_maxIncomingPeers = std::min(std::max(n, 10U), 450U); } - [[nodiscard]] int deserialize_block(const uint8_t* buf, uint32_t size, uint64_t received_timestamp); + [[nodiscard]] int deserialize_block(const uint8_t* buf, uint32_t size, bool compact, uint64_t received_timestamp); [[nodiscard]] const PoolBlock* get_block() const { return m_block; } [[nodiscard]] const PoolBlock* find_block(const hash& id) const; diff --git a/src/pool_block.h b/src/pool_block.h index 143e722..76b0a1a 100644 --- a/src/pool_block.h +++ b/src/pool_block.h @@ -198,7 +198,7 @@ struct PoolBlock std::vector serialize_mainchain_data(size_t* header_size = nullptr, size_t* miner_tx_size = nullptr, int* outputs_offset = nullptr, int* outputs_blob_size = nullptr, const uint32_t* nonce = nullptr, const uint32_t* extra_nonce = nullptr, bool include_tx_hashes = true) const; std::vector serialize_sidechain_data() const; - [[nodiscard]] int deserialize(const uint8_t* data, size_t size, const SideChain& sidechain, uv_loop_t* loop); + [[nodiscard]] int deserialize(const uint8_t* data, size_t size, const SideChain& sidechain, uv_loop_t* loop, bool compact); void reset_offchain_data(); bool get_pow_hash(RandomX_Hasher_Base* hasher, uint64_t height, const hash& seed_hash, hash& pow_hash, bool force_light_mode = false); diff --git a/src/pool_block_parser.inl b/src/pool_block_parser.inl index 3c50fef..2a518be 100644 --- a/src/pool_block_parser.inl +++ b/src/pool_block_parser.inl @@ -23,7 +23,7 @@ namespace p2pool { // Since data here can come from external and possibly malicious sources, check everything // Only the syntax (i.e. the serialized block binary format) and the keccak hash are checked here // Semantics must also be checked elsewhere before accepting the block (PoW, reward split between miners, difficulty calculation and so on) -int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& sidechain, uv_loop_t* loop) +int PoolBlock::deserialize(const uint8_t* data, size_t size, const SideChain& sidechain, uv_loop_t* loop, bool compact) { try { // Sanity check @@ -353,15 +353,44 @@ skip_protocol_tx: const int transactions_offset = static_cast(data - data_begin); + std::vector parent_indices; std::vector transactions; - if (num_transactions > std::numeric_limits::max() / HASH_SIZE) return __LINE__; - if (static_cast(data_end - data) < num_transactions * HASH_SIZE) return __LINE__; - transactions.reserve(num_transactions + 1); - transactions.resize(1); - for (uint64_t i = 0; i < num_transactions; ++i) { - hash id; - READ_BUF(id.h, HASH_SIZE); - transactions.emplace_back(id); + if (compact) { + if (static_cast(data_end - data) < num_transactions) return __LINE__; + + // limit reserved memory size because we can't check "num_transactions" properly here + const uint64_t k = std::min(num_transactions + 1, 256); + transactions.reserve(k); + parent_indices.reserve(k); + + transactions.resize(1); + parent_indices.resize(1); + + for (uint64_t i = 0; i < num_transactions; ++i) { + uint64_t parent_index; + READ_VARINT(parent_index); + + hash id; + if (parent_index == 0) { + READ_BUF(id.h, HASH_SIZE); + } + + transactions.emplace_back(id); + parent_indices.emplace_back(parent_index); + } + } + else { + if (num_transactions > std::numeric_limits::max() / HASH_SIZE) return __LINE__; + if (static_cast(data_end - data) < num_transactions * HASH_SIZE) return __LINE__; + + transactions.reserve(num_transactions + 1); + transactions.resize(1); + + for (uint64_t i = 0; i < num_transactions; ++i) { + hash id; + READ_BUF(id.h, HASH_SIZE); + transactions.emplace_back(id); + } } const int transactions_actual_blob_size = static_cast(data - data_begin) - transactions_offset; @@ -409,8 +438,32 @@ skip_protocol_tx: m_transactions.reserve(transactions.size() + 1); m_transactions.emplace_back(hash{}); - for (size_t i = 1; i < transactions.size(); ++i) { - m_transactions.emplace_back(transactions[i]); + if (compact) { + const PoolBlock* parent = sidechain.find_block(m_parent); + if (!parent) { + return __LINE__; + } + + const uint64_t n = transactions.size(); + + for (uint64_t i = 1; i < n; ++i) { + const uint64_t parent_index = parent_indices[i]; + if (parent_index) { + if (parent_index >= parent->m_transactions.size()) { + return __LINE__; + } + transactions[i] = parent->m_transactions[parent_index]; + m_transactions.emplace_back(parent->m_transactions[parent_index]); + } + else { + m_transactions.emplace_back(transactions[i]); + } + } + } + else { + for (size_t i = 1; i < transactions.size(); ++i) { + m_transactions.emplace_back(transactions[i]); + } } m_transactions.shrink_to_fit(); diff --git a/src/side_chain.cpp b/src/side_chain.cpp index 8ca2baa..0476f3d 100644 --- a/src/side_chain.cpp +++ b/src/side_chain.cpp @@ -1603,7 +1603,7 @@ void SideChain::verify_loop(PoolBlock* block) if (m_pool && (block->m_minerWallet == m_pool->params().m_miningWallet)) { LOGINFO(0, log::Green() << "SHARE ADDED: height = " << block->m_sidechainHeight << ", id = " << block->m_sidechainId << ", mainchain height = " << block->m_txinGenHeight); } - server->broadcast(*block); + server->broadcast(*block, get_parent(block)); } } @@ -2070,7 +2070,7 @@ void SideChain::update_chain_tip(PoolBlock* block) if (p2pServer() && block->m_wantBroadcast && !block->m_broadcasted) { block->m_broadcasted = true; - p2pServer()->broadcast(*block); + p2pServer()->broadcast(*block, get_parent(block)); } }