Block relay

After a block is mined it is broadcast to the P2P network where it will eventually be relayed to all nodes on the network. There are two methods available for relaying blocks:

  1. Legacy Relay

    • A node participating in legacy relaying will always send or request entire blocks.

    • For nodes that maintain a mempool this is quite bandwidth inefficient, since they probably already have most of the transactions from a new block in their mempool.

  2. Compact Block Relay

    • Specified in BIP 152.

    • The goal is to address the bandwidth inefficiencies of legacy relaying by only relaying the transactions of a new block that the requesting peer has not yet seen.

    • Check out this Compact Blocks FAQ for benchmarks and more info.

blocksonly versus block-relay-only

Bitcoin Core 0.12 introduced a -blocksonly setting that can reduce a node’s bandwidth usage by 88%. The reduction is achieved by not participating in transaction relay. For more info check out this post on blocksonly mode by Gregory Maxwell.

Blocksonly nodes currently use compact block relaying to download blocks even though they don’t maintain a full mempool. PR#22340 makes blocksonly nodes use legacy relaying to download new blocks. Because -blocksonly is a global startup option, it therefore applies to all connections

block-relay-only connections are a specific type of connection which is used by Bitcoin Core full nodes to only participate in block relay.

As currently implemented block-relay-only connections (introduced in PR#15759), disables both transaction and address relay. Bitcoin Core nodes per default settings make two outbound block-relay-only connections in addition to 8 regular outbound connections (also see eclipse attacks for more use cases of these connections).

Table 1. blocksonly mode vs block-relay-only connections

 

-blocksonly

block-relay-only

Applies to

All node connections (global)

Two randomly-chosen connections

Does Addr relay

Sends transactions

May do in special cases (e.g. submitted via RPC)

Receives transactions

Signals not to with fRelay, will disconnect if breached

?

Other connections

still makes two block-relay-only connections
(for which block-relay-only rules apply)

N/A

Bloom filters and SPV

A bloom filter is a probabilistic data structure. It supports two operations:

  1. adding an element to the filter

  2. querying an element from the filter

If an element has been previously added, then querying for the element will return true. If an element has not been added, then querying for the element may return true or false. In other words, querying may return a false positive, but will never return a false negative.

See the wikipedia page for how a bloom filter is implemented with hash functions onto a bitfield. Note that the false positive rate depends on the size of the filter and the number of hash functions.

BIP 37 introduced a new method for Simple Payment Verification (SPV) clients to use bloom filters to track transactions that affect their addresses. BIP 37 was implemented in Bitcoin Core in PR#1795.

Using the P2P messages defined in BIP 37, an SPV client can request that a full node send it transactions which match a bloom filter. The full node will then relay unconfirmed transactions that match the filter, and the client can request merkle blocks, which only contain the transactions that match the filter.

The SPV client chooses the bloom filter parameters (filter size, number of hashes and a 'tweak' for the hashes) and sends them to the node in a filterload message.

The original implementation contained a logic bug. If the client sent a filterload message with a zero-sized filter, then the serving node could later attempt a divide-by-zero and crash when querying an element from the filter. See CVE-2013-5700 for further details.

This bug was quietly fixed in PR#2914 without advertising the reason. That fix added the isFull and isEmpty booleans, which have proven to be confusing for developers.

PR#18806 removed those isFull and isEmpty booleans and adds a more straightforward fix for the issue.

Compact Block Filters for Light Clients

Compact Block Filters were introduced with BIP 157/158 as an improvement upon Bloom filters, as used in BIP 37. Instead of the client sending a filter to a full node peer, full nodes generate deterministic filters on block data that are served to the client. The light client gets these filters from the server and checks for itself if any of its objects match what is seen in the filter. If it does match, then the light client asks for the full block.

BIP 158 describes a structure for compact filters on block data. It specifies one filter type called Basic block filters, which encodes the scriptPubKeys of all the UTXOs spent in the block, and the scriptPubKeys of all the new UTXOs created in the block. This is the only block filter currently supported. PR#12254 implemented compact block filters in Bitcoin Core, and PR#14121 added a new index (-blockfilterindex=1), which stores the compact block filters for blocks that have been validated.

BIP 157 is the proposed specification for requesting and sending compact filters between nodes on the p2p network. It was implemented with a series of PRs, demonstrated in PR#18876.

Benefits:

  • Less asymmetry in the client. If light clients request a filter for a block, the server wont have to do any more work than the client had to do when making the request.

  • More privacy and less trust. The light client no longer sends a fingerprint of the data it is interested in to the server, and so it becomes way more difficult to analyse the light client’s activity.

  • Conceptually, BIP158’s Golomb-Coded Set (GCS) filter is similar to a Bloom filter (no false negatives, a controllable rate of false positives), but more compact.

Downsides:

  • They require more disk space because of the overhead that comes with the new index.

  • GCS filters are write-once (you can’t update them once created), and querying is much slower.

    • Bloom filters are effectively O(n) for finding n elements in them. GCS are O(m+n) for finding n elements in a filter of size m. So, Bloom filters are way faster if you’re only going to do one or a few queries. But as you’re querying for larger and larger number of elements, the relative downside of a GCS’s performance goes down.

glimpse of the future; PR#25957 uses BIP 157 block filters for faster wallet rescans.