Exemple #1
0
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
Exemple #2
0
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
Exemple #3
0
    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))