def test_fee_estimate(flat_fee, prop_fee_cli, max_lin_imbalance_fee,
                      target_amount, expected_fee):
    """ Tests the backwards fee calculation. """
    capacity = TA(10_000)

    prop_fee = ppm_fee_per_channel(ProportionalFeeAmount(prop_fee_cli))
    imbalance_fee = None
    if max_lin_imbalance_fee > 0:
        # This created a simple asymmetric imbalance fee
        imbalance_fee = [(0, 0), (capacity, 0),
                         (2 * capacity, max_lin_imbalance_fee)]

    tn = TokenNetworkForTests(
        channels=[
            dict(participant1=1, participant2=2),
            dict(participant1=2, participant2=3)
        ],
        default_capacity=capacity,
    )

    tn.set_fee(2,
               1,
               flat=flat_fee,
               proportional=prop_fee,
               imbalance_penalty=imbalance_fee)
    tn.set_fee(2,
               3,
               flat=flat_fee,
               proportional=prop_fee,
               imbalance_penalty=imbalance_fee)
    assert tn.estimate_fee(1, 3, value=PA(target_amount)) == expected_fee
Example #2
0
def test_fee_round_trip(flat_fee, prop_fee, imbalance_fee, amount):
    """ Tests mediation fee deduction. """
    balance = TokenAmount(100_000)
    prop_fee_per_channel = ppm_fee_per_channel(ProportionalFeeAmount(prop_fee))
    imbalance_fee = calculate_imbalance_fees(
        channel_capacity=balance,
        proportional_imbalance_fee=ProportionalFeeAmount(imbalance_fee))
    payer_channel = factories.create(
        NettingChannelStateProperties(
            our_state=NettingChannelEndStateProperties(balance=TokenAmount(0)),
            partner_state=NettingChannelEndStateProperties(balance=balance),
            fee_schedule=FeeScheduleState(
                flat=FeeAmount(flat_fee),
                proportional=prop_fee_per_channel,
                imbalance_penalty=imbalance_fee,
            ),
        ))
    payer_channel_backwards = factories.create(
        NettingChannelStateProperties(
            partner_state=NettingChannelEndStateProperties(
                balance=TokenAmount(0)),
            our_state=NettingChannelEndStateProperties(balance=balance),
            fee_schedule=FeeScheduleState(
                flat=FeeAmount(flat_fee),
                proportional=prop_fee_per_channel,
                imbalance_penalty=imbalance_fee,
            ),
        ))
    payee_channel = factories.create(
        NettingChannelStateProperties(
            our_state=NettingChannelEndStateProperties(balance=balance),
            partner_state=NettingChannelEndStateProperties(
                balance=TokenAmount(0)),
            fee_schedule=FeeScheduleState(
                flat=FeeAmount(flat_fee),
                proportional=prop_fee_per_channel,
                imbalance_penalty=imbalance_fee,
            ),
        ))

    fee_calculation = get_initial_payment_for_final_target_amount(
        final_amount=PaymentAmount(amount),
        channels=[payer_channel_backwards, payee_channel])
    assert fee_calculation

    amount_after_fees = get_lock_amount_after_fees(
        lock=make_hash_time_lock_state(amount=fee_calculation.total_amount),
        payer_channel=payer_channel,
        payee_channel=payee_channel,
    )
    assert amount_after_fees

    assert abs(amount - amount_after_fees) < 100
Example #3
0
def test_get_lock_amount_after_fees(flat_fee, prop_fee, initial_amount,
                                    expected_amount):
    """ Tests mediation fee deduction. """
    prop_fee_per_channel = ppm_fee_per_channel(ProportionalFeeAmount(prop_fee))
    lock = make_hash_time_lock_state(amount=initial_amount)
    payer_channel = factories.create(
        NettingChannelStateProperties(fee_schedule=FeeScheduleState(
            flat=flat_fee, proportional=prop_fee_per_channel)))
    payee_channel = factories.create(
        NettingChannelStateProperties(fee_schedule=FeeScheduleState(
            flat=flat_fee, proportional=prop_fee_per_channel)))

    locked_after_fees = get_lock_amount_after_fees(lock=lock,
                                                   payer_channel=payer_channel,
                                                   payee_channel=payee_channel)
    assert locked_after_fees == expected_amount
Example #4
0
def test_fee_add_remove_invariant(flat_fee, prop_fee, imbalance_fee, amount, balance1, balance2):
    """ First adding and then removing fees must yield the original value """
    total_balance = TokenAmount(100_000_000_000_000_000_000)
    prop_fee_per_channel = ppm_fee_per_channel(ProportionalFeeAmount(prop_fee))
    imbalance_fee = calculate_imbalance_fees(
        channel_capacity=total_balance,
        proportional_imbalance_fee=ProportionalFeeAmount(imbalance_fee),
    )
    fee_schedule = FeeScheduleState(
        cap_fees=False,
        flat=FeeAmount(flat_fee),
        proportional=prop_fee_per_channel,
        imbalance_penalty=imbalance_fee,
    )
    channel_in = factories.create(
        NettingChannelStateProperties(
            our_state=NettingChannelEndStateProperties(balance=total_balance - balance1),
            partner_state=NettingChannelEndStateProperties(balance=balance1),
            fee_schedule=fee_schedule,
        )
    )
    channel_out = factories.create(
        NettingChannelStateProperties(
            our_state=NettingChannelEndStateProperties(balance=balance2),
            partner_state=NettingChannelEndStateProperties(balance=total_balance - balance2),
            fee_schedule=fee_schedule,
        )
    )

    amount_with_fees = get_amount_with_fees(
        amount_without_fees=amount,
        schedule_in=channel_in.fee_schedule,
        schedule_out=channel_out.fee_schedule,
        receivable_amount=balance1,
        balance_in=total_balance - balance1,
        balance_out=balance2,
    )
    assume(amount_with_fees)
    assert amount_with_fees
    amount_without_fees = get_amount_without_fees(
        amount_with_fees=amount_with_fees, channel_in=channel_in, channel_out=channel_out
    )
    assume(amount_without_fees)
    assert amount - 1 <= amount_without_fees <= amount + 1
Example #5
0
def test_get_lock_amount_after_fees(flat_fee, prop_fee, initial_amount, expected_amount):
    """ Tests mediation fee deduction. """
    prop_fee_per_channel = ppm_fee_per_channel(ProportionalFeeAmount(prop_fee))
    lock = make_hash_time_lock_state(amount=initial_amount)
    channel_in = factories.create(
        NettingChannelStateProperties(
            partner_state=NettingChannelEndStateProperties(balance=TokenAmount(2000)),
            fee_schedule=FeeScheduleState(flat=flat_fee, proportional=prop_fee_per_channel),
        )
    )
    channel_out = factories.create(
        NettingChannelStateProperties(
            our_state=NettingChannelEndStateProperties(balance=TokenAmount(2000)),
            fee_schedule=FeeScheduleState(flat=flat_fee, proportional=prop_fee_per_channel),
        )
    )

    locked_after_fees = get_amount_without_fees(
        amount_with_fees=lock.amount, channel_in=channel_in, channel_out=channel_out
    )
    assert locked_after_fees == expected_amount
Example #6
0
def test_get_initial_payment_for_final_target_amount(
    flat_fee: FeeAmount,
    prop_fee: ProportionalFeeAmount,
    balance: TokenAmount,
    final_amount: PaymentAmount,
    initial_amount: PaymentWithFeeAmount,
    expected_fees: List[FeeAmount],
):
    prop_fee = ppm_fee_per_channel(prop_fee)
    channel_set = make_channel_set([
        NettingChannelStateProperties(
            canonical_identifier=factories.create(
                CanonicalIdentifierProperties(
                    channel_identifier=ChannelID(1))),
            our_state=NettingChannelEndStateProperties(balance=TokenAmount(0)),
            partner_state=NettingChannelEndStateProperties(balance=balance),
            fee_schedule=FeeScheduleState(flat=flat_fee,
                                          proportional=prop_fee),
        ),
        NettingChannelStateProperties(
            canonical_identifier=factories.create(
                CanonicalIdentifierProperties(
                    channel_identifier=ChannelID(2))),
            our_state=NettingChannelEndStateProperties(balance=balance),
            partner_state=NettingChannelEndStateProperties(
                balance=TokenAmount(0)),
            fee_schedule=FeeScheduleState(flat=flat_fee,
                                          proportional=prop_fee),
        ),
    ])

    calculation = get_initial_amount_for_amount_after_fees(
        amount_after_fees=final_amount,
        channels=[(channel_set.channels[0], channel_set.channels[1])],
    )

    assert calculation is not None
    assert calculation.total_amount == initial_amount
    assert calculation.mediation_fees == expected_fees
def test_compounding_fees(flat_fee_cli, prop_fee_cli, estimated_fee):
    """ The transferred amount needs to include the fees for all mediators.
    Earlier mediators will apply the proportional fee not only on the payment
    amount, but also on the fees for later mediators.
    """
    flat_fee = flat_fee_cli // 2
    prop_fee = ppm_fee_per_channel(ProportionalFeeAmount(prop_fee_cli))

    tn = TokenNetworkForTests(
        channels=[
            dict(participant1=1, participant2=2),
            dict(participant1=2, participant2=3),
            dict(participant1=3, participant2=4),
        ],
        default_capacity=TA(10_000),
    )

    tn.set_fee(2, 1, flat=flat_fee, proportional=prop_fee)
    tn.set_fee(2, 3, flat=flat_fee, proportional=prop_fee)
    tn.set_fee(3, 2, flat=flat_fee, proportional=prop_fee)
    tn.set_fee(3, 4, flat=flat_fee, proportional=prop_fee)
    assert tn.estimate_fee(1, 4, value=PA(1_000)) == estimated_fee
Example #8
0
def test_get_lock_amount_after_fees_imbalanced_channel(
    cap_fees, flat_fee, prop_fee, imbalance_fee, initial_amount, expected_amount
):
    """ Tests mediation fee deduction. """
    balance = TokenAmount(100_000)
    prop_fee_per_channel = ppm_fee_per_channel(ProportionalFeeAmount(prop_fee))
    imbalance_fee = calculate_imbalance_fees(
        channel_capacity=balance, proportional_imbalance_fee=ProportionalFeeAmount(imbalance_fee)
    )
    lock = make_hash_time_lock_state(amount=initial_amount)
    channel_in = factories.create(
        NettingChannelStateProperties(
            our_state=NettingChannelEndStateProperties(balance=TokenAmount(0)),
            partner_state=NettingChannelEndStateProperties(balance=balance),
            fee_schedule=FeeScheduleState(
                cap_fees=cap_fees,
                flat=FeeAmount(flat_fee),
                proportional=prop_fee_per_channel,
                imbalance_penalty=imbalance_fee,
            ),
        )
    )
    channel_out = factories.create(
        NettingChannelStateProperties(
            our_state=NettingChannelEndStateProperties(balance=balance),
            partner_state=NettingChannelEndStateProperties(balance=TokenAmount(0)),
            fee_schedule=FeeScheduleState(
                cap_fees=cap_fees,
                flat=FeeAmount(flat_fee),
                proportional=prop_fee_per_channel,
                imbalance_penalty=imbalance_fee,
            ),
        )
    )

    locked_after_fees = get_amount_without_fees(
        amount_with_fees=lock.amount, channel_in=channel_in, channel_out=channel_out
    )
    assert locked_after_fees == expected_amount
Example #9
0
def test_fee_round_trip(flat_fee, prop_fee, imbalance_fee, amount, balance1, balance2):
    """Tests mediation fee deduction.

    First we're doing a PFS-like calculation going backwards from the target
    amount to get the amount that the initiator has to send. Then we calculate
    the fees from a mediator's point of view and check if `amount_with_fees -
    fees = amount`.
    """
    # Find examples where there is a reasonable chance of succeeding
    amount = int(min(amount, balance1 * 0.95 - 1, balance2 * 0.95 - 1))
    assume(amount > 0)

    total_balance = TokenAmount(100_000_000_000_000_000_000)
    prop_fee_per_channel = ppm_fee_per_channel(ProportionalFeeAmount(prop_fee))
    imbalance_fee = calculate_imbalance_fees(
        channel_capacity=total_balance,
        proportional_imbalance_fee=ProportionalFeeAmount(imbalance_fee),
    )
    channel_in = factories.create(
        NettingChannelStateProperties(
            our_state=NettingChannelEndStateProperties(balance=total_balance - balance1),
            partner_state=NettingChannelEndStateProperties(balance=balance1),
            fee_schedule=FeeScheduleState(
                cap_fees=False,
                flat=FeeAmount(flat_fee),
                proportional=prop_fee_per_channel,
                imbalance_penalty=imbalance_fee,
            ),
        )
    )
    channel_out = factories.create(
        NettingChannelStateProperties(
            our_state=NettingChannelEndStateProperties(balance=balance2),
            partner_state=NettingChannelEndStateProperties(balance=total_balance - balance2),
            fee_schedule=FeeScheduleState(
                cap_fees=False,
                flat=FeeAmount(flat_fee),
                proportional=prop_fee_per_channel,
                imbalance_penalty=imbalance_fee,
            ),
        )
    )

    # How much do we need to send so that the target receives `amount`? PFS-like calculation.
    fee_calculation = get_initial_amount_for_amount_after_fees(
        amount_after_fees=PaymentAmount(amount), channels=[(channel_in, channel_out)]
    )
    assume(fee_calculation)  # There is not enough capacity for the payment in all cases
    assert fee_calculation

    # How much would a mediator send to the target? Ideally exactly `amount`.
    amount_without_margin_after_fees = get_amount_without_fees(
        amount_with_fees=fee_calculation.total_amount,
        channel_in=channel_in,
        channel_out=channel_out,
    )
    assume(amount_without_margin_after_fees)  # We might lack capacity for the payment
    assert abs(amount - amount_without_margin_after_fees) <= 1  # Equal except for rounding errors

    # If we add the fee margin, the mediator must always send at least `amount` to the target!
    amount_with_fee_and_margin = calculate_safe_amount_with_fee(
        fee_calculation.amount_without_fees, FeeAmount(sum(fee_calculation.mediation_fees))
    )
    amount_with_margin_after_fees = get_amount_without_fees(
        amount_with_fees=amount_with_fee_and_margin, channel_in=channel_in, channel_out=channel_out
    )
    assume(amount_with_margin_after_fees)  # We might lack capacity to add margins
    assert amount_with_margin_after_fees >= amount