def test_output_value(self): from hathor.transaction.base_transaction import bytes_to_output_value # first test using a small output value with 8 bytes. It should fail parents = [tx.hash for tx in self.genesis_txs] outputs = [TxOutput(1, b'')] tx = Transaction(outputs=outputs, parents=parents) original_struct = tx.get_struct() struct_bytes = tx.get_funds_struct() # we'll get the struct without the last output bytes and add it ourselves struct_bytes = struct_bytes[:-7] # add small value using 8 bytes and expect failure when trying to deserialize struct_bytes += (-1).to_bytes(8, byteorder='big', signed=True) struct_bytes += int_to_bytes(0, 1) struct_bytes += int_to_bytes(0, 2) struct_bytes += tx.get_graph_struct() struct_bytes += int_to_bytes(tx.nonce, tx.SERIALIZATION_NONCE_SIZE) len_difference = len(struct_bytes) - len(original_struct) assert len_difference == 4, 'new struct is incorrect, len difference={}'.format(len_difference) with self.assertRaises(ValueError): Transaction.create_from_struct(struct_bytes) # now use 8 bytes and make sure it's working outputs = [TxOutput(MAX_OUTPUT_VALUE, b'')] tx = Transaction(outputs=outputs, parents=parents) tx.update_hash() original_struct = tx.get_struct() tx2 = Transaction.create_from_struct(original_struct) tx2.update_hash() assert tx == tx2 # Validating that all output values must be positive value = 1 address = decode_address('WUDtnw3GYjvUnZmiHAmus6hhs9GoSUSJMG') script = P2PKH.create_output_script(address) output = TxOutput(value, script) output.value = -1 tx = Transaction(inputs=[], outputs=[output], parents=parents, storage=self.tx_storage) with self.assertRaises(InvalidOutputValue): tx.resolve() # 'Manually resolving', to validate verify method tx.hash = bytes.fromhex('012cba011be3c29f1c406f9015e42698b97169dbc6652d1f5e4d5c5e83138858') with self.assertRaises(InvalidOutputValue): tx.verify() # Invalid output value invalid_output = bytes.fromhex('ffffffff') with self.assertRaises(InvalidOutputValue): bytes_to_output_value(invalid_output) # Can't instantiate an output with negative value with self.assertRaises(AssertionError): TxOutput(-1, script)
def __bytes__(self) -> bytes: """Returns a byte representation of the input :rtype: bytes """ ret = b'' ret += self.tx_id ret += int_to_bytes(self.index, 1) ret += int_to_bytes(len(self.data), 2) # data length ret += self.data return ret
def __bytes__(self) -> bytes: """Returns a byte representation of the output :rtype: bytes """ ret = b'' ret += output_value_to_bytes(self.value) ret += int_to_bytes(self.token_data, 1) ret += int_to_bytes(len(self.script), 2) # script length ret += self.script return ret
def get_sighash_bytes(self) -> bytes: """Return a serialization of the input for the sighash. It always clears the input data. :return: Serialization of the input :rtype: bytes """ ret = bytearray() ret += self.tx_id ret += int_to_bytes(self.index, 1) ret += int_to_bytes(0, 2) return bytes(ret)
def serialize_token_info(self) -> bytes: """ Returns the serialization for token name and symbol """ encoded_name = self.token_name.encode('utf-8') encoded_symbol = self.token_symbol.encode('utf-8') ret = b'' ret += int_to_bytes(TOKEN_INFO_VERSION, 1) ret += int_to_bytes(len(encoded_name), 1) ret += encoded_name ret += int_to_bytes(len(encoded_symbol), 1) ret += encoded_symbol return ret
def get_sighash_bytes(self, clear_data: bool) -> bytes: """Return a serialization of the input for the sighash :return: Serialization of the input :rtype: bytes """ if not clear_data: return bytes(self) else: ret = b'' ret += self.tx_id ret += int_to_bytes(self.index, 1) ret += int_to_bytes(0, 2) return ret
def get_graph_struct(self) -> bytes: """Return the graph data serialization of the block, without including the nonce field :return: graph data serialization of the transaction :rtype: bytes """ struct_bytes_without_data = super().get_graph_struct() data_bytes = int_to_bytes(len(self.data), 1) return struct_bytes_without_data + data_bytes + self.data
def get_struct_nonce(self) -> bytes: """Return a partial serialization of the transaction's proof-of-work, which is usually the nonce field :return: Partial serialization of the transaction's proof-of-work :rtype: bytes """ assert self.SERIALIZATION_NONCE_SIZE is not None struct_bytes = int_to_bytes(self.nonce, self.SERIALIZATION_NONCE_SIZE) return struct_bytes
def test_token_info_not_utf8(self): token_name = 'TestCoin' token_symbol = 'TST' # Token version 1; Name length; Name; Symbol length; Symbol bytes1 = (bytes([0x01]) + int_to_bytes(len(token_name), 1) + token_name.encode('utf-8') + int_to_bytes(len(token_symbol), 1) + token_symbol.encode('utf-8')) name, symbol, _ = TokenCreationTransaction.deserialize_token_info( bytes1) self.assertEqual(name, token_name) self.assertEqual(symbol, token_symbol) encoded_name = token_name.encode('utf-16') bytes2 = (bytes([0x01]) + int_to_bytes(len(encoded_name), 1) + encoded_name + int_to_bytes(len(token_symbol), 1) + token_symbol.encode('utf-8')) with self.assertRaises(StructError): TokenCreationTransaction.deserialize_token_info(bytes2) encoded_symbol = token_symbol.encode('utf-16') bytes3 = (bytes([0x01]) + int_to_bytes(len(token_name), 1) + token_name.encode('utf-8') + int_to_bytes(len(encoded_symbol), 1) + encoded_symbol) with self.assertRaises(StructError): TokenCreationTransaction.deserialize_token_info(bytes3)
def to_proto(self, include_metadata: bool = True) -> protos.BaseTransaction: tx_proto = protos.Block(version=self.version, weight=self.weight, timestamp=self.timestamp, parents=self.parents, outputs=map(TxOutput.to_proto, self.outputs), hash=self.hash, data=self.data) tx_proto.nonce = int_to_bytes(self.nonce, 16) if include_metadata: tx_proto.metadata.CopyFrom(self.get_metadata().to_proto()) return protos.BaseTransaction(block=tx_proto)