def test_block_parsing_and_serialization(self): tx_in = TxIn( prev_tx=b'\x00' * 32, prev_index=0xffffffff, script_sig=Script([b'muhhh coinz']), ) sk = create_sk(100) sec = sk.verifying_key.to_sec(compressed=False) tx_out = TxOut( amount=50 * 100_000_000, script_pubkey=p2pk_script(sec), ) coinbase = Tx( version=1, tx_ins=[tx_in], tx_outs=[tx_out], locktime=0, ) block = mine( Block(version=1, prev_block=genesis.hash(), merkle_root=merkle_root([coinbase.hash()]), timestamp=int(time.time()), bits=starting_bits, nonce=b'\x00\x00\x00\x00', txns=[coinbase])) assert Block.parse(BytesIO(block.serialize())).hash() == block.hash()
def insufficient_proof(bitcoin_node): block = Block( version=1, prev_block=bitcoin_node.headers[-1].hash(), merkle_root=merkle_root([b'xyz']), # FIXME timestamp=int(time.time()), bits=starting_bits, nonce=b'\x00\x00\x00\x00', txns=[prepare_coinbase(bob_sec)]) assert not block.check_pow() valid = False hints = make_hints([ f'Proof-of-Work not satisfied', ]) return block, valid, hints
def spend_nonexistant_output(bitcoin_node): coinbase = prepare_coinbase(bob_sec) tx_in = TxIn( prev_tx=urandom(32), prev_index=0, script_sig=p2pk_script(bob_sec), ) tx_out = TxOut( amount=50 * 100_000_000, script_pubkey=p2pk_script(bob_sec), ) bad_spend = Tx( version=1, tx_ins=[tx_in], tx_outs=[tx_out], locktime=0, ) block = mine( Block( version=1, prev_block=bitcoin_node.headers[-1].hash(), merkle_root=merkle_root([coinbase.hash(), bad_spend.hash()]), # FIXME timestamp=int(time.time()), bits=starting_bits, nonce=b'\x00\x00\x00\x00', txns=[coinbase, bad_spend], )) valid = False hints = make_hints([ 'this transaction spends an output that does not exist', ]) # FIXME: check that utxo_set was updated return block, valid, hints
def bad_coinbase(bitcoin_node): # this scenario sucks b/c we test multiple things and it's unclear whether the latter # checks are even happening tx_in = TxIn( prev_tx=b'\x00' * 32, prev_index=0xffffffff, script_sig=p2pk_script(urandom(10)), ) tx_out = TxOut( amount=100 * 100_000_000, script_pubkey=p2pk_script(bob_sec), ) coinbase = Tx( version=1, tx_ins=[tx_in], tx_outs=[tx_out], locktime=0, ) block = mine( Block( version=1, prev_block=bitcoin_node.headers[-1].hash(), merkle_root=merkle_root([coinbase.hash()]), # FIXME timestamp=int(time.time()), bits=starting_bits, nonce=b'\x00\x00\x00\x00', txns=[coinbase])) valid = False hints = make_hints([ "Bad coinbase", ]) return block, valid, hints
def wrong_bits(bitcoin_node): block = mine( Block(version=1, prev_block=bitcoin_node.headers[-1].hash(), merkle_root=merkle_root([b'xyz']), timestamp=int(time.time()), bits=target_to_bits(16**63), nonce=b'\x00\x00\x00\x00', txns=[prepare_coinbase(bob_sec)])) valid = False hints = make_hints([ f'Block.bits should be {repr(starting_bits)}', ]) return block, valid, hints
def missing_coinbase(bitcoin_node): block = mine( Block( version=1, prev_block=bitcoin_node.headers[-1].hash(), merkle_root=merkle_root([b'xyz']), # FIXME timestamp=int(time.time()), bits=starting_bits, nonce=b'\x00\x00\x00\x00', txns=[])) valid = False hints = make_hints([ "Look block.txns", "Coinbase is missing", ]) return block, valid, hints
def good_coinbase(bitcoin_node): coinbase = prepare_coinbase(bob_sec) block = mine( Block( version=1, prev_block=bitcoin_node.headers[-1].hash(), merkle_root=merkle_root([coinbase.hash()]), # FIXME timestamp=int(time.time()), bits=starting_bits, nonce=b'\x00\x00\x00\x00', txns=[coinbase])) valid = True hints = make_hints([ 'We need to update the utxo set', ]) # FIXME: check that utxo_set was updated return block, valid, hints
def first_valid_spend(bitcoin_node): coinbase = prepare_coinbase(bob_sec) assert len(list(bitcoin_node.utxo_set.keys())) == 1 outpoint = list(bitcoin_node.utxo_set.keys())[0] # FIXME utxo = bitcoin_node.utxo_set[outpoint] tx_in = TxIn( prev_tx=bytes.fromhex(outpoint[0]), prev_index=outpoint[1], ) tx_out = TxOut( amount=utxo.amount - 10, script_pubkey=p2pk_script(bob_sec), ) bad_spend = Tx( version=1, tx_ins=[tx_in], tx_outs=[tx_out], locktime=0, ) ### HACKS!!! ### for i in range(len(bad_spend.tx_ins)): bad_spend.sign_input(i, bob_sk, utxo.script_pubkey) ### /HACKS!!! ### block = mine( Block( version=1, prev_block=bitcoin_node.headers[-1].hash(), merkle_root=merkle_root([coinbase.hash(), bad_spend.hash()]), # FIXME timestamp=int(time.time()), bits=starting_bits, nonce=b'\x00\x00\x00\x00', txns=[coinbase, bad_spend], )) valid = True hints = make_hints([ 'did utxo set update?', ]) # FIXME: check that utxo_set was updated return block, valid, hints
def parse(cls, stream): block = Block.parse(stream) return cls(block)
def parse(cls, stream): return Block.parse(stream)
from io import BytesIO from solutions.network import PeerConnection, GetHeadersMessage, HeadersMessage, GetDataMessage, BlockMessage from solutions.block import RAW_GENESIS_BLOCK, BlockHeader, Block from lib import target_to_bits GENESIS_HEADER = BlockHeader.parse(BytesIO(RAW_GENESIS_BLOCK)) GENESIS_BLOCK = Block.parse(BytesIO(RAW_GENESIS_BLOCK)) starting_bits = target_to_bits(16**62) class BitcoinNode: def __init__(self): self.headers = [GENESIS_HEADER] self.blocks = [GENESIS_BLOCK] self.utxo_set = {} self.peer = None def connect(self, host, port): self.peer = PeerConnection(host, port) self.peer.handshake() def receive_header(self, header): # TODO: verify hash matches # TODO: check proof-of-work # append block headers received to headers array previous = self.headers[-1] if header.prev_block != previous.hash(): raise RuntimeError('discontinuous block at {}'.format( len(self.headers)))