Exemple #1
0
def test_insufficient_funds_2(generate_utxo_pool):
    utxo_pool = generate_utxo_pool([])
    selection = select_coins(CoinSelectionParams(utxo_pool, 1, 0, 0, 0, 0, 0))
    assert selection.outcome == CoinSelection.Outcome.INSUFFICIENT_FUNDS
    assert len(selection.outputs) == 0
    assert selection.effective_value == 0
    assert selection.change_value == 0
Exemple #2
0
def test_makes_slightly_larger_than_dust_change(generate_utxo_pool):
    short_term_fee_per_byte = 100
    long_term_fee_per_byte = 100
    change_output_size_in_bytes = 100
    change_spend_size_in_bytes = 100

    utxo_pool = generate_utxo_pool([1 * CENT], short_term_fee_per_byte,
                                   long_term_fee_per_byte)

    not_input_size_in_bytes = 100
    fixed_fee = short_term_fee_per_byte * not_input_size_in_bytes
    cost_of_creating_change = short_term_fee_per_byte * change_output_size_in_bytes
    cost_of_spending_change = long_term_fee_per_byte * change_spend_size_in_bytes
    cost_of_change = cost_of_creating_change + cost_of_spending_change

    total_effective_value = sum(output_group.effective_value
                                for output_group in utxo_pool)
    params = CoinSelectionParams(
        utxo_pool=utxo_pool,
        target_value=total_effective_value - fixed_fee - cost_of_change - 1,
        short_term_fee_per_byte=short_term_fee_per_byte,
        long_term_fee_per_byte=long_term_fee_per_byte,
        change_spend_size_in_bytes=100,
        change_output_size_in_bytes=100,
        not_input_size_in_bytes=not_input_size_in_bytes)

    selection = select_coins(params)

    assert selection.outcome == CoinSelection.Outcome.SUCCESS
    assert selection.change_value == params.cost_of_change + params.fixed_fee + 1
Exemple #3
0
def test_invalid_spend(generate_utxo_pool):
    utxo_pool = generate_utxo_pool([
        1 * CENT,
        2 * CENT,
        3 * CENT,
        4 * CENT,
    ])
    selection = select_coins(CoinSelectionParams(utxo_pool, 0, 0, 0, 0, 0, 0))

    assert selection.outcome == CoinSelection.Outcome.INVALID_SPEND
    assert len(selection.outputs) == 0
    assert selection.effective_value == 0
    assert selection.change_value == 0
Exemple #4
0
def test_insufficient_funds_after_fees(generate_utxo_pool):
    utxo_pool = generate_utxo_pool([10 * CENT])
    selection = select_coins(
        CoinSelectionParams(
            utxo_pool=utxo_pool,
            target_value=10 * CENT,
            short_term_fee_per_byte=100,
            long_term_fee_per_byte=100,
            change_spend_size_in_bytes=100,
            change_output_size_in_bytes=100,
            not_input_size_in_bytes=100,
        ))
    assert selection.outcome == CoinSelection.Outcome.INSUFFICIENT_FUNDS_AFTER_FEES
    assert len(selection.outputs) == 0
    assert selection.effective_value == 0
    assert selection.change_value == 0
Exemple #5
0
def test_success(generate_utxo_pool):
    utxo_pool = generate_utxo_pool([
        1 * CENT,
        2 * CENT,
        3 * CENT,
        4 * CENT,
    ])
    selection = select_coins(
        CoinSelectionParams(utxo_pool=utxo_pool,
                            target_value=5 * CENT,
                            short_term_fee_per_byte=100,
                            long_term_fee_per_byte=100,
                            change_spend_size_in_bytes=100,
                            change_output_size_in_bytes=100,
                            not_input_size_in_bytes=100))

    assert selection.outcome == CoinSelection.Outcome.SUCCESS
    assert len(selection.outputs) > 0
    assert selection.effective_value >= 5 * CENT
    assert selection.change_value > 0
Exemple #6
0
def test_does_not_make_dust_change(generate_utxo_pool):
    short_term_fee_per_byte = 100
    long_term_fee_per_byte = 100
    utxo_pool = generate_utxo_pool([1 * CENT], short_term_fee_per_byte,
                                   long_term_fee_per_byte)
    not_input_size_in_bytes = 100
    fixed_fee = short_term_fee_per_byte * not_input_size_in_bytes
    total_effective_value = sum(output_group.effective_value
                                for output_group in utxo_pool)

    selection = select_coins(
        CoinSelectionParams(utxo_pool=utxo_pool,
                            target_value=total_effective_value - fixed_fee - 1,
                            short_term_fee_per_byte=short_term_fee_per_byte,
                            long_term_fee_per_byte=long_term_fee_per_byte,
                            change_spend_size_in_bytes=100,
                            change_output_size_in_bytes=100,
                            not_input_size_in_bytes=not_input_size_in_bytes))

    assert selection.outcome == CoinSelection.Outcome.SUCCESS
    assert selection.change_value == 0
Exemple #7
0

short_term_fee_per_byte = get_short_term_fee_rate(False)

# 2.3.2 Get long term fee rate per byte
# Sadly, I haven't found any public API's exposing this information
# If you find one or make one yourself, let me know! Would be a cool project.
# Otherwise, it's reasonable to come up with some heuristic method for your app
# Lacking better options, it's okay to use short_term_fee_per_byte,
# though this will cause inefficiencies when short_term_fees are anomalously low or high
long_term_fee_per_byte = short_term_fee_per_byte

# 3 At this point we are ready to select coins; suppose we want to spend 150000 satoshis
target_value = 150000
coin_selection = select_coins(
    CoinSelectionParams(utxo_pool, target_value, short_term_fee_per_byte,
                        long_term_fee_per_byte, change_output_size_in_bytes,
                        change_spend_size_in_bytes, not_input_size_in_bytes))

# 4 Based on the CoinSelection.Outcome, you can raise different exceptions
# or surface different error text to your UI or whatever
if coin_selection.outcome != CoinSelection.Outcome.SUCCESS:
    raise Exception("Coin selection failed")


# 5 Map the coin selection back to your app-specifc class, e.g.
def map_coin_selection_to_utxos(
        coin_selection: CoinSelection,
        utxo_pool: List[YourUTXOClass]) -> List[YourUTXOClass]:
    selected_utxos = []
    for selected_coin in coin_selection.outputs:
        selected_utxo = next(utxo for utxo in utxo_pool