Esempio n. 1
0
def test_chunk_data():
    assert list(chunk_data(ODD_HEX, 2)) == [
        "4f",
        "ad",
        "d1",
        "97",
        "73",
        "28",
        "c1",
        "1e",
        "fc",
        "1c",
        "1d",
        "8a",
        "78",
        "1a",
        "a6",
        "b9",
        "67",
        "79",
        "84",
        "d3",
        "e0",
        "bd",
        "0b",
        "fc",
        "52",
        "b9",
        "f3",
        "b0",
        "38",
        "85",
        "a0",
        "0",
    ]
Esempio n. 2
0
def sanitize_tx_data(
    unspents,
    outputs,
    fee,
    leftover,
    combine=True,
    message=None,
    compressed=True,
    custom_pushdata=False,
):
    """
    sanitize_tx_data()

    fee is in satoshis per byte.
    """

    outputs = outputs.copy()

    for i, output in enumerate(outputs):
        dest, amount, currency = output
        outputs[i] = (dest, currency_to_satoshi_cached(amount, currency))

    if not unspents:
        raise ValueError("Transactions must have at least one unspent.")

    # Temporary storage so all outputs precede messages.
    messages = []
    total_op_return_size = 0

    if message and (custom_pushdata is False):
        try:
            message = message.encode("utf-8")
        except AttributeError:
            pass  # assume message is already a bytes-like object

        message_chunks = chunk_data(message, MESSAGE_LIMIT)

        for message in message_chunks:
            messages.append((message, 0))
            total_op_return_size += get_op_return_size(message,
                                                       custom_pushdata=False)

    elif message and (custom_pushdata is True):
        if len(message) >= 220:
            # FIXME add capability for >220 bytes for custom pushdata elements
            raise ValueError(
                "Currently cannot exceed 220 bytes with custom_pushdata.")
        else:
            messages.append((message, 0))
            total_op_return_size += get_op_return_size(message,
                                                       custom_pushdata=True)

    # Include return address in fee estimate.
    total_in = 0
    num_outputs = len(outputs) + 1
    sum_outputs = sum(out[1] for out in outputs)

    if combine:
        # calculated_fee is in total satoshis.
        calculated_fee = estimate_tx_fee(len(unspents), num_outputs, fee,
                                         compressed, total_op_return_size)
        total_out = sum_outputs + calculated_fee
        unspents = unspents.copy()
        total_in += sum(unspent.amount for unspent in unspents)

    else:
        unspents = sorted(unspents, key=lambda x: x.amount)

        index = 0

        for index, unspent in enumerate(unspents):
            total_in += unspent.amount
            calculated_fee = estimate_tx_fee(
                len(unspents[:index + 1]),
                num_outputs,
                fee,
                compressed,
                total_op_return_size,
            )
            total_out = sum_outputs + calculated_fee

            if total_in >= total_out:
                break

        unspents[:] = unspents[:index + 1]

    remaining = total_in - total_out

    if remaining > 0:
        outputs.append((leftover, remaining))
    elif remaining < 0:
        raise InsufficientFunds(f"Balance {total_in} is less than "
                                f"{total_out} (including fee).")

    outputs.extend(messages)

    return unspents, outputs
Esempio n. 3
0
def sanitize_tx_data(unspents,
                     outputs,
                     fee,
                     leftover,
                     combine=True,
                     message=None,
                     compressed=True):

    outputs = outputs.copy()

    for i, output in enumerate(outputs):
        dest, amount, currency = output
        outputs[i] = (dest, currency_to_satoshi_cached(amount, currency))

    if not unspents:
        raise ValueError('Transactions must have at least one unspent.')

    # Temporary storage so all outputs precede messages.
    messages = []

    if message:
        message_chunks = chunk_data(message.encode('utf-8'), MESSAGE_LIMIT)

        for message in message_chunks:
            messages.append((message, 0))

    # Include return address in fee estimate.
    fee = estimate_tx_fee(len(unspents),
                          len(outputs) + len(messages) + 1, fee, compressed)
    total_out = sum(out[1] for out in outputs) + fee

    total_in = 0

    if combine:
        unspents = unspents.copy()
        total_in += sum(unspent.amount for unspent in unspents)

    else:
        unspents = sorted(unspents, key=lambda x: x.amount)

        index = 0

        for index, unspent in enumerate(unspents):
            total_in += unspent.amount

            if total_in >= total_out:
                break

        unspents[:] = unspents[:index + 1]

    remaining = total_in - total_out

    if remaining > 0:
        outputs.append((leftover, remaining))
    elif remaining < 0:
        raise InsufficientFunds('Balance {} is less than {} (including '
                                'fee).'.format(total_in, total_out))

    outputs.extend(messages)

    return unspents, outputs
Esempio n. 4
0
def sanitize_tx_data(unspents, outputs, fee, leftover, combine=True, message=None, compressed=True):
    """
    sanitize_tx_data()

    fee is in satoshis per byte.
    """

    outputs = outputs.copy()

    for i, output in enumerate(outputs):
        dest, amount, currency = output
        # LEGACYADDRESSDEPRECATION
        # FIXME: Will be removed in an upcoming release, breaking compatibility with legacy addresses.
        dest = cashaddress.to_cash_address(dest)
        outputs[i] = (dest, currency_to_satoshi_cached(amount, currency))

    if not unspents:
        raise ValueError('Transactions must have at least one unspent.')

    # Temporary storage so all outputs precede messages.
    messages = []

    if message:
        message_chunks = chunk_data(message.encode('utf-8'), MESSAGE_LIMIT)

        for message in message_chunks:
            messages.append((message, 0))

    # Include return address in fee estimate.

    total_in = 0
    num_outputs = len(outputs) + len(messages) + 1
    sum_outputs = sum(out[1] for out in outputs)

    if combine:
        # calculated_fee is in total satoshis.
        calculated_fee = estimate_tx_fee(len(unspents), num_outputs, fee, compressed)
        total_out = sum_outputs + calculated_fee
        unspents = unspents.copy()
        total_in += sum(unspent.amount for unspent in unspents)

    else:
        unspents = sorted(unspents, key=lambda x: x.amount)

        index = 0

        for index, unspent in enumerate(unspents):
            total_in += unspent.amount
            calculated_fee = estimate_tx_fee(len(unspents[:index + 1]), num_outputs, fee, compressed)
            total_out = sum_outputs + calculated_fee

            if total_in >= total_out:
                break

        unspents[:] = unspents[:index + 1]

    remaining = total_in - total_out

    if remaining > 0:
        outputs.append((leftover, remaining))
    elif remaining < 0:
        raise InsufficientFunds('Balance {} is less than {} (including '
                                'fee).'.format(total_in, total_out))

    outputs.extend(messages)

    return unspents, outputs
Esempio n. 5
0
def test_chunk_data():
    assert list(chunk_data(ODD_HEX, 2)) == [
        '4f', 'ad', 'd1', '97', '73', '28', 'c1', '1e', 'fc', '1c', '1d', '8a',
        '78', '1a', 'a6', 'b9', '67', '79', '84', 'd3', 'e0', 'bd', '0b', 'fc',
        '52', 'b9', 'f3', 'b0', '38', '85', 'a0', '0'
    ]