def gen_packet(cls, command, payload): version = utils.int2bytes(0x0209) command = utils.gen_command_bytes_array(command) length = utils.int2bytes(len(payload)) packet_uuid = uuid.uuid4().bytes checksum = hashlib.sha256(payload).digest()[:8] return cls(version, command, length, packet_uuid, checksum, payload)
def test_int2bytes(self): self.assertEqual(utils.int2bytes(0x12345678), '78563412'.decode('hex'), 'test int2bytes fail') self.assertEqual(utils.int2bytes(0xabcdef12), '12efcdab'.decode('hex'), 'test int2bytes fail') self.assertEqual(utils.int2bytes(0x0209), '09020000'.decode('hex'), 'test int2bytes fail')
def test_to_bytes(self): self.assertEqual( self.post_packet.to_bytes(), ''.join([ utils.int2bytes(0x0209), utils.gen_command_bytes_array('POST'), utils.int2bytes(2), self.post_packet.uuid, hashlib.sha256('{}').digest()[:8], '{}' ])) self.assertEqual( self.getdata_packet.to_bytes(), ''.join([ utils.int2bytes(0x0209), utils.gen_command_bytes_array('GETDATA'), utils.int2bytes(2), self.getdata_packet.uuid, hashlib.sha256('{}').digest()[:8], '{}' ])) self.assertEqual( self.data_packet.to_bytes(), ''.join([ utils.int2bytes(0x0209), utils.gen_command_bytes_array('DATA'), utils.int2bytes(2), self.data_packet.uuid, hashlib.sha256('{}').digest()[:8], '{}' ])) self.assertEqual( self.ack_packet.to_bytes(), ''.join([ utils.int2bytes(0x0209), utils.gen_command_bytes_array('ACK'), utils.int2bytes(2), self.ack_packet.uuid, hashlib.sha256('{}').digest()[:8], '{}' ]))
def add_fees(self, output=0, amount=None): """ Adds the chosen fees to the chosen output of the transaction. :param self: self :type self: TX :param output: The output where the fees will be charged. The first input will be chosen by default (output=0). :type output: int :param amount: The amount of fees to be charged. The minimum fees wil be charged by default (amount=None). :type amount: int. :return: The maximum size of the transaction. :rtype: int """ # Minimum fees will be applied if amount is None: fees = self.get_p2pkh_tx_max_len() * RECOMMENDED_MIN_TX_FEE else: fees = amount # Get the bitcoin amount from the chosen output, cast it into integer and subtract the fees. amount = int(change_endianness(self.value[output]), 16) - fees # Fill all the missing bytes up to the value length(8 bytes) and get the value back to its LE representation. self.value[output] = change_endianness(int2bytes(amount, 8)) # Update the hex representation of the transaction self.to_hex()
def test_to_bytes(self): self.assertEqual(self.post_packet.to_bytes(), ''.join( [utils.int2bytes(0x0209), utils.gen_command_bytes_array('POST'), utils.int2bytes(2), self.post_packet.uuid, hashlib.sha256('{}').digest()[:8], '{}'])) self.assertEqual(self.getdata_packet.to_bytes(), ''.join( [utils.int2bytes(0x0209), utils.gen_command_bytes_array('GETDATA'), utils.int2bytes(2), self.getdata_packet.uuid, hashlib.sha256('{}').digest()[:8], '{}'])) self.assertEqual(self.data_packet.to_bytes(), ''.join( [utils.int2bytes(0x0209), utils.gen_command_bytes_array('DATA'), utils.int2bytes(2), self.data_packet.uuid, hashlib.sha256('{}').digest()[:8], '{}'])) self.assertEqual(self.ack_packet.to_bytes(), ''.join( [utils.int2bytes(0x0209), utils.gen_command_bytes_array('ACK'), utils.int2bytes(2), self.ack_packet.uuid, hashlib.sha256('{}').digest()[:8], '{}']))
def build_p2pkh_std_tx(self, prev_tx_id, prev_out_index, value, scriptPubKey, scriptSig=None, fees=None): """ Builds a standard P2PKH transaction using default parameters such as version = 01000000 or nSequence = FFFFFFFF. :param self: self :type self: TX :param prev_tx_id: List of references to the previous transactions from where the current transaction will redeem some bitcoins. :type prev_tx_id: str list :param prev_out_index: List of references transaction output from where the funds will be redeemed. :type prev_out_index: int list :param value: List of value to be transferred to the desired destinations, in Satoshis (The difference between the total input value and the total output value will be charged as fee). :type value: int list :param scriptPubKey: List of scripts that will lock the outputs of the current transaction. :type scriptPubKey: hex str list :param scriptSig: List of scripts that will provide proof of fulfillment of the redeem conditions from the previous transactions (referenced by the prev_tx_id and prev_out_index). :type scriptSig: hex str list :return: None. :rtype: None """ # 4-byte version number (default: 01 little endian). self.version = "01000000" ############# # INPUTS # ############# # Number of inputs (varint, between 1-9 bytes long). n_inputs = len(prev_tx_id) self.inputs = encode_varint(n_inputs) # e.g "01" # Reference to the UTXO to redeem. # 32-byte hash of the previous transaction (little endian). for i in range(n_inputs): self.prev_tx_id.append(change_endianness(prev_tx_id[i])) # e.g "c7495bd4c5102d7e40c231279eaf9877e825364847ddebc34911f5a0f0d79ea5" # 4-byte output index (little endian). self.prev_out_index.append( change_endianness(int2bytes(prev_out_index[i], 4))) # e.g "00000000" # ScriptSig # The order in the tx is: scriptSig_len, scriptSig. # Temporary filled with "0" "0" for standard script transactions (Signature) for i in range(n_inputs): if scriptSig is None: self.scriptSig_len.append( "0" ) # Input script length (varint, between 1-9 bytes long) self.scriptSig.append("0") else: self.scriptSig_len.append(int2bytes(len(scriptSig[i]) / 2, 1)) # 4-byte sequence number (default:ffffffff). self.nSequence.append("ffffffff") ############# # OUTPUTS # ############# # Number of outputs (varint, between 1-9 bytes long). n_outputs = len(scriptPubKey) self.outputs = encode_varint(n_outputs) # e.g "01" # 8-byte field (64 bit integer) representing the amount of Satoshis to be spent (little endian). # 0.00349815 (UTXO value) - 0.00005000 (fee) = 0.00344815 BTC = 344815 (Satoshi) = ef4250 (Little endian) for i in range(n_outputs): self.value.append(change_endianness(int2bytes( value[i], 8))) # e.g "ef42050000000000" # Output script and its length (varint, between 1-9 bytes long) self.scriptPubKey_len.append( encode_varint(len(scriptPubKey[i]) / 2)) # e.g "06" self.scriptPubKey = scriptPubKey # e.g "010301029488" # 4-byte lock time field (default: 0) self.nLockTime = "00000000" self.to_hex() # ToDO: Define a proper way of selecting the fees self.add_fees()