BIP 0157 · 0158
How can a light client ask whether a block concerns it?
Full nodes publish a compact filter for every block; a light client tests its own scripts against it and downloads only the blocks that match. A match means “maybe”, and a miss means “not among the filter’s scripts”.
A light client does not download every block. It downloads the block headers, 80 bytes each, checks their proof of work, and follows the chain with the most work. Then it needs to learn which blocks touch its own coins without fetching them all. The older answer, BIP 37, had the client hand a Bloom filter of its interests to a full node. The authors of BIP 157 argue that most implementations gave wallets virtually zero privacy, let a malicious node omit data with little risk of detection, and let crafted filters burden honest nodes.12
BIPs 157 and 158, both assigned in 2017 and recorded as Deployed, reverse the direction. Full nodes build one deterministic filter per block, once, and serve it to anyone; the client tests the filter itself and downloads a block only if it matches. The filter is a Golomb-coded set: it matches every item in the set, and any other item with probability 1/M.345
Source details
This chapter is an independent explanation, not the proposals themselves. Preamble fields are shown as recorded in the pinned files, with author e-mail addresses omitted. Status is the proposal’s own field, not an endorsement or a sign of consensus.
BIP 157: Client Side Block Filtering
- BIP
- 157
- Layer
- Peer Services
- Title
- Client Side Block Filtering
- Authors
- Olaoluwa Osuntokun
Alex Akselrod
Jim Posen - Status
- Deployed
- Type
- Specification
- Assigned
- 2017-05-24
- License
- CC0-1.0
- Requires
- 158
BIP 158: Compact Block Filters for Light Clients
- BIP
- 158
- Layer
- Peer Services
- Title
- Compact Block Filters for Light Clients
- Authors
- Olaoluwa Osuntokun
Alex Akselrod - Status
- Deployed
- Type
- Specification
- Assigned
- 2017-05-24
- License
- CC0-1.0
- Requires
- 157
| n | (q, r) | code |
|---|---|---|
| 0 | (0, 0) | 0 00 |
| 1 | (0, 1) | 0 01 |
| 2 | (0, 2) | 0 10 |
| 3 | (0, 3) | 0 11 |
| 4 | (1, 0) | 10 00 |
| 5 | (1, 1) | 10 01 |
| 6 | (1, 2) | 10 10 |
| 7 | (1, 3) | 10 11 |
| 8 | (2, 0) | 110 00 |
| 9 | (2, 1) | 110 01 |
Same rule, P = 19
The smallest hashed value in block 1,263,442’s filter is 570,774. As a gap from zero: q = 1, r = 46,486.
10 0001011010110010110
21 bits. Writing a value below F = 2,354,793 at fixed width would take 22; the gain comes from small gaps being common.
Table from BIP 158 (line 138), each row recomputed by the tested model; the P = 19 code is the first code of a published filter.
What goes in
The one filter type BIP 158 defines, basic, is meant to hold everything a regular wallet needs. For each transaction in the block it MUST contain exactly two kinds of item: the script being spent by each input, except in the coinbase transaction, and the scriptPubKey of each output, except outputs that start with OP_RETURN. “Nil” (empty) items MUST NOT be included, and each script goes in once: the BIP calls the result a set.89
OP_RETURN outputs are left out for a reason the authors give: so that a future soft fork could commit to the filters, probably in an OP_RETURN output of the coinbase, without the commitment having to contain itself.10
BIP 158’s test vectors exercise these rules on real testnet blocks: one whose transaction pays to an empty script, one that spends from an empty script, one whose scripts repeat and whose coinbase carries an OP_RETURN witness commitment, which is left out, and one with nothing to put in the filter at all, whose filter is the single byte 00.891112
So the filter answers one question about one block: does any output pay to this script, or any input spend from it? It holds scripts and nothing else. A wallet that watches its own scripts can ask that question; a miss tells it nothing about anything outside the set, such as a transaction ID or an OP_RETURN output.813
Building the set
Each script is first hashed with SipHash, which BIP 158 requires with parameters c = 2 and d = 4. The key is the first 16 bytes of the block’s hash in little-endian order, so it is deterministic but differs from block to block. The 64-bit output is then mapped into the range [0, N·M), where N is the number of items, by multiplying by N·M and keeping the top 64 bits of the product.1415
The hashed values are sorted, and only the gaps between neighbours are written. Because the values are spread uniformly at random, the gaps follow roughly a geometric distribution, with small gaps common and large ones rarer, and Golomb-Rice coding is built for exactly that: the gap divided by 2^P goes in unary, as that many ones and a zero, and the remainder follows in P bits. The bit stream is padded with zeros to a whole byte and prefixed with N as a CompactSize. A filter with no items MUST be a single zero byte.6121617
For the basic filter, P MUST be 19 and M MUST be 784931. The authors cite analysis that M ≈ 1.497137·2^P is close to optimal, and empirical analysis that these values minimize bandwidth once both the filters themselves and the blocks downloaded because of false positives are counted.1819
The coding is tight. Each code needs at least P + 1 bits, so a filter of N items takes at least N·(P + 1) bits, 20 per item for the basic filter. In the published vectors, block 180,480’s filter holds 13 scripts in 35 bytes and block 926,485’s holds 9 in 25: about 21 bits per item, close to that floor, against about 23 bits to write each hashed value at fixed width, and hundreds of bits for the scripts themselves.2021
A · Interactive
Static view: the first block, its first query and its coding. With JavaScript you can choose among 6 published blocks and test other scripts.
Block 926,485: 5 transactions, 9 elements in the filter
- output script
76a914876fbb…c6d688ac - output script
6a24aa21a9ed…e1b377eeleft out: starts with OP_RETURN - output script
52534b424c4f…7f109090 - output script
76a9143ebc40…397288ac - output script
76a914503330…573988ac - output script
76a914c01a7c…156688ac - output script
a914b7e6f7ff…e6b1b387 - output script
76a914913bcc…e7eb88ac - output script
a9148fc37ad4…64faa687 - output script
76a914913bcc…e7eb88acleft out: duplicate of an earlier element - spent script
a914feb8a296…23887687 - spent script
76a914c01a7c…156688acleft out: duplicate of an earlier element - spent script
76a914913bcc…e7eb88acleft out: duplicate of an earlier element - spent script
76a914913bcc…e7eb88acleft out: duplicate of an earlier element - spent script
76a914913bcc…e7eb88acleft out: duplicate of an earlier element - spent script
76a914913bcc…e7eb88acleft out: duplicate of an earlier element - spent script
76a914913bcc…e7eb88acleft out: duplicate of an earlier element - spent script
76a914913bcc…e7eb88acleft out: duplicate of an earlier element
Test a script against the filter
Script 76a914876fbb…c6d688ac, from this block.
Hashed into [0, F): 1,923,207 of F = 7,064,379
- value 1:
10,156is below the target: keep decoding - value 2:
1,923,207equals the target
Match: the block may concern this script. A match can be a false positive, so the client downloads the block to find out.
The filter on the wire: 25 bytes
09027acea61b6cc3fb33f5d52f7d088a6b2f75d234e89ca800
First byte: N = 9 as a CompactSize. Then each gap between sorted values, as q ones, a zero, and 19 remainder bits:
- gap 10,156
00000010011110101100q = 0, r = 10,156 - gap 1,913,051
11101010011000011011011q = 3, r = 340,187 - gap 418,301
01100110000111111101q = 0, r = 418,301 - gap 737,117
100110011111101011101q = 1, r = 212,829 - gap 339,837
01010010111101111101q = 0, r = 339,837 - gap 34,982
00001000100010100110q = 0, r = 34,982
3 more codes follow. 186 bits of codes, then 6 zero bits of padding to the byte boundary.
Source: BIP 158 testnet-19.json, block 926,485 (“Duplicate pushdata 913bcc2be49cb534c20474c4dee1e9c4c317e7eb”), line 9. Rebuilt at build time by the tested model; the filter bytes and the filter header equal the vector’s.
B · Worked example
BIP 158’s testnet block 926485 (“Duplicate pushdata 913bcc2be49cb534c20474c4dee1e9c4c317e7eb”): from the block’s scripts to the published filter, then two queries.
Collect the elements: 9 distinct scripts
- output 1
76a914876fbb82ec05caa6af7a3b5e5a983aae6c6cc6d688ac- output 2
52534b424c4f434b3acd16772ad61a3c5f00287480b720f6035d5e54c9efc71be94bb5e3727f109090- output 3
76a9143ebc40e411ed3c76f86711507ab952300890397288ac
9 of 18 scripts are left out (OP_RETURN, empty or repeated).
Hash each into [0, N·M) with SipHash keyed by the block hash
- F = N · 784931
7064379- smallest value
10156
Sort, take the gaps, Golomb-Rice code them
- first gap 10156
0 0000010011110101100
186 bits plus 6 of padding.
Prefix N and serialize
- filter, 25 bytes
09027acea61b6cc3fb33f5d52f7d088a6b2f75d234e89ca800
Query: hash the script the same way and walk the values
- script in the block
76a914876fbb82ec05caa6af… → match- script from block 180480
2102e769e60137a4df6b0df8… → no match
A match means “possibly”; no match means “not among the elements”.
Source: BIP 158 testnet-19.json (line 9); rebuilt by the tested model and checked against the vector.
Querying runs the same steps in reverse. The client hashes its script with the same key and range, then decodes the gaps one by one, adding them up. If a running total equals the target, the filter matches; once a total passes the target, the search stops. A wallet with many scripts can hash and sort them all and walk the filter once.2223
Yes means maybe
A match says the block may concern the client, not that it does. With M = 784931, an unrelated script matches a given filter with probability 1/784931, about 1.3 in a million. That is small per test, but a wallet tests many scripts against many blocks: 100 scripts checked against 10,000 blocks give an expected 1.3 false matches, each costing a needless block download.51924
A miss is firmer. Every item in the set matches with probability 1, so if the filter does not match a script, no output in that block pays to it and no input spends from it, provided the filter is the right one, which is what filter headers are for. In the figure, each block’s own scripts all match its filter, and the scripts taken from other blocks, as expected, do not.51113
Filters do not hide what the client does next. The authors argue that privacy improves because blocks can be fetched from any source, so no single peer learns everything a client needs. Still, whoever serves a block sees that it was requested, so a client fetching blocks from the network SHOULD pick outbound peers at random, to limit what intersection analysis can learn; the most cautious clients may use private information retrieval.2526
Trusting the filters
A filter cannot be checked by looking at it, so BIP 157 chains them. A filter’s hash is the double SHA-256 of its bytes, and its header is the double SHA-256 of that hash followed by the previous block’s filter header. The previous header for the genesis block is 32 zero bytes. Like block headers, each filter header commits to everything before it.2728
Block 0
- filter
019dfca8- filter hash = dSHA256(filter)
c03705b2d6…f78a4c- previous header
0000000000…000000(32 zero bytes, for genesis)- header = dSHA256(hash ‖ previous)
21584579b7…81b750
- block 1 is not in the vectors
Block 2
- filter
0174a170- filter hash = dSHA256(filter)
3cd1fafd2a…adc8eb- previous header
d7bdac13a5…c24fe1(block 1’s header)- header = dSHA256(hash ‖ previous)
186afd11ef…386cf0
Block 3
- filter
016cf7a0- filter hash = dSHA256(filter)
ae191633e8…95987f- previous header
186afd11ef…386cf0(block 2’s header)- header = dSHA256(hash ‖ previous)
8d63aadf5a…729d2a
Blocks from BIP 158’s testnet-19.json; hashes and headers recomputed by the tested model and checked against the vectors. Hashes are shown in display byte order, as the vectors print them; the double SHA-256 is computed over the internal (reversed) order.
The BIP’s recommended client procedure leans on that chain. A client SHOULD sync block headers first, then download and verify filter headers, and MAY first fetch the filter header at every 1,000th block (getcfcheckpt), then fill in each interval and check it against them. Unless it has a trusted peer, it SHOULD ask several. If two peers disagree, it SHOULD find the first header they disagree on, download that block, compute the filter itself, and ban any peer whose header does not match. It SHOULD also check every filter it downloads against its header. The authors’ claim is that a client with at least one honest peer can identify the correct filters.29303132
Strictly, the block alone is not enough to recompute a basic filter: the filter also holds the scripts its inputs spend, which the block does not contain, and BIP 157 does not say how a light client should obtain or check them. A client may also skip a request entirely: if a filter header commits to the hash of the empty filter, the BIP lets it conclude the filter is empty.3334
The serving side is kept cheap. Nodes SHOULD build and store each block’s filters as the block connects, and SHOULD NOT build them on request, since a peer could otherwise ask for small filters of large blocks and force expensive I/O. Requests are bounded: at most 1,000 blocks of filters, or 2,000 filter headers, at a time. A node signals support with the NODE_COMPACT_FILTERS service bit, and the authors chose this P2P-only design over a consensus change that would make blocks commit to their filters.35363738
Evidence
Each numbered marker in the text points here, and the ↩ links lead back to every place that cites an entry. Quotes are verbatim from the BIP files at commit
3a10b5b, including their wiki markup; links open
the exact lines; the quoted text sits under each entry. Labels say what kind of statement each is: a rule, the author’s rationale, history,
a test vector, or our own inference.
-
↩ Rule Light clients download all block headers, verify proof of work, follow the most-work chain, and then download only the data relevant to them; headers are a fixed 80 bytes.
Quoted source text (1)
They achieve this by downloading all block headers, verifying the proofs of work, and following the longest proof-of-work chain. Since block headers are a fixed 80-bytes and are generated every 10 minutes on average, the bandwidth required to sync the block headers is minimal. Light clients then download only the blockchain data relevant to them directly from peers and validate inclusion in the header chain.
-
↩ Author’s rationale The authors argue that with BIP 37 the client sends a Bloom filter to a full node, that most implementations offer virtually zero privacy, that malicious nodes can omit data with little risk of detection, and that crafted filters are a DoS vector.
BIP 157 L51–53 BIP 157 L57–59 BIP 157 L61–62 BIP 157 L64–66
Quoted source text (4)
With BIP 37, a client sends a Bloom filter it wants to watch to a full node peer, then receives notifications for each new transaction or block that matches the filter.
It has been shown, however, that most implementations available offer virtually ''zero privacy'' to wallets and other
Additionally, malicious full nodes serving light clients can omit critical data with little risk of detection
honest nodes servicing BIP 37 light clients may incur significant I/O and CPU resource usage due to maliciously crafted Bloom filters, creating a denial-of-service (DoS) vector
-
↩ History BIP 157 (Osuntokun, Akselrod, Posen) and BIP 158 (Osuntokun, Akselrod) were both assigned on 2017-05-24 and are recorded as Deployed.
Quoted source text (2)
Title: Client Side Block Filtering Authors: Olaoluwa Osuntokun <laolu32@gmail.com> Alex Akselrod <alex@akselrod.org> Jim Posen <jimpo@coinbase.com> Status: Deployed Type: Specification Assigned: 2017-05-24
Title: Compact Block Filters for Light Clients Authors: Olaoluwa Osuntokun <laolu32@gmail.com> Alex Akselrod <alex@akselrod.org> Status: Deployed Type: Specification Assigned: 2017-05-24
-
↩ Author’s rationale BIP 157 is the opposite of BIP 37: full nodes generate deterministic filters of block data, built once and stored, and the client downloads a block if the filter matches.
Quoted source text (1)
The alternative detailed in this document can be seen as the opposite of 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. A light client can then download an entire block if the filter matches the data it is watching for. Since filters are deterministic, they only need to be constructed once and stored on disk, whenever a new block is connected to the chain.
-
↩ a b c Rule A Golomb-coded set matches all items in the set with probability 1 and other items with probability 1/M.
Quoted source text (1)
compressed into a probabilistic structure called a ''Golomb-coded set'' (GCS), which matches all items in the set with probability 1, and matches other items with probability <code>1/M</code> for some integer parameter <code>M</code>.
-
↩ a b c Rule Golomb-Rice coding splits a value into a quotient and a remainder modulo 2^P; the quotient is written in unary (q ones then a zero) and the remainder as P big-endian bits.
Quoted source text (1)
With Golomb-Rice, a value is split into a quotient and remainder modulo <code>2^P</code>, which are encoded separately. The quotient <code>q</code> is encoded as ''unary'', with a string of <code>q</code> 1's followed by one 0. The remainder <code>r</code> is represented in big-endian by P bits.
-
↩ Test vector The model reproduces every row of BIP 158's P = 2 Golomb-Rice table, and the first code of a published filter with P = 19.
Quoted source text (1)
For example, this is a table of Golomb-Rice coded values using <code>P=2</code>:
-
↩ a b c d Rule A basic filter MUST contain exactly, for each transaction, the previous output script of each input (except the coinbase) and each output's scriptPubKey except OP_RETURN outputs; nil items MUST NOT be included.
Quoted source text (1)
The basic filter is designed to contain everything that a light client needs to sync a regular Bitcoin wallet. A basic filter MUST contain exactly the following items for each transaction in a block: * The previous output script (the script being spent) for each input, except for the coinbase transaction. * The scriptPubKey of each output, aside from all <code>OP_RETURN</code> output scripts. Any "nil" items MUST NOT be included into the final set of filter elements.
-
↩ a b Test vector Each script enters the filter once: BIP 158 calls the elements a set, and the published filter for block 926,485, whose scripts repeat, only reproduces when duplicates are collapsed (9 elements, not 17).
Quoted source text (1)
Any "nil" items MUST NOT be included into the final set of filter elements.
-
↩ Author’s rationale OP_RETURN outputs are excluded so filters can later be committed to by soft fork, likely in a coinbase OP_RETURN, without a circular dependency.
BIP 158 L277–283 BIP 158 L282–283
Quoted source text (2)
We exclude all outputs that start with <code>OP_RETURN</code> in order to allow filters to easily be committed to in the future via a soft-fork. A likely area for future commitments is an additional <code>OP_RETURN</code> output in the coinbase transaction similar to the current witness commitment
we avoid a circular dependency between the commitment, and the item being committed to.
-
↩ a b c d Test vector This site's block-filter model rebuilds all ten of BIP 158's testnet filter vectors (the BIP's text says five blocks; the pinned file has ten) from the blocks and spent scripts, byte for byte, and reproduces their filter headers; the figures show some of them, rebuilt at build time.
Quoted source text (1)
Test vectors for basic block filters on five testnet blocks, including the filters and filter headers, can be found [[bip-0158/testnet-19.json|here]].
-
↩ a b Rule The bit stream is padded with zeros to a byte boundary; the serialized filter is N as a CompactSize followed by the compressed bytes; a zero-element filter MUST be one zero byte.
BIP 158 L197–198 BIP 158 L305–309 BIP 158 L311
Quoted source text (3)
Finally, the bit stream is padded with 0's to the nearest byte boundary and serialized to the output byte vector.
Since the value <code>N</code> is required to decode a GCS, a serialized GCS includes it as a prefix, written as a <code>CompactSize</code>. Thus, the complete serialization of a filter is: * <code>N</code>, encoded as a <code>CompactSize</code> * The bytes of the compressed filter itself
A zero element filter MUST be written as one byte containing zeroes.
-
↩ a b Editorial inference A filter holds scripts only; a missing match rules out an output paying to, or an input spending from, that script in the block, and says nothing about data outside the element set (txids, OP_RETURN outputs).
BIP 158 L270–273 BIP 158 L61–62
Quoted source text (2)
A basic filter MUST contain exactly the following items for each transaction in a block: * The previous output script (the script being spent) for each input, except for the coinbase transaction. * The scriptPubKey of each output, aside from all <code>OP_RETURN</code> output scripts.
which matches all items in the set with probability 1
-
↩ Rule Items are hashed with SipHash, which MUST use c = 2 and d = 4, and mapped into [0, F) by multiplying by F and taking the top 64 bits of the 128-bit product.
Quoted source text (2)
The items are first passed through the pseudorandom function ''SipHash'', which takes a 128-bit key <code>k</code> and a variable-sized byte vector and produces a uniformly random 64-bit output. Implementations of this BIP MUST use the SipHash parameters <code>c = 2</code> and <code>d = 4</code>.
The 64-bit SipHash outputs are then mapped uniformly over the desired range by multiplying with F and taking the top 64 bits of the 128-bit result.
-
↩ Rule The key k MUST be the first 16 bytes of the block hash in little-endian representation, so it is deterministic but varies by block.
Quoted source text (1)
The parameter <code>k</code> MUST be set to the first 16 bytes of the hash (in standard little-endian representation) of the block for which the filter is constructed. This ensures the key is deterministic while still varying from block to block.
-
↩ Rule A GCS of N items is built by hashing into [0, N·M), sorting, taking differences, and Golomb-Rice coding them.
Quoted source text (1)
At a high level, a GCS is constructed from a set of <code>N</code> items by: # hashing all items to 64-bit integers in the range <code>[0, N * M)</code> # sorting the hashed values in ascending order # computing the differences between each value and the previous one # writing the differences sequentially, compressed with Golomb-Rice coding
-
↩ Author’s rationale Because the hashed items are uniformly distributed, their sorted differences resemble a geometric distribution, which Golomb-Rice coding compresses optimally.
Quoted source text (1)
Since the items are distributed uniformly, it can be shown that the differences resemble a geometric distribution<ref>https://en.wikipedia.org/wiki/Geometric_distribution</ref>. ''Golomb-Rice'' ''coding''<ref>https://en.wikipedia.org/wiki/Golomb_coding#Rice_coding</ref> is a technique that optimally compresses geometrically distributed values.
-
↩ Rule The basic filter (type 0x00) MUST use P = 19 and M = 784931.
BIP 158 L263–265 BIP 158 L290–291
Quoted source text (2)
* Basic (<code>0x00</code>) ** <code>M = 784931</code> ** <code>P = 19</code>
The parameter <code>P</code> MUST be set to <code>19</code>, and the parameter <code>M</code> MUST be set to <code>784931</code>.
-
↩ a b Author’s rationale The authors cite analysis that M = 1.497137·2^P is close to optimal when P and M are chosen independently, and empirical analysis that these parameters minimize bandwidth, counting both false-positive block downloads and filter size.
BIP 158 L291–294 BIP 158 L296–298
Quoted source text (2)
Analysis has shown that if one is able to select <code>P</code> and <code>M</code> independently, then setting <code>M=1.497137 * 2^P</code> is close to optimal
Empirical analysis also shows that these parameters minimize the bandwidth utilized, considering both the expected number of blocks downloaded due to false positives and the size of the filters themselves.
-
↩ Rule A GCS takes at least N·(P + 1) bits.
Quoted source text (1)
The result is a byte vector with a minimum size of <code>N * (P + 1)</code> bits.
-
↩ Test vector In the published vectors, block 180,480's filter has 13 elements in 35 bytes and block 926,485's has 9 in 25 bytes: about 21 bits per element, close to the 20-bit floor, against about 23 bits to write each hashed value at fixed width below N·M.
Quoted source text (1)
Test vectors for basic block filters on five testnet blocks, including the filters and filter headers
-
↩ a b Rule To query, the target is hashed the same way and the decoded deltas are summed in order; the search stops once a value exceeds the target.
BIP 158 L219–223 BIP 158 L243–246
Quoted source text (2)
To check membership of an item in a compressed GCS, one must reconstruct the hashed set members from the encoded deltas. The procedure to do so is the reverse of the compression: deltas are decoded one by one and added to a cumulative sum. Each intermediate sum represents a hashed value in the original set. The queried item is hashed in the same way as the set members
// Since the values in the set are sorted, terminate the search once // the decoded value exceeds the target. if set_item > target_hash: break
-
↩ Rule Several targets can be checked at once by hashing and sorting them and merging against the decoded set.
Quoted source text (1)
Some applications may need to check for set intersection instead of membership of a single item. This can be performed far more efficiently than checking each item individually by leveraging the sorted structure of the compressed GCS. First the query elements are all hashed and sorted, then compared in order against the decompressed GCS contents.
-
↩ Test vector With M = 784931, an unrelated script matches a given filter with probability 1/784931, about 1.3 in a million; checking 100 scripts against 10,000 blocks gives an expected 1.3 false matches.
Quoted source text (1)
Set membership queries against the hash outputs will have a false positive rate of <code>1 / M</code>.
-
↩ Author’s rationale The authors argue privacy improves because blocks can be downloaded from any source, so no one peer gets complete information; very privacy-conscious clients may use Private Information Retrieval.
Quoted source text (1)
Finally, client privacy is improved because blocks can be downloaded from ''any source'', so that no one peer gets complete information on the data required by a client. Extremely privacy conscious light clients may opt to anonymously fetch blocks using advanced techniques such a Private Information Retrieval
-
↩ Rule A client fetching full blocks from the P2P network SHOULD download them from outbound peers at random to mitigate privacy loss from transaction intersection analysis. (Inference: the peer serving a downloaded block sees which block was requested.)
Quoted source text (1)
If a client is fetching full blocks from the P2P network, they SHOULD be downloaded from outbound peers at random to mitigate privacy loss due to transaction intersection analysis.
-
↩ a b Rule A filter's hash is the double-SHA256 of the serialized filter; its header is the double-SHA256 of the filter hash concatenated with the previous filter header; the genesis block's previous header is 32 zero bytes.
Quoted source text (1)
The canonical hash of a block filter is the double-SHA256 of the serialized filter. Filter headers are 32-byte hashes derived for each block filter. They are computed as the double-SHA256 of the concatenation of the filter hash with the previous filter header. The previous filter header used to calculate that of the genesis block is defined to be the 32-byte array of 0's.
-
↩ Author’s rationale Filter headers commit to all previous filters, like block headers; if peers' header chains differ, the client finds where they diverge, downloads the block, computes the correct filter, and identifies the faulty peer.
Quoted source text (1)
Similar to how block headers have a Merkle commitment to all transaction data in the block, we define filter headers that have commitments to the block filters. Also like block headers, filter headers each have a commitment to the preceding one. Before downloading the block filters themselves, a light client can download all filter headers for the current block chain and use them to verify the authenticity of the filters. If the filter header chains differ between multiple peers, the client can identify the point where they diverge, then download the full block and compute the correct filter, thus identifying which peer is faulty.
-
↩ Rule Clients SHOULD sync block headers first, then download and verify filter headers (getcfheaders), MAY first fetch checkpoints every 1,000 blocks (getcfcheckpt), SHOULD connect to multiple peers unless a trusted peer serves headers, and on conflict SHOULD download the block, derive the filter and ban peers whose header does not match.
BIP 157 L367–369 BIP 157 L374–378 BIP 157 L383–385 BIP 157 L389–392
Quoted source text (4)
Clients SHOULD first sync the entire block header chain from peers using the standard headers-first syncing mechanism before downloading any block filters or filter headers.
Once a client's block headers are in sync, it SHOULD download and verify filter headers for all blocks and filter types that it might later download. The client SHOULD send <code>getcfheaders</code> messages to peers and derive and store the filter headers for each block. The client MAY first fetch headers at evenly spaced intervals of 1,000 by sending <code>getcfcheckpt</code>.
Unless securely connected to a trusted peer that is serving filter headers, the client SHOULD connect to multiple outbound peers that support each filter type to mitigate the risk of downloading incorrect headers.
The client then SHOULD download the full block from any peer and derive the correct filter and filter header. The client SHOULD ban any peers that sent a filter header that does not match the computed one.
-
↩ Rule The client SHOULD test that each filter links to its filter header and ban peers that send incorrect filters.
Quoted source text (1)
The client SHOULD test that each filter links to its corresponding filter header and ban peers that send incorrect filters.
-
↩ Author’s rationale The protocol guarantees that light clients with at least one honest peer can identify the correct block filters.
Quoted source text (1)
The resulting protocol guarantees that light clients with at least one honest peer are able to identify the correct block filters.
-
↩ Rule Clients configured with trusted checkpoints MAY sync block headers only from the last checkpoint; separately, a client MAY fetch filter headers at every 1,000th block with getcfcheckpt and verify each 1,000-header range against them.
Quoted source text (1)
The client MAY first fetch headers at evenly spaced intervals of 1,000 by sending <code>getcfcheckpt</code>. The header checkpoints allow the client to download filter headers for different intervals from multiple peers in parallel, verifying each range of 1,000 headers against the checkpoints.
-
↩ Editorial inference To recompute a basic filter one needs the scripts the block's inputs spend, which the block itself does not contain; BIP 157 does not say how a light client should obtain or check them.
Quoted source text (2)
* The previous output script (the script being spent) for each input, except for the coinbase transaction.
The client then SHOULD download the full block from any peer and derive the correct filter and filter header.
-
↩ Rule A client MAY check whether a filter is empty from its header before requesting it.
Quoted source text (1)
The client MAY check if a filter is empty before requesting it by checking if the filter header commits to the hash of the empty filter, saving a round trip if that is the case.
-
↩ Rule Nodes SHOULD generate and persist filters for each new block, and SHOULD NOT generate filters dynamically on request, because small filters of large blocks would allow DoS through asymmetric I/O.
BIP 157 L347–349 BIP 157 L355–357
Quoted source text (2)
For each new block that is connected to the main chain, nodes SHOULD generate filters for all supported types and persist them.
Nodes SHOULD NOT generate filters dynamically on request, as malicious peers may be able to perform DoS attacks by requesting small filters derived from large blocks.
-
↩ Rule A getcfilters request MUST span a height difference strictly less than 1,000 (at most 1,000 blocks) and a getcfheaders request strictly less than 2,000; checkpoints are filter headers at heights that are positive multiples of 1,000.
BIP 157 L170 BIP 157 L234 BIP 157 L308–310
Quoted source text (3)
The height of the block with hash StopHash MUST be greater than or equal to StartHeight, and the difference MUST be strictly less than 1000.
The height of the block with hash StopHash MUST be greater than or equal to StartHeight, and the difference MUST be strictly less than 2,000.
The filter headers included are the set of all filter headers on the requested chain where the height is a positive multiple of 1,000.
-
↩ Rule BIP 158 allocates service bit NODE_COMPACT_FILTERS (1 << 6); a node that sets it MUST respond to all BIP 157 messages for filter type 0x00.
Quoted source text (1)
| NODE_COMPACT_FILTERS | style="white-space: nowrap;" | <code>1 << 6</code> | If enabled, the node MUST respond to all BIP 157 messages for filter type <code>0x00</code>
-
↩ Author’s rationale The authors chose filter headers at the P2P layer over requiring blocks to commit to filters, which would need a consensus change.
Quoted source text (1)
An alternative solution is to require Bitcoin blocks to include commitments to derived block filters, so light clients can verify authenticity given block headers and some additional witness data. This would require a network-wide change to the Bitcoin consensus rules, however, whereas this document proposes a solution purely at the P2P layer.