def deserialize_message_header(cls, header): global checksum stream = io.BytesIO(header) magic = ut.bytes_to_hex(stream.read(4)) command = stream.read(12).decode('ascii').strip('\x00') length = bitcoinx.read_le_uint32(stream.read) try: checksum = ut.bytes_to_hex(stream.read(4)) decoded_header = {'magic': magic, 'command': command, 'length': length, 'checksum': checksum, } return decoded_header except Exception as e: print(e) print('reading in checksum failed...')
def create_p2pkh_transaction(private_key, unspents, outputs, custom_pushdata=False): public_key = private_key.public_key public_key_len = len(public_key).to_bytes(1, byteorder="little") scriptCode = private_key.scriptcode scriptCode_len = int_to_varint(len(scriptCode)) version = VERSION_1 lock_time = LOCK_TIME # sequence = SEQUENCE hash_type = HASH_TYPE input_count = int_to_unknown_bytes(len(unspents), byteorder="little") output_count = int_to_unknown_bytes(len(outputs), byteorder="little") output_block = construct_output_block(outputs, custom_pushdata=custom_pushdata) # Optimize for speed, not memory, by pre-computing values. inputs = [] for unspent in unspents: script = hex_to_bytes(unspent.script) script_len = int_to_unknown_bytes(len(script), byteorder="little") txid = hex_to_bytes(unspent.txid)[::-1] txindex = unspent.txindex.to_bytes(4, byteorder="little") amount = unspent.amount.to_bytes(8, byteorder="little") inputs.append(TxIn(script, script_len, txid, txindex, amount)) hashPrevouts = double_sha256(b"".join([i.txid + i.txindex for i in inputs])) hashSequence = double_sha256(b"".join([SEQUENCE for i in inputs])) hashOutputs = double_sha256(output_block) # scriptCode_len is part of the script. for i, txin in enumerate(inputs): to_be_hashed = (version + hashPrevouts + hashSequence + txin.txid + txin.txindex + scriptCode_len + scriptCode + txin.amount + SEQUENCE + hashOutputs + lock_time + hash_type) hashed = sha256(to_be_hashed) # BIP-143: Used for Bitcoin Cash # signature = private_key.sign(hashed) + b'\x01' signature = private_key.sign(hashed) + b"\x41" script_sig = (len(signature).to_bytes(1, byteorder="little") + signature + public_key_len + public_key) inputs[i].script = script_sig inputs[i].script_len = int_to_unknown_bytes(len(script_sig), byteorder="little") return bytes_to_hex(version + input_count + construct_input_block(inputs) + output_count + output_block + lock_time)
def _tx_out_boilerplate(tx_in, unlocking_script, output_block): tx_in.script = unlocking_script tx_in.script_len = int_to_unknown_bytes(len(unlocking_script), byteorder='little') input_count = int_to_unknown_bytes(1, byteorder='little') output_count = int_to_unknown_bytes(1, byteorder='little') return bytes_to_hex(VERSION_1 + input_count + construct_input_block([tx_in]) + output_count + output_block + LOCK_TIME)
def deserialize_reject(cls, f): message = bitcoinx.packing.read_varbytes(f.read) ccode = f.read(1) reason = bitcoinx.packing.read_varbytes(f.read) # TODO different ccodes will / won't have "extra data" add mapping to CCODES in simple_spv.constants # data = bitcoinx.packing.read_varbytes(f.read) # no data ccode_translation = CCODES['0x' + ut.bytes_to_hex(ccode)] return message, ccode_translation, reason
def calc_txid(tx_hex): return bytes_to_hex(double_sha256(hex_to_bytes(tx_hex))[::-1])
def test_flip_hex_byte_order(): assert flip_hex_byte_order(bytes_to_hex(BYTES_LITTLE)) == HEX
def test_upper(self): assert bytes_to_hex(BYTES_BIG, upper=True) == HEX.upper()
def test_default(self): assert bytes_to_hex(BYTES_BIG) == HEX
def test_correct(self): assert bytes.fromhex(bytes_to_hex(BYTES_BIG)) == BYTES_BIG
def deserialize_all_messages(self): """This method is called inside the main spv_daemon loop and deserializes all messages in self.buffer and will independently recv the rest of the buffer if needed for long messages. For example "headers" with up to 2000 headers in a single message.""" messages = [] # deserialize all messages in buffer and if incomplete messages remain, recv more buffer until complete while self.buffer.getbuffer().nbytes > 0: # Calculate the size of the buffer self.buffer.seek(0, os.SEEK_END) buffer_size = self.buffer.tell() # Check if a complete message header is present if buffer_size < struct.calcsize("i 12s i i"): print("incomplete header") self.buffer.seek(0, os.SEEK_END) self.buffer.write(self.receive_raw_bytes(1024 * 8)) continue # return to top of loop to try again with hopefully complete header # check if message begins with network magic as expected self.buffer.seek(0) if self.buffer.read(4) == BITCOIN_SV_MAGIC: pass else: raise ValueError( "message does not begin with bitcoin SV network magic: 0xe3e1f3e8") # should never happen # Go to the beginning of the buffer self.buffer.seek(0) # Deserialize header try: header_bytes = self.buffer.read(struct.calcsize("i 12s i i")) message_header = Deserialize.deserialize_message_header(header_bytes) except Exception as e: print(e) print("couldn't deserialize message header") total_length = len(header_bytes) + message_header['length'] # Check if complete message is present if buffer_size < total_length: # print('buffer size less than total length of message') self.buffer.seek(0, os.SEEK_END) # puts buffer to end so that recv(1024*8) is appended to the end self.buffer.write(self.receive_raw_bytes(1024 * 8)) continue # go directly to top of loop to try again with hopefully complete message # Read in payload payload = self.buffer.read(message_header['length']) # Re-initialise self.buffer with remainder only (clips away processed message) self.buffer = io.BytesIO(self.buffer.read()) payload_checksum = ut.bytes_to_hex(simple_spv.tools.checksum(payload)) # Check if the checksum is valid if payload_checksum == message_header['checksum']: with open('logger.txt', 'a') as outfile: outfile.write('checksums match for: ' + str(message_header['command']) + "\n") # Call appropriate deserialization function from deserializers class message_header_command = message_header['command'] deser_func_name = "deserialize_" + message_header_command if deser_func_name == "deserialize_pong": pass else: print(deser_func_name) deser_func = getattr(Deserialize, deser_func_name, None) f = io.BytesIO(payload) message = deser_func(f) # print(message) else: raise ValueError("something went wrong with deserialization of payload") messages.append((message_header, message)) # Success # Return messages for handling in main loop return messages