upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/compactblock.h
blob: 745ee3b221850df4baf95c7ee58302dbaa6a81a2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
// 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 <vector>

// 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<unsigned int> vPrefilledIndex;
    std::vector<CTransaction> 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<uint64> 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<unsigned int>& 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<unsigned int>& vMissingIndex,
                           const std::vector<CTransaction>& vTxn);

#endif