軽量クライアントがフルノードに伝えるトランザクション
フルノードはブルームフィルターを介してトランザクションを取り扱い、通過するトランザクションに関し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