Skip to content

Commit d033cd5

Browse files
kwvgPastaPastaPasta
authored andcommitted
partial bitcoin#26341: add BIP158 false-positive element check in rpc_scanblocks.py
excludes: - fa54d30
1 parent eab94ac commit d033cd5

File tree

2 files changed

+78
-27
lines changed

2 files changed

+78
-27
lines changed
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
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
Lines changed: 29 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
#!/usr/bin/env python3
2-
# Copyright (c) 2016 The Bitcoin Core developers
2+
# Copyright (c) 2016-2022 The Bitcoin Core developers
33
# Distributed under the MIT software license, see the accompanying
44
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
5-
"""Specialized SipHash-2-4 implementations.
5+
"""SipHash-2-4 implementation.
66
7-
This implements SipHash-2-4 for 256-bit integers.
7+
This implements SipHash-2-4. For convenience, an interface taking 256-bit
8+
integers is provided in addition to the one accepting generic data.
89
"""
910

1011
def rotl64(n, b):
1112
return n >> (64 - b) | (n & ((1 << (64 - b)) - 1)) << b
1213

14+
1315
def siphash_round(v0, v1, v2, v3):
1416
v0 = (v0 + v1) & ((1 << 64) - 1)
1517
v1 = rotl64(v1, 13)
@@ -27,37 +29,37 @@ def siphash_round(v0, v1, v2, v3):
2729
v2 = rotl64(v2, 32)
2830
return (v0, v1, v2, v3)
2931

30-
def siphash256(k0, k1, h):
31-
n0 = h & ((1 << 64) - 1)
32-
n1 = (h >> 64) & ((1 << 64) - 1)
33-
n2 = (h >> 128) & ((1 << 64) - 1)
34-
n3 = (h >> 192) & ((1 << 64) - 1)
32+
33+
def siphash(k0, k1, data):
34+
assert(type(data) == bytes)
3535
v0 = 0x736f6d6570736575 ^ k0
3636
v1 = 0x646f72616e646f6d ^ k1
3737
v2 = 0x6c7967656e657261 ^ k0
38-
v3 = 0x7465646279746573 ^ k1 ^ n0
39-
v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
40-
v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
41-
v0 ^= n0
42-
v3 ^= n1
43-
v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
44-
v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
45-
v0 ^= n1
46-
v3 ^= n2
38+
v3 = 0x7465646279746573 ^ k1
39+
c = 0
40+
t = 0
41+
for d in data:
42+
t |= d << (8 * (c % 8))
43+
c = (c + 1) & 0xff
44+
if (c & 7) == 0:
45+
v3 ^= t
46+
v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
47+
v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
48+
v0 ^= t
49+
t = 0
50+
t = t | (c << 56)
51+
v3 ^= t
4752
v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
4853
v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
49-
v0 ^= n2
50-
v3 ^= n3
51-
v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
52-
v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
53-
v0 ^= n3
54-
v3 ^= 0x2000000000000000
55-
v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
56-
v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
57-
v0 ^= 0x2000000000000000
58-
v2 ^= 0xFF
54+
v0 ^= t
55+
v2 ^= 0xff
5956
v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
6057
v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
6158
v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
6259
v0, v1, v2, v3 = siphash_round(v0, v1, v2, v3)
6360
return v0 ^ v1 ^ v2 ^ v3
61+
62+
63+
def siphash256(k0, k1, num):
64+
assert(type(num) == int)
65+
return siphash(k0, k1, num.to_bytes(32, 'little'))

0 commit comments

Comments
 (0)