import socket
import struct
import time
import hashlib
from random import randint
from io import BytesIO
# MurmurHash3 implementation
def murmur_hash3(data, seed=0):
c1 = 0xcc9e2d51
c2 = 0x1b873593
length = len(data)
h1 = seed & 0xffffffff
num_blocks = length // 4
for i in range(num_blocks):
k1 = struct.unpack("> 17)) & 0xffffffff
k1 = (k1 * c2) & 0xffffffff
h1 ^= k1
h1 = ((h1 << 13) | (h1 >> 19)) & 0xffffffff
h1 = ((h1 * 5) + 0xe6546b64) & 0xffffffff
tail = data[num_blocks*4:]
k1 = 0
tlen = len(tail)
if tlen >= 3:
k1 ^= (tail[2] << 16) & 0xffffffff
if tlen >= 2:
k1 ^= (tail[1] << 8) & 0xffffffff
if tlen >= 1:
k1 ^= tail[0] & 0xffffffff
if tlen > 0:
k1 = (k1 * c1) & 0xffffffff
k1 = ((k1 << 15) | (k1 >> 17)) & 0xffffffff
k1 = (k1 * c2) & 0xffffffff
h1 ^= k1
h1 ^= length
h1 ^= (h1 >> 16) & 0xffffffff
h1 = (h1 * 0x85ebca6b) & 0xffffffff
h1 ^= (h1 >> 13) & 0xffffffff
h1 = (h1 * 0xc2b2ae35) & 0xffffffff
h1 ^= (h1 >> 16) & 0xffffffff
return h1
# Bloom Filter class
class BloomFilter:
def __init__(self, size, num_funcs, tweak):
self.size = size # in bytes
self.num_funcs = num_funcs
self.tweak = tweak & 0xffffffff
self.bit_field = bytearray([0] * size)
def add(self, item):
for i in range(self.num_funcs):
seed = (i * 0xfba4c795 + self.tweak) & 0xffffffff
h = murmur_hash3(item, seed)
bit = h % (self.size * 8)
self.bit_field[bit // 8] |= (1 << (bit % 8))
def filter_bytes(self):
return bytes(self.bit_field)
# Varint serialization
def serialize_varint(n):
if n < 0xfd:
return struct.pack("= len(flags):
raise ValueError("Flags too short")
flag = (flags[bit_pos // 8] & (1 << (bit_pos % 8))) != 0
bit_pos += 1
if height == 0 or not flag:
if hash_pos >= len(hashes):
raise ValueError("Hashes too short")
curr = hashes[hash_pos]
hash_pos += 1
if height == 0 and flag:
matches.append(curr)
return curr
left = traverse(height - 1, pos * 2)
if pos * 2 + 1 >= (1 << height):
right = left # Duplicate leaf
else:
right = traverse(height - 1, pos * 2 + 1)
return self.double_sha256(left + right)
height = 0
while (1 << height) < tx_count:
height += 1
root = traverse(height, 0)
if hash_pos != len(hashes):
raise ValueError("Extra hashes")
if bit_pos != len(flags) * 8:
raise ValueError("Extra bits")
return matches, root
def double_sha256(self, b):
return hashlib.sha256(hashlib.sha256(b).digest()).digest()
if __name__ == '__main__':
hosts = [
'45.94.168.5', # Known working node
]
txid_hex = "0b446280724fdb10892d0f765b378023e41ddca48ca5cf6e9d08e23ccdcb65a9"
block_hex = "00000000000000005c7ed697383655849a809350e1716100be6c57e190f89bba"
# Internal byte order (reverse of displayed hex)
txid_internal = bytes.fromhex(txid_hex)[::-1]
block_internal = bytes.fromhex(block_hex)[::-1]
# Create bloom filter for the TX
bloom_size = 1 # Small size for single item
num_funcs = 5
tweak = 21
bloom = BloomFilter(bloom_size, num_funcs, tweak)
bloom.add(txid_internal)
for host in hosts:
try:
print(f"Trying to connect to {host}...")
node = SimpleNode(host, port=48333, testnet=True, logging=True)
node.handshake()
node.send_filterload(bloom)
node.send_getdata(block_internal)
node.wait_for_merkleblock(txid_internal)
node.close()
break # Exit loop on success
except socket.gaierror as e:
print(f"Hostname resolution failed for {host}: {e}")
except socket.error as e:
print(f"Connection failed for {host}: {e}")
except ValueError as e:
print(f"Protocol error for {host}: {e}")
except Exception as e:
print(f"Other error for {host}: {e}")
else:
print("All hosts failed. Try finding an active Testnet4 node IP from Bitnodes or run a local node.")