1 #!/usr/bin/env python3
2 # Copyright (c) 2022-present The Bitcoin Core developers
3 # Distributed under the MIT software license, see the accompanying
4 # file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 """Helper routines relevant for compact block filters (BIP158).
6 """
7 from .crypto.siphash import siphash
8 9 10 def bip158_basic_element_hash(script_pub_key, N, block_hash):
11 """ Calculates the ranged hash of a filter element as defined in BIP158:
12 13 'The first step in the filter construction is hashing the variable-sized
14 raw items in the set to the range [0, F), where F = N * M.'
15 16 'The items are first passed through the pseudorandom function SipHash, which takes a
17 128-bit key k and a variable-sized byte vector and produces a uniformly random 64-bit
18 output. Implementations of this BIP MUST use the SipHash parameters c = 2 and d = 4.'
19 20 'The parameter k MUST be set to the first 16 bytes of the hash (in standard
21 little-endian representation) of the block for which the filter is constructed. This
22 ensures the key is deterministic while still varying from block to block.'
23 """
24 M = 784931
25 block_hash_bytes = bytes.fromhex(block_hash)[::-1]
26 k0 = int.from_bytes(block_hash_bytes[0:8], 'little')
27 k1 = int.from_bytes(block_hash_bytes[8:16], 'little')
28 return (siphash(k0, k1, script_pub_key) * (N * M)) >> 64
29 30 31 def bip158_relevant_scriptpubkeys(node, block_hash):
32 """ Determines the basic filter relevant scriptPubKeys as defined in BIP158:
33 34 'A basic filter MUST contain exactly the following items for each transaction in a block:
35 - The previous output script (the script being spent) for each input, except for
36 the coinbase transaction.
37 - The scriptPubKey of each output, aside from all OP_RETURN output scripts.'
38 """
39 spks = set()
40 for tx in node.getblock(blockhash=block_hash, verbosity=3)['tx']:
41 # gather prevout scripts
42 for i in tx['vin']:
43 if 'prevout' in i:
44 spks.add(bytes.fromhex(i['prevout']['scriptPubKey']['hex']))
45 # gather output scripts
46 for o in tx['vout']:
47 if o['scriptPubKey']['type'] != 'nulldata':
48 spks.add(bytes.fromhex(o['scriptPubKey']['hex']))
49 return spks
50