// Copyright (c) 2026 Bitflash developers // Distributed under the MIT/X11 software license. // // Compact block relay (BIP152-style). Instead of sending a peer's full set // of transactions for a block they're about to receive -- almost all of // which they already have in their mempool from normal tx relay -- send a // short (48-bit, keyed-hash) ID per transaction instead. A peer that already // has every referenced transaction reconstructs the full block locally from // its mempool without any of the transaction bytes crossing the wire at // all; only genuinely missing transactions (typically just the coinbase) // need a follow-up round trip. // // Compatibility: this is purely additive. A peer that never sends // "sendcmpct" is never sent a compact block -- it keeps getting full // "block" messages exactly as before (see the getdata handler in // main.cpp). Old, unupgraded nodes on the live network are completely // unaffected; ProcessMessage's unknown-command handling means they simply // never see the new message types. No coordinated network upgrade needed. // // Deliberately scoped down from full BIP152: no high-bandwidth (unsolicited // push) mode, no differential-index encoding for prefilled transactions, // only the current chain tip is ever offered in compact form (an old block // is unlikely to still overlap much with any peer's current mempool, so the // bandwidth win doesn't apply -- full "block" is sent for those, same as // today). Only the coinbase is prefilled; every other transaction position // carries a short ID. #ifndef BITFLASH_COMPACTBLOCK_H #define BITFLASH_COMPACTBLOCK_H // Requires CBlock/CTransaction already visible -- #include "headers.h" (or // main.h) before this file. Deliberately doesn't include main.h itself: // main.h has no include guard (matches this codebase's existing headers, it // relies on being pulled in exactly once via headers.h), so a second // #include "main.h" here would break every translation unit that already // brought it in via headers.h first. #include // The compact representation of a block: header fields, a per-block nonce // (keys the short-ID hash so IDs can't be precomputed/ground across blocks), // the coinbase in full, and a 48-bit short ID for every other transaction // in block order. class CCmpctBlock { public: // header int nVersion; uint256 hashPrevBlock; uint256 hashMerkleRoot; unsigned int nTime; unsigned int nBits; unsigned int nNonce; // Keys the short-ID hash for this block specifically -- chosen fresh // per block (not reused from nNonce, which is public PoW data an // attacker already knows and could otherwise grind against). uint64 shortIdNonce; // Sent in full. In this implementation: always exactly the coinbase, // at index 0. std::vector vPrefilledIndex; std::vector vPrefilledTx; // One short ID per NON-prefilled transaction, in ascending block-index // order (i.e. the Nth entry here belongs to the Nth block position that // isn't in vPrefilledIndex). std::vector vShortTxId; IMPLEMENT_SERIALIZE ( READWRITE(this->nVersion); nVersion = this->nVersion; READWRITE(hashPrevBlock); READWRITE(hashMerkleRoot); READWRITE(nTime); READWRITE(nBits); READWRITE(nNonce); READWRITE(shortIdNonce); READWRITE(vPrefilledIndex); READWRITE(vPrefilledTx); READWRITE(vShortTxId); ) uint256 HeaderHash() const; }; // Compute the 48-bit short ID for a transaction under a given block's // short-ID nonce. Keyed (via the block's hashPrevBlock + shortIdNonce) so // IDs from one block can't be precomputed before that block exists, and // differ block to block even for the same transaction. uint64 ComputeShortTxId(const uint256& hashPrevBlock, uint64 shortIdNonce, const uint256& txid); // Build the compact form of `block` (which must have vtx[0] as the // coinbase, as every block on this chain does). bool BuildCompactBlock(const CBlock& block, CCmpctBlock& out); // Attempt to reconstruct a full block from a received compact block using // the local mempool. On full success, returns true and fills `outBlock`. // On partial success (some transactions not found locally), returns false // and fills `vMissingIndex` with the block-position indices that still need // to be requested via "getblocktxn" -- outBlock is left partially built // (coinbase and any resolved positions filled; caller must not use it until // FillMissingAndFinish completes it). bool TryReconstructBlock(const CCmpctBlock& cmpct, CBlock& outBlock, std::vector& vMissingIndex); // Complete a partially-reconstructed block (from a prior TryReconstructBlock // that returned false) using the transactions received in a "blocktxn" // response. vTxn must be in the same order as the indices this node // requested via "getblocktxn" for this block. bool FillMissingAndFinish(CBlock& outBlock, const std::vector& vMissingIndex, const std::vector& vTxn); #endif