def output_value_to_bytes(number: int) -> bytes: if number <= 0: raise InvalidOutputValue('Invalid value for output') if number > _MAX_OUTPUT_VALUE_32: return (-number).to_bytes(8, byteorder='big', signed=True) else: return number.to_bytes( 4, byteorder='big', signed=True) # `signed` makes no difference, but oh well
def bytes_to_output_value(buf: bytes) -> Tuple[int, bytes]: (value_high_byte, ), _ = unpack('!b', buf) if value_high_byte < 0: output_struct = '!q' value_sign = -1 else: output_struct = '!i' value_sign = 1 try: (signed_value, ), buf = unpack(output_struct, buf) except StructError as e: raise InvalidOutputValue('Invalid byte struct for output') from e value = signed_value * value_sign assert value >= 0 if value < _MAX_OUTPUT_VALUE_32 and value_high_byte < 0: raise ValueError('Value fits in 4 bytes but is using 8 bytes') return value, buf
def update_token_info_from_outputs( self, token_dict: Dict[bytes, TokenInfo]) -> None: """Iterate over the outputs and add values to token info dict. Updates the dict in-place. Also, checks if no token has authorities on the outputs not present on the inputs :raises InvalidToken: when there's an error in token operations """ # iterate over outputs and add values to token_dict for index, tx_output in enumerate(self.outputs): if tx_output.value <= 0: raise InvalidOutputValue( 'Output value must be a positive integer. Value: {} and index: {}' .format(tx_output.value, index)) token_uid = self.get_token_uid(tx_output.get_token_index()) token_info = token_dict.get(token_uid) if token_info is None: raise InvalidToken('no inputs for token {}'.format( token_uid.hex())) else: # for authority outputs, make sure the same capability (mint/melt) was present in the inputs if tx_output.can_mint_token() and not token_info.can_mint: raise InvalidToken( 'output has mint authority, but no input has it: {}'. format(tx_output.to_human_readable())) if tx_output.can_melt_token() and not token_info.can_melt: raise InvalidToken( 'output has melt authority, but no input has it: {}'. format(tx_output.to_human_readable())) if tx_output.is_token_authority(): # make sure we only have authorities that we know of if tx_output.value > TxOutput.ALL_AUTHORITIES: raise InvalidToken( 'Invalid authorities in output (0b{0:b})'.format( tx_output.value)) else: # for regular outputs, just subtract from the total amount sum_tokens = token_info.amount + tx_output.value token_dict[token_uid] = TokenInfo(sum_tokens, token_info.can_mint, token_info.can_melt)
def verify_outputs(self) -> None: """Verify there are no hathor authority UTXOs and outputs are all positive :raises InvalidToken: when there's a hathor authority utxo :raises InvalidOutputValue: output has negative value :raises TooManyOutputs: when there are too many outputs """ self.verify_number_of_outputs() for index, output in enumerate(self.outputs): # no hathor authority UTXO if (output.get_token_index() == 0) and output.is_token_authority(): raise InvalidToken( 'Cannot have authority UTXO for hathor tokens: {}'.format( output.to_human_readable())) # output value must be positive if output.value <= 0: raise InvalidOutputValue( 'Output value must be a positive integer. Value: {} and index: {}' .format(output.value, index))