軽量クライアントがフルノードに伝えるトランザクション
フルノードはブルームフィルターを介してトランザクションを取り扱い、通過するトランザクションに関しmerkleblockコマンドを送信する
アイテムがどの入れ物に入るかを判断するプロセス
from helper import hash256
bit_field_size=10
bit_field = [0] * bit_field_size
h = hash256(b'hello world')
bit = int.from_bytes(h, 'big') % bit_field_size
bit_field[bit] = 1
print(bit_field)
複数
from helper import hash256
bit_field_size=10
bit_field = [0] * bit_field_size
for item in (b'hello world', b'goodbye'):
h = hash256(item)
bit = int.from_bytes(h, 'big') % bit_field_size
bit_field[bit] = 1
print(bit_field)
### 複数ハッシュ関数を使用するブルームフィルター
from helper import hash256, hash160
bit_field_size=10
bit_field = [0] * bit_field_size
for item in (b'hello world', b'goodbye'):
for hash_function in (hash256, hash160):
h = hash_function(item)
bit = int.from_bytes(h, 'big') % bit_field_size
bit_field[bit] = 1
print(bit_field)
### BIP0037ブルームフィルター
murmur3と呼ばれるハッシュ関数を使用する
from helper import murmur3
from bloomfilter import BIP37_CONSTANT
field_size = 2
num_functions = 2
tweak = 42
bit_field_size = field_size * 8
bit_field = [0] * bit_field_size
for phrase in (b'hello world', b'goodbye'):
for i in range(num_functions):
seed = i * BIP37_CONSTANT + tweak
h = murmur3(phrase, seed=seed)
bit = h % bit_field_size
bit_field[bit] = 1
print(bit_field)
[0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0]
from bloomfilter import BloomFilter, BIP37_CONSTANT
from helper import bit_field_to_bytes, murmur3
field_size = 10
function_count = 5
tweak = 99
items = (b'Hello World', b'Goodbye!')
bit_field_size = field_size * 8
bit_field = [0] * bit_field_size
for item in items:
for i in range(function_count):
seed = i * BIP37_CONSTANT + tweak
h = murmur3(item, seed=seed)
bit = h % bit_field_size
bit_field[bit] = 1
print(bit_field_to_bytes(bit_field).hex())
### ブルームフィルターの読み込み
def filterload(self, flag=1):
payload = encode_varint(self.size)
payload += self.filter_bytes()
payload += int_to_little_endian(self.function_count, 4)
payload += int_to_little_endian(self.tweak, 4)
payload += int_to_little_endian(flag, 1)
return GenericMessage(b'filterload', payload)
class GetDataMessage:
command = b'getdata'
def __init__(self):
self.data = []
def add_data(self, data_type, identifier):
self.data.append((data_type, identifier))
def serialize(self):
result = encode_varint(len(self.data))
for data_type, identifier in self.data:
result += int_to_little_endian(data_type, 4)
result += identifier[::-1]
return result
from bloomfilter import BloomFilter
from helper import decode_base58
from merkleblock import MerkleBlock
from network import FILTERED_BLOCK_DATA_TYPE, GetHeadersMessage, GetDataMessage, HeadersMessage, SimpleNode
from tx import Tx
last_block_hex = '00000000000538d5c2246336644f9a4956551afb44ba47278759ec55\
ea912e19'
address = 'mwJn1YPMq7y5F8J3LkC5Hxg9PHyZ5K4cFv'
h160 = decode_base58(address)
node = SimpleNode('testnet.programmingbitcoin.com', testnet=True, logging=False)
bf = BloomFilter(size=30, function_count=5, tweak=90210)
bf.add(h160)
node.handshake()
node.send(bf.filterload())
start_block = bytes.fromhex(last_block_hex)
getheaders = GetHeadersMessage(start_block=start_block)
node.send(getheaders)
headers = node.wait_for(HeadersMessage)
getdata = GetDataMessage()
for b in headers.blocks:
if not b.check_pow():
raise RuntimeError('proof of work is invalid')
getdata.add_data(FILTERED_BLOCK_DATA_TYPE, b.hash())
node.send(getdata)
found = False
while not found:
message = node.wait_for(MerkleBlock, Tx)
if message.command == b'merkleblock':
if not message.is_valid():
raise RuntimeError('invalid merkle proof')
else:
for i, tx_out in enumerate(message.tx_outs):
if tx_out.script_pubkey.address(testnet=True) == address:
print('found: {}:{}'.format(message.id(), i))
found = True
break