|
| 1 | +#!/usr/bin/env python3 |
| 2 | +# Copyright (c) 2022 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 .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 relvant 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 |
0 commit comments