《区块链编程》第十二章
简单支付验证
练习1
p200
代码实现
# -*- coding: utf-8 -*-
# @Author: 从化北(喵星人)
# @Date: 2022-01-17 15:12:21
# @Last Modified by: 从化北
# @Last Modified time: 2022-01-17 15:16:41
from helper import hash160
bit_field_size = 10
bit_field = [0] * bit_field_size
for item in (b'hello world', b'goodbye'):
h = hash160(item)
bit = int.from_bytes(h, 'big') % bit_field_size
bit_field[bit] = 1
print(bit_field)
测试
[1, 1, 0, 0, 0, 0, 0, 0, 0, 0]
[Finished in 610ms]
练习2
p203
代码实现
# -*- coding: utf-8 -*-
# @Author: 从化北(喵星人)
# @Date: 2022-01-17 15:18:00
# @Last Modified by: 从化北
# @Last Modified time: 2022-01-17 15:22:27
from bloomfilter import BloomFilter, BIP37_CONSTANT
from helper import bit_field_to_bytes, murmur3
field_size = 10
function_cuont = 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_cuont):
seed = i * BIP37_CONSTANT + tweak
h = murmur3(item, seed=seed)
bit = h % bit_field_size
bit_field[bit] = 1
print(bit_field)
print(bit_field_to_bytes(bit_field).hex())
测试
[0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0]
4000600a080000010940
[Finished in 374ms]
练习3
p203
代码实现
# -*- coding: utf-8 -*-
# @Author: 从化北(喵星人)
# @Date: 2022-01-17 15:23:41
# @Last Modified by: 从化北
# @Last Modified time: 2022-01-17 15:23:41
class BloomFilter:
...
def add(self, item):
'''Add an item to the filter'''
# iterate self.function_count number of times
for i in range(self.function_count):
# BIP0037 spec seed is i*BIP37_CONSTANT + self.tweak
seed = i * BIP37_CONSTANT + self.tweak
# get the murmur3 hash given that seed
h = murmur3(item, seed)
# set the bit at the hash mod the bitfield size (self.size*8)
bit = h % (self.size * 8)
# set the bit field at bit to be 1
self.bit_field[bit] = 1
测试
无
练习4
p204
代码实现
# -*- coding: utf-8 -*-
# @Author: 从化北(喵星人)
# @Date: 2022-01-17 15:26:42
# @Last Modified by: 从化北
# @Last Modified time: 2022-01-17 15:26:42
class BloomFilter:
...
def filterload(self, flag=1):
'''Return the filterload message'''
# start the payload with the size of the filter in bytes
payload = encode_varint(self.size)
# next add the bit field using self.filter_bytes()
payload += self.filter_bytes()
# function count is 4 bytes little endian
payload += int_to_little_endian(self.function_count, 4)
# tweak is 4 bytes little endian
payload += int_to_little_endian(self.tweak, 4)
# flag is 1 byte little endian
payload += int_to_little_endian(flag, 1)
# return a GenericMessage whose command is b'filterload'
# and payload is what we've calculated
return GenericMessage(b'filterload', payload)
运行结果
无
练习5
p205
代码实现
# -*- coding: utf-8 -*-
# @Author: 从化北(喵星人)
# @Date: 2022-01-17 15:30:50
# @Last Modified by: 从化北
# @Last Modified time: 2022-01-17 15:30:50
class GetDataMessage:
...
def serialize(self):
# start with the number of items as a varint
result = encode_varint(len(self.data))
# loop through each tuple (data_type, identifier) in self.data
for data_type, identifier in self.data:
# data type is 4 bytes Little-Endian
result += int_to_little_endian(data_type, 4)
# identifier needs to be in Little-Endian
result += identifier[::-1]
return result
运行结果
无
练习6
p206
代码实现
# -*- coding: utf-8 -*-
# @Author: 从化北(喵星人)
# @Date: 2022-01-17 19:35:47
# @Last Modified by: 从化北
# @Last Modified time: 2022-01-17 20:10:25
import time
from block import Block
from bloomfilter import BloomFilter
from ecc import PrivateKey
from helper import(
decode_base58,
encode_varint,
hash256,
little_endian_to_int,
read_varint,
)
from merkleblock import MerkleBlock
from network import(
GetDataMessage,
GetHeadersMessage,
HeadersMessage,
NetworkEnvelope,
SimpleNode,
TX_DATA_TYPE,
FILTERED_BLOCK_DATA_TYPE,
)
from script import p2pkh_script, Script
from tx import Tx, TxIn, TxOut
last_block_hex = '000000000000001516868f7978c999c087861a5f61302b660a226f7d2deffba6'
secret = little_endian_to_int(hash256(b'***********'))
private_key = PrivateKey(secret=secret)
addr = private_key.point.address(testnet=True)
h160 = decode_base58(addr)
target_address = 'mvgTF5toE4Y7d59ibvq5E7fMka7uiAYprs'
target_h160 = decode_base58(target_address)
target_script = p2pkh_script(target_h160)
fee = 5000
node = SimpleNode('testnet.programmingbitcoin.com', testnet=True, logging=False)
bf = BloomFilter(30, 5, 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)
last_block = None
getdata = GetDataMessage()
for b in headers.blocks:
if not b.check_pow():
raise RuntimeError('proof of work is invalid')
if last_block is not None and b.prev_block != last_block:
raise RuntimeError('chain broken')
getdata.add_data(FILTERED_BLOCK_DATA_TYPE, b.hash())
last_block = b.hash()
node.send(getdata)
prev_tx, prev_index, prev_amount = None, None, None
while prev_tx is None:
message = node.wait_for(MerkleBlock, Tx)
if message.command == b'merkleblock':
if not message.is_valid():
raise RuntimeError('invalid merkle proof')
else:
message.testnet = True
for i, tx_out in enumerate(message.tx_outs):
if tx_out.script_pubkey.address(testnet=True) == addr:
prev_tx = message.hash()
prev_index = i
prev_amount = tx_out.amount
print('found: {}:{}'.format(prev_tx.hex(), prev_index))
tx_in = TxIn(prev_tx, prev_index)
output_amount = prev_amount - fee
tx_out = TxOut(output_amount, target_script)
tx_obj = Tx(1, [tx_in], [tx_out], 0, testnet=True)
print(tx_obj.sign_input(0, private_key))
print(tx_obj.serialize().hex())
node.send(tx_obj)
time.sleep(1)
getdata = GetDataMessage()
getdata.add_data(TX_DATA_TYPE, tx_obj.hash())
node.send(getdata)
received_tx = node.wait_for(Tx)
if received_tx.id() == tx_obj.id():
print("success!")
运行结果
found: fe696aa4947ed9a02b27f65bdfd0d01a5ffea85924df34a8afa04efc5ea2c861:1
True
010000000161c8a25efc4ea0afa834df2459a8fe5f1ad0d0df5bf6272ba0d97e94a46a69fe010000006a473044022055d412f7b16c2ffd3ec68f2b19c0189087e2dd1ef9a988559e68c43858db3092022056683ab8e0b0675198acec2b30b4082e54c2cd06ed986dcee6118d53bce28962012103ee84c2eb9f347de0cc501f3b109eb14e93ee0d77f60c9eb61993cb5a45f1d95dffffffff0188130000000000001976a914a655cbb0b31bcd08157a6aeecc5d76a2e5be2bbe88ac00000000
[Finished in 5.6s]
说明
测试6中,的success没有提示。
我后续实验了一下, received_tx.id()和tx_obj.id() 并不一致。
然后我又运行了一下给的example。 结果是一致的。
不知道为什么。好难受
还有, getdata的payload中,第三个Hash identifier 代表什么? 我本以为它代表想要获得数据的哈希值。 但是好像并不是那样,我试验了一些,还是没搞懂。哎。
我在GitHub上提交了一个issue,有人知道的话,快来救救孩子吧。