def test_parse_merkleblock(): hex_merkle_block = '00000020df3b053dc46f162a9b00c7f0d5124e2676d47bbe7c5d0793a500000000000000ef445fef2ed495c275892206ca533e7411907971013ab83e3b47bd0d692d14d4dc7c835b67d8001ac157e670bf0d00000aba412a0d1480e370173072c9562becffe87aa661c1e4a6dbc305d38ec5dc088a7cf92e6458aca7b32edae818f9c2c98c37e06bf72ae0ce80649a38655ee1e27d34d9421d940b16732f24b94023e9d572a7f9ab8023434a4feb532d2adfc8c2c2158785d1bd04eb99df2e86c54bc13e139862897217400def5d72c280222c4cbaee7261831e1550dbb8fa82853e9fe506fc5fda3f7b919d8fe74b6282f92763cef8e625f977af7c8619c32a369b832bc2d051ecd9c73c51e76370ceabd4f25097c256597fa898d404ed53425de608ac6bfe426f6e2bb457f1c554866eb69dcb8d6bf6f880e9a59b3cd053e6c7060eeacaacf4dac6697dac20e4bd3f38a2ea2543d1ab7953e3430790a9f81e1c67f5b58c825acf46bd02848384eebe9af917274cdfbb1a28a5d58a23a17977def0de10d644258d9c54f886d47d293a411cb6226103b55635' mb = network.parse_merkleblock(BytesIO(bytes.fromhex(hex_merkle_block))) assert mb['version'] == 0x20000000 merkle_root_hex = 'ef445fef2ed495c275892206ca533e7411907971013ab83e3b47bd0d692d14d4' merkle_root = bytes.fromhex(merkle_root_hex)[::-1] assert mb['merkle_root'] == merkle_root prev_block_hash_hex = 'df3b053dc46f162a9b00c7f0d5124e2676d47bbe7c5d0793a500000000000000' prev_block_hash = bytes.fromhex(prev_block_hash_hex)[::-1] assert mb['prev_block_hash'] == prev_block_hash timestamp = util.little_endian_to_int(bytes.fromhex('dc7c835b')) assert mb['timestamp'] == timestamp bits = bytes.fromhex('67d8001a') assert mb['bits'] == bits nonce = bytes.fromhex('c157e670') assert mb['nonce'] == nonce total = util.little_endian_to_int(bytes.fromhex('bf0d0000')) assert mb['total'] == total hex_hashes = [ 'ba412a0d1480e370173072c9562becffe87aa661c1e4a6dbc305d38ec5dc088a', '7cf92e6458aca7b32edae818f9c2c98c37e06bf72ae0ce80649a38655ee1e27d', '34d9421d940b16732f24b94023e9d572a7f9ab8023434a4feb532d2adfc8c2c2', '158785d1bd04eb99df2e86c54bc13e139862897217400def5d72c280222c4cba', 'ee7261831e1550dbb8fa82853e9fe506fc5fda3f7b919d8fe74b6282f92763ce', 'f8e625f977af7c8619c32a369b832bc2d051ecd9c73c51e76370ceabd4f25097', 'c256597fa898d404ed53425de608ac6bfe426f6e2bb457f1c554866eb69dcb8d', '6bf6f880e9a59b3cd053e6c7060eeacaacf4dac6697dac20e4bd3f38a2ea2543', 'd1ab7953e3430790a9f81e1c67f5b58c825acf46bd02848384eebe9af917274c', 'dfbb1a28a5d58a23a17977def0de10d644258d9c54f886d47d293a411cb62261', ] hashes = [bytes.fromhex(h)[::-1] for h in hex_hashes] mb['hashes'] == hashes flags = bytes.fromhex('b55635') assert mb['flags'] == flags
def parse_segwit(s): tx = { "version": 1, "mark": None, "flag": None, "txi.count": 0, "txis": [], "txo.count": 0, "txos": [], # "wits": append to each txi "locktime": 0, "hash_all": None } tx["version"] = util.little_endian_to_int(s.read(4)) tx["mark"] = s.read(1) tx["flag"] = s.read(1) if tx["mark"] != b"\x00" and tx["flag"] != b"\x01": raise RuntimeError('not a segwit transaction {}'.format(tx["flag"])) tx["txi.count"] = util.read_varint(s) for _ in range(tx["txi.count"]): tx["txis"].append(parse_txi(s)) tx["txo.count"] = util.read_varint(s) for _ in range(tx["txo.count"]): tx["txos"].append(parse_txo(s)) # parse witness script for txi in tx["txis"]: count = util.read_varint(s) for _ in range(count): length = util.read_varint(s) if length == 0: # ? txi["wits"].append(0) else: txi["wits"].append(s.read(length)) tx["locktime"] = util.little_endian_to_int(s.read(4)) # locktime is an integer in 4 bytes, little-endian return tx
def test_little_endian_to_int(): h = bytes.fromhex('99c3980000000000') want = 10011545 assert util.little_endian_to_int(h) == want h = bytes.fromhex('a135ef0100000000') want = 32454049 assert util.little_endian_to_int(h) == want
def parse(s): result = {} result['version'] = util.little_endian_to_int(s.read(4)) result['prev_block_hash'] = s.read(32)[::-1] result['merkle_root'] = s.read(32)[::-1] result['timestamp'] = util.little_endian_to_int(s.read(4)) result['bits'] = s.read(4) result['nonce'] = s.read(4) return result
def parse_txi(s): txi = { "txid": 0, # 32bytes, not hex value. "idx": 0, "script.len": 0, "script": [], "seq.no": 0, "wits": [] } txi["txid"] = s.read(32)[::-1] # prev_tx is 32 bytes, little endian // le to be txi["idx"] = util.little_endian_to_int(s.read(4)) # prev_index is an integer in 4 bytes, little endian txi["script.len"], txi["script"] = script.parse(s) # parse script txi["seq.no"] = util.little_endian_to_int(s.read(4)) # sequence is an integer in 4 bytes, little-endian # wits: witness script return txi
def parse_msg(s, testnet=False): """Takes a stream and creates a NetworkEnvelope""" # check the network magic magic = s.read(4) if magic == b'': raise RuntimeError('Connection reset!') if testnet: expected_magic = TESTNET_MAGIC else: expected_magic = MAINNET_MAGIC if magic != expected_magic: raise RuntimeError('magic is not right {} vs {}'.format( magic.hex(), expected_magic.hex())) # command 12 bytes command = s.read(12) # strip the trailing 0's command = command.strip(b'\x00') # payload length 4 bytes, little endian payload_length = util.little_endian_to_int(s.read(4)) # checksum 4 bytes, first four of hash256 of payload checksum = s.read(4) # payload is of length payload_length payload_bytes = s.read(payload_length) # verify checksum calculated_checksum = util.hash256(payload_bytes)[:4] if calculated_checksum != checksum: raise RuntimeError('checksum does not match') # return msg return {"magic": magic, "command": command, "payload_bytes": payload_bytes}
def check_pow(obj): """Returns whether this block satisfies proof of work""" # get the hash256 of the serialization of this block h256 = util.hash256(serialize(obj)) # interpret this hash as a little-endian number proof = util.little_endian_to_int(h256) # return whether this integer is less than the target return proof < target(obj)
def bits_to_target(bits): """Turns bits into a target (large 256-bit integer)""" # last byte is exponent exponent = bits[-1] # the first three bytes are the coefficient in little endian coefficient = util.little_endian_to_int(bits[:-1]) # the formula is: # coefficient * 256**(exponent-3) return coefficient * 256**(exponent - 3)
def parse_txo(s): txo = { "amount": 0, "script.len": 0, "script": "" } # amount is an integer in 8 bytes, little endian (amount = satoshi * 10**8, 1 satoshi = 0.00000001btc) txo["amount"] = util.little_endian_to_int(s.read(8)) txo["script.len"], txo["script"] = script.parse(s) # lock script return txo
def parse_legacy(s): tx = { "version": 1, # "mark": None, # "flag": None, "txi.count": 0, "txis": [], "txo.count": 0, "txos": [], # "wits": None, "locktime": 0 } tx["version"] = util.little_endian_to_int(s.read(4)) tx["txi.count"] = util.read_varint(s) for _ in range(tx["txi.count"]): tx["txis"].append(parse_txi(s)) tx["txo.count"] = util.read_varint(s) for _ in range(tx["txo.count"]): tx["txos"].append(parse_txo(s)) tx["locktime"] = util.little_endian_to_int(s.read(4)) # locktime is an integer in 4 bytes, little-endian return tx
def parse_merkleblock(s): """Takes a byte stream and parses a merkle block. Returns a Merkle Block object""" # version - 4 bytes, Little-Endian integer version = util.little_endian_to_int(s.read(4)) # prev_block - 32 bytes, Little-Endian (use [::-1]) prev_block_hash = s.read(32)[::-1] # merkle_root - 32 bytes, Little-Endian (use [::-1]) merkle_root = s.read(32)[::-1] # timestamp - 4 bytes, Little-Endian integer timestamp = util.little_endian_to_int(s.read(4)) # bits - 4 bytes bits = s.read(4) # nonce - 4 bytes nonce = s.read(4) # total transactions in block - 4 bytes, Little-Endian integer total = util.little_endian_to_int(s.read(4)) # number of transaction hashes - varint num_hashes = util.read_varint(s) # each transaction is 32 bytes, Little-Endian hashes = [] for _ in range(num_hashes): hashes.append(s.read(32)[::-1]) # length of flags field - varint flags_length = util.read_varint(s) # read the flags field flags = s.read(flags_length) return { 'version': version, 'prev_block_hash': prev_block_hash, 'merkle_root': merkle_root, 'timestamp': timestamp, 'bits': bits, 'nonce': nonce, 'total': total, 'hashes': hashes, 'flags': flags }
def parse(s): length = util.read_varint(s) commands = [] # initialize the commands array count = 0 # initialize the number of bytes we've read to 0 while count < length: # loop until we've read length bytes temp = s.read(1)[0] # get code count += 1 # increment the bytes we've read if 1 <= temp <= 75: # if the current byte is between 1 and 75 inclusive commands.append(s.read(temp)) # add the next n bytes as an cmd count += temp # increase the count by n elif temp == 76: # op_pushdata1 data_length = util.little_endian_to_int(s.read(1)) commands.append(s.read(data_length)) count += data_length + 1 elif temp == 77: # op_pushdata2 data_length = util.little_endian_to_int(s.read(2)) commands.append(s.read(data_length)) count += data_length + 2 else: # we have an op_code. set the current byte to op_code commands.append(temp) # add the op_code to the list of commands if count != length: raise SyntaxError('parsing script failed') return length, commands