Beispiel #1
0
def test_edge_weight(addresses):
    channel_id = ChannelID(1)
    participant1 = addresses[0]
    participant2 = addresses[1]
    settle_timeout = 15
    view = ChannelView(TokenNetworkAddress("0x11"), channel_id, participant1,
                       participant2, settle_timeout)
    amount = TokenAmount(int(1e18))  # one RDN

    # no penalty
    assert TokenNetwork.edge_weight(dict(),
                                    dict(view=view),
                                    amount=amount,
                                    fee_penalty=0) == 1

    # channel already used in a previous route
    assert (TokenNetwork.edge_weight({channel_id: 2},
                                     dict(view=view),
                                     amount=amount,
                                     fee_penalty=0) == 3)

    # absolute fee
    view.absolute_fee = FeeAmount(int(0.03e18))
    assert TokenNetwork.edge_weight(dict(),
                                    dict(view=view),
                                    amount=amount,
                                    fee_penalty=100) == 4

    # relative fee
    view.absolute_fee = FeeAmount(0)
    view.relative_fee = 0.01
    assert TokenNetwork.edge_weight(dict(),
                                    dict(view=view),
                                    amount=amount,
                                    fee_penalty=100) == 2
Beispiel #2
0
def test_update_fee(order, pathfinding_service_mock, token_network_model):
    pathfinding_service_mock.database.insert(
        "token_network", dict(address=token_network_model.address))
    if order == "normal":
        setup_channel(pathfinding_service_mock, token_network_model)

    fee_schedule = FeeScheduleState(
        flat=FeeAmount(1),
        proportional=ProportionalFeeAmount(int(0.1e9)),
        imbalance_penalty=[(TokenAmount(0), FeeAmount(0)),
                           (TokenAmount(10), FeeAmount(10))],
    )
    fee_update = PFSFeeUpdate(
        canonical_identifier=CanonicalIdentifier(
            chain_identifier=ChainID(1),
            token_network_address=token_network_model.address,
            channel_identifier=ChannelID(1),
        ),
        updating_participant=PARTICIPANT1,
        fee_schedule=fee_schedule,
        timestamp=datetime.now(timezone.utc),
        signature=EMPTY_SIGNATURE,
    )
    fee_update.sign(LocalSigner(PARTICIPANT1_PRIVKEY))
    pathfinding_service_mock.handle_message(fee_update)

    if order == "fee_update_before_channel_open":
        setup_channel(pathfinding_service_mock, token_network_model)

    cv = token_network_model.G[PARTICIPANT1][PARTICIPANT2]["view"]
    for key in ("flat", "proportional", "imbalance_penalty"):
        assert getattr(cv.fee_schedule_sender,
                       key) == getattr(fee_schedule, key)
Beispiel #3
0
    def _calculate_fees(self) -> Optional[List[FeeAmount]]:
        """Calcluates fees backwards for this path.

        Returns ``None``, if the fee calculation cannot be done.
        """
        total = PaymentWithFeeAmount(self.value)
        fees: List[FeeAmount] = []
        for prev_node, mediator, next_node in reversed(
                list(window(self.nodes, 3))):
            view_in: ChannelView = self.G[prev_node][mediator]["view"]
            view_out: ChannelView = self.G[mediator][next_node]["view"]

            log.debug(
                "Fee calculation",
                amount=total,
                view_out=view_out,
                view_in=view_in,
                amount_without_fees=total,
                balance_in=view_in.capacity_partner,
                balance_out=view_out.capacity,
                schedule_in=view_in.fee_schedule_receiver,
                schedule_out=view_out.fee_schedule_sender,
                receivable_amount=view_in.capacity,
            )

            amount_with_fees = get_amount_with_fees(
                amount_without_fees=total,
                balance_in=Balance(view_in.capacity_partner),
                balance_out=Balance(view_out.capacity),
                schedule_in=view_in.fee_schedule_receiver,
                schedule_out=view_out.fee_schedule_sender,
                receivable_amount=view_in.capacity,
            )

            if amount_with_fees is None:
                log.warning(
                    "Invalid path because of invalid fee calculation",
                    amount=total,
                    view_out=view_out,
                    view_in=view_in,
                    amount_without_fees=total,
                    balance_in=view_in.capacity_partner,
                    balance_out=view_out.capacity,
                    schedule_in=view_in.fee_schedule_receiver,
                    schedule_out=view_out.fee_schedule_sender,
                    receivable_amount=view_in.capacity,
                )
                return None

            fee = PaymentWithFeeAmount(amount_with_fees - total)
            total += fee  # type: ignore

            fees.append(FeeAmount(fee))

        # The hop to the target does not incur mediation fees
        fees.append(FeeAmount(0))

        return fees
Beispiel #4
0
def populate_token_network_random(token_network_model: TokenNetwork,
                                  private_keys: List[str]) -> None:
    # seed for pseudo-randomness from config constant, that changes from time to time
    random.seed(NUMBER_OF_CHANNELS)

    for channel_id_int in range(NUMBER_OF_CHANNELS):
        channel_id = ChannelID(channel_id_int)

        private_key1, private_key2 = random.sample(private_keys, 2)
        address1 = Address(private_key_to_address(private_key1))
        address2 = Address(private_key_to_address(private_key2))
        settle_timeout = 15
        token_network_model.handle_channel_opened_event(
            channel_id, address1, address2, settle_timeout)

        # deposit to channels
        deposit1 = TokenAmount(random.randint(0, 1000))
        deposit2 = TokenAmount(random.randint(0, 1000))
        address1, address2 = token_network_model.channel_id_to_addresses[
            channel_id]
        token_network_model.handle_channel_new_deposit_event(
            channel_id, address1, deposit1)
        token_network_model.handle_channel_new_deposit_event(
            channel_id, address2, deposit2)
        token_network_model.handle_channel_balance_update_message(
            UpdatePFS(
                canonical_identifier=CanonicalIdentifier(
                    chain_identifier=ChainID(1),
                    channel_identifier=channel_id,
                    token_network_address=TokenNetworkAddressBytes(
                        decode_hex(token_network_model.address)),
                ),
                updating_participant=decode_hex(address1),
                other_participant=decode_hex(address2),
                updating_nonce=Nonce(1),
                other_nonce=Nonce(1),
                updating_capacity=deposit1,
                other_capacity=deposit2,
                reveal_timeout=2,
                mediation_fee=FeeAmount(0),
            ))
        token_network_model.handle_channel_balance_update_message(
            UpdatePFS(
                canonical_identifier=CanonicalIdentifier(
                    chain_identifier=ChainID(1),
                    channel_identifier=channel_id,
                    token_network_address=TokenNetworkAddressBytes(
                        decode_hex(token_network_model.address)),
                ),
                updating_participant=decode_hex(address2),
                other_participant=decode_hex(address1),
                updating_nonce=Nonce(2),
                other_nonce=Nonce(1),
                updating_capacity=deposit2,
                other_capacity=deposit1,
                reveal_timeout=2,
                mediation_fee=FeeAmount(0),
            ))
Beispiel #5
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
Beispiel #6
0
    def populate_token_network(token_network: TokenNetwork,
                               addresses: List[Address],
                               channel_descriptions: List):
        for (
                channel_id,
            (
                p1_index,
                p1_deposit,
                p1_capacity,
                _p1_fee,
                p1_reveal_timeout,
                p2_index,
                p2_deposit,
                p2_capacity,
                _p2_fee,
                p2_reveal_timeout,
                settle_timeout,
            ),
        ) in enumerate(channel_descriptions):
            token_network.handle_channel_opened_event(
                ChannelID(channel_id),
                addresses[p1_index],
                addresses[p2_index],
                settle_timeout=settle_timeout,
            )

            token_network.handle_channel_new_deposit_event(
                ChannelID(channel_id), addresses[p1_index], p1_deposit)
            token_network.handle_channel_new_deposit_event(
                ChannelID(channel_id), addresses[p2_index], p2_deposit)

            token_network.handle_channel_balance_update_message(
                channel_identifier=ChannelID(channel_id),
                updating_participant=addresses[p1_index],
                other_participant=addresses[p2_index],
                updating_nonce=Nonce(1),
                other_nonce=Nonce(1),
                updating_capacity=p1_capacity,
                other_capacity=p2_capacity,
                reveal_timeout=p1_reveal_timeout,
                mediation_fee=FeeAmount(0),
            )
            token_network.handle_channel_balance_update_message(
                channel_identifier=ChannelID(channel_id),
                updating_participant=addresses[p2_index],
                other_participant=addresses[p1_index],
                updating_nonce=Nonce(2),
                other_nonce=Nonce(1),
                updating_capacity=p2_capacity,
                other_capacity=p1_capacity,
                reveal_timeout=p2_reveal_timeout,
                mediation_fee=FeeAmount(0),
            )
Beispiel #7
0
    def imbalance_fee_sender(self, amount: PaymentWithFeeAmount,
                             balance: Balance) -> FeeAmount:
        if not self._penalty_func:
            return FeeAmount(0)

        try:
            return FeeAmount(
                # Mediator is loosing balance on his channel side
                round(
                    self._penalty_func(balance - amount) -
                    self._penalty_func(balance)))
        except ValueError:
            raise UndefinedMediationFee()
Beispiel #8
0
def calculate_fee_margin(payment_amount: PaymentAmount,
                         estimated_fee: FeeAmount) -> FeeAmount:
    if estimated_fee == 0:
        # If the total fees are zero, we assume that no fees are set. If the
        # fees sum up to zero incidentally, we should add a margin, but we
        # can't detect that case.
        return FeeAmount(0)

    return FeeAmount(
        int(
            ceil(
                abs(estimated_fee) * DEFAULT_MEDIATION_FEE_MARGIN +
                payment_amount * PAYMENT_AMOUNT_BASED_FEE_MARGIN)))
Beispiel #9
0
    def imbalance_fee(self, amount: PaymentWithFeeAmount,
                      balance: Balance) -> FeeAmount:
        if self._penalty_func:
            # Total channel balance - node balance = balance (used as x-axis for the penalty)
            balance = self._penalty_func.x_list[-1] - balance
            try:
                return FeeAmount(
                    round(
                        self._penalty_func(balance + amount) -
                        self._penalty_func(balance)))
            except ValueError:
                raise UndefinedMediationFee()

        return FeeAmount(0)
Beispiel #10
0
def test_update_fee(order, pathfinding_service_mock, token_network_model):
    metrics_state = save_metrics_state(metrics.REGISTRY)

    pathfinding_service_mock.database.insert(
        "token_network", dict(address=token_network_model.address)
    )
    if order == "normal":
        setup_channel(pathfinding_service_mock, token_network_model)
        exception_expected = False
    else:
        exception_expected = True

    fee_schedule = FeeScheduleState(
        flat=FeeAmount(1),
        proportional=ProportionalFeeAmount(int(0.1e9)),
        imbalance_penalty=[(TokenAmount(0), FeeAmount(0)), (TokenAmount(10), FeeAmount(10))],
    )
    fee_update = PFSFeeUpdate(
        canonical_identifier=CanonicalIdentifier(
            chain_identifier=ChainID(61),
            token_network_address=token_network_model.address,
            channel_identifier=ChannelID(1),
        ),
        updating_participant=PARTICIPANT1,
        fee_schedule=fee_schedule,
        timestamp=datetime.utcnow(),
        signature=EMPTY_SIGNATURE,
    )
    fee_update.sign(LocalSigner(PARTICIPANT1_PRIVKEY))
    pathfinding_service_mock.handle_message(fee_update)

    # Test for metrics having seen the processing of the message
    assert (
        metrics_state.get_delta(
            "messages_processing_duration_seconds_sum",
            labels={"message_type": "PFSFeeUpdate"},
        )
        > 0.0
    )
    assert metrics_state.get_delta(
        "messages_exceptions_total", labels={"message_type": "PFSFeeUpdate"}
    ) == float(exception_expected)

    if order == "fee_update_before_channel_open":
        setup_channel(pathfinding_service_mock, token_network_model)

    cv = token_network_model.G[PARTICIPANT1][PARTICIPANT2]["view"]
    for key in ("flat", "proportional", "imbalance_penalty"):
        assert getattr(cv.fee_schedule_sender, key) == getattr(fee_schedule, key)
Beispiel #11
0
    def imbalance_fee_receiver(self, amount: PaymentWithFeeAmount,
                               balance: Balance) -> FeeAmount:
        if not self._penalty_func:
            return FeeAmount(0)

        # Calculate the mediators balance
        balance = self._penalty_func.x_list[-1] - balance
        try:
            return FeeAmount(
                # Mediator is gaining balance on his channel side
                round(
                    self._penalty_func(balance + amount) -
                    self._penalty_func(balance)))
        except ValueError:
            raise UndefinedMediationFee()
Beispiel #12
0
def resolve_routes(
    routes: List[RouteMetadata],
    token_network_address: TokenNetworkAddress,
    chain_state: ChainState,
) -> List[RouteState]:
    """ resolve the forward_channel_id for a given route """

    resolvable = []
    for route_metadata in routes:
        if len(route_metadata.route) < 2:
            continue

        channel_state = views.get_channelstate_by_token_network_and_partner(
            chain_state=chain_state,
            token_network_address=token_network_address,
            partner_address=route_metadata.route[1],
        )

        if channel_state is not None:
            resolvable.append(
                RouteState(
                    route=route_metadata.route,
                    forward_channel_id=channel_state.canonical_identifier.
                    channel_identifier,
                    # This is only used in the mediator, so fees are set to 0
                    estimated_fee=FeeAmount(0),
                ))
    return resolvable
Beispiel #13
0
class RouteState(State):
    """ A possible route for a payment to a given target. """

    # TODO: Add timestamp
    route: List[Address]
    swaps: Dict[Address, TokenNetworkAddress] = field(default_factory=dict)
    estimated_fee: FeeAmount = FeeAmount(0)

    @property
    def next_hop_address(self) -> Address:
        assert len(self.route) >= 1, "Route has no next hop"
        return self.route[1]

    @property
    def next_hop(self) -> Address:
        """Identifies the next node

        Use this to compare if two routes go to the same next node.

        Planned change: Will return a token network in addition to the node address.
        """
        assert len(self.route) >= 1, "Route has no next hop"
        return self.route[1]

    def __repr__(self) -> str:
        return "RouteState ({}), fee: {}".format(
            " -> ".join(to_checksum_address(addr) for addr in self.route),
            self.estimated_fee,
        )
Beispiel #14
0
def test_mfee3():
    """ Unit test for the fee calculation in the mfee3_only_imbalance_fees scenario """
    amount = 500_000_000_000_000_000
    deposit = TokenAmount(1_000_000_000_000_000_000)
    imbalance_penalty = calculate_imbalance_fees(deposit, ProportionalFeeAmount(10_000))
    fee_schedule = FeeScheduleState(imbalance_penalty=imbalance_penalty, cap_fees=False)
    channels = make_channel_pair(fee_schedule, deposit)

    # 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=[channels]
    )
    assert fee_calculation
    amount_with_margin = calculate_safe_amount_with_fee(
        fee_calculation.amount_without_fees, FeeAmount(sum(fee_calculation.mediation_fees))
    )
    assert amount_with_margin == 480_850_038_799_922_400

    # print values for scenario
    print("{:_} {:_}".format(deposit - amount_with_margin, amount_with_margin))
    for med_fee in running_sum(fee_calculation.mediation_fees):
        print(
            "{:_} {:_}".format(
                deposit - amount_with_margin + med_fee, amount_with_margin - med_fee
            )
        )
Beispiel #15
0
def get_updatepfs_message(
    updating_participant: Address,
    other_participant: Address,
    chain_identifier=ChainID(1),
    channel_identifier=ChannelID(0),
    token_network_address:
    TokenNetworkAddressBytes = DEFAULT_TOKEN_NETWORK_ADDRESS_BYTES,
    updating_nonce=Nonce(1),
    other_nonce=Nonce(0),
    updating_capacity=TokenAmount(90),
    other_capacity=TokenAmount(110),
    reveal_timeout: int = 2,
    mediation_fee: FeeAmount = FeeAmount(0),
    privkey_signer: bytes = PRIVAT_KEY_EXAMPLE_1,
) -> UpdatePFS:
    updatepfs_message = UpdatePFS(
        canonical_identifier=CanonicalIdentifier(
            chain_identifier=chain_identifier,
            channel_identifier=channel_identifier,
            token_network_address=token_network_address,
        ),
        updating_participant=decode_hex(updating_participant),
        other_participant=decode_hex(other_participant),
        updating_nonce=updating_nonce,
        other_nonce=other_nonce,
        updating_capacity=updating_capacity,
        other_capacity=other_capacity,
        reveal_timeout=reveal_timeout,
        mediation_fee=mediation_fee,
    )

    updatepfs_message.sign(LocalSigner(privkey_signer))

    return updatepfs_message
def get_fee_update_message(  # pylint: disable=too-many-arguments
    updating_participant: Address,
    chain_id=ChainID(61),
    channel_identifier=DEFAULT_CHANNEL_ID,
    token_network_address: TokenNetworkAddress = DEFAULT_TOKEN_NETWORK_ADDRESS,
    fee_schedule: FeeScheduleState = FeeScheduleState(
        cap_fees=True,
        flat=FeeAmount(1),
        proportional=ProportionalFeeAmount(1)),
    timestamp: datetime = datetime.utcnow(),
    privkey_signer: bytes = PRIVATE_KEY_1,
) -> PFSFeeUpdate:
    fee_message = PFSFeeUpdate(
        canonical_identifier=CanonicalIdentifier(
            chain_identifier=chain_id,
            channel_identifier=channel_identifier,
            token_network_address=token_network_address,
        ),
        updating_participant=updating_participant,
        fee_schedule=fee_schedule,
        timestamp=timestamp,
        signature=EMPTY_SIGNATURE,
    )

    fee_message.sign(LocalSigner(privkey_signer))

    return fee_message
Beispiel #17
0
def fee_receiver(
    fee_schedule: FeeScheduleState,
    balance: Balance,
    amount: PaymentWithFeeAmount,
    iterations: int = 2,
) -> FeeAmount:
    """Returns the mediation fee for this channel when receiving the given amount"""

    def fee_in(imbalance_fee: FeeAmount) -> FeeAmount:
        return FeeAmount(
            round(
                (
                    (amount + fee_schedule.flat + imbalance_fee)
                    / (1 - fee_schedule.proportional / 1e6)
                )
                - amount
            )
        )

    imbalance_fee = FeeAmount(0)
    for _ in range(iterations):
        imbalance_fee = imbalance_fee_receiver(
            fee_schedule=fee_schedule,
            amount=PaymentWithFeeAmount(amount + fee_in(imbalance_fee=imbalance_fee)),
            balance=balance,
        )

    return fee_in(imbalance_fee=imbalance_fee)
    def _calculate_fees(self) -> None:
        total = PaymentWithFeeAmount(self.value)
        for prev_node, mediator, next_node in reversed(list(window(self.nodes, 3))):
            try:
                view_in: ChannelView = self.G[prev_node][mediator]["view"]
                view_out: ChannelView = self.G[mediator][next_node]["view"]

                fee_out = view_out.backwards_fee_sender(
                    balance=Balance(view_out.capacity), amount=total
                )

                total += fee_out  # type: ignore

                fee_in = view_in.backwards_fee_receiver(
                    balance=Balance(view_in.capacity), amount=total
                )

                total += fee_in  # type: ignore

                self.fees.append(FeeAmount(fee_in + fee_out))
            except UndefinedMediationFee:
                log.warning(
                    "Invalid fee calculation",
                    amount=total,
                    view_out=view_out,
                    view_in=view_in,
                    fee_schedule_sender=view_out.fee_schedule_sender,
                    fee_schedule_receiver=view_in.fee_schedule_receiver,
                )
                self._is_valid = False
Beispiel #19
0
    def is_valid(self) -> bool:
        """ Check capacity and settle timeout

        Capacity: The capacity for the last channel must be at least
        the payment value. The previous channel's capacity has to be larger
        than value + last channel's capacity, etc.

        Settle timeout: The raiden client will not forward payments if the
        channel over which they receive has a too low settle_timeout. So we
        should not use such routes. See
        https://github.com/raiden-network/raiden-services/issues/5.
        """
        log.debug("Checking path validity", nodes=self.nodes, value=self.value)
        if hasattr(self, "_is_valid"):
            return self._is_valid
        required_capacity = self.value
        edges = reversed(list(self.edge_attrs))
        fees = self.fees + [
            FeeAmount(0)
        ]  # The hop to the target does not incur mediation fees
        for edge, fee in zip(edges, fees):
            # check capacity
            if edge["view"].capacity < required_capacity:
                log.debug(
                    "Path invalid because of missing capacity",
                    edge=edge,
                    fee=fees,
                    available_capacity=edge["view"].capacity,
                    required_capacity=required_capacity,
                )
                return False
            required_capacity = PaymentAmount(required_capacity + fee)

            # check if settle_timeout / reveal_timeout >= default ratio
            ratio = edge["view"].settle_timeout / edge["view"].reveal_timeout
            if ratio < DEFAULT_SETTLE_TO_REVEAL_TIMEOUT_RATIO:
                log.debug(
                    "Path invalid because of too low reveal timeout ratio",
                    edge=edge,
                    fee=fees,
                    settle_timeout=edge["view"].settle_timeout,
                    reveal_timeout=edge["view"].reveal_timeout,
                    ratio=ratio,
                    required_ratio=DEFAULT_SETTLE_TO_REVEAL_TIMEOUT_RATIO,
                )
                return False

        # check node reachabilities
        for node in self.nodes:
            node_reachability = self.address_to_reachability.get(
                node, AddressReachability.UNKNOWN)
            if node_reachability != AddressReachability.REACHABLE:
                log.debug(
                    "Path invalid because of unavailable node",
                    node=node,
                    node_reachability=node_reachability,
                )
                return False

        return True
Beispiel #20
0
def get_updatepfs_message(  # pylint: disable=too-many-arguments
    updating_participant: Address,
    other_participant: Address,
    chain_identifier=ChainID(1),
    channel_identifier=DEFAULT_CHANNEL_ID,
    token_network_address: TokenNetworkAddress = DEFAULT_TOKEN_NETWORK_ADDRESS,
    updating_nonce=Nonce(1),
    other_nonce=Nonce(0),
    updating_capacity=TokenAmount(90),
    other_capacity=TokenAmount(110),
    reveal_timeout: int = 2,
    mediation_fee: FeeAmount = FeeAmount(0),
    privkey_signer: bytes = PRIVATE_KEY_1,
) -> UpdatePFS:
    updatepfs_message = UpdatePFS(
        canonical_identifier=CanonicalIdentifier(
            chain_identifier=chain_identifier,
            channel_identifier=channel_identifier,
            token_network_address=token_network_address,
        ),
        updating_participant=updating_participant,
        other_participant=other_participant,
        updating_nonce=updating_nonce,
        other_nonce=other_nonce,
        updating_capacity=updating_capacity,
        other_capacity=other_capacity,
        reveal_timeout=reveal_timeout,
        mediation_fee=mediation_fee,
        signature=EMPTY_SIGNATURE,
    )

    updatepfs_message.sign(LocalSigner(privkey_signer))

    return updatepfs_message
Beispiel #21
0
def resolve_routes(
    routes: List[RouteMetadata],
    token_network_address: TokenNetworkAddress,
    chain_state: ChainState,
) -> List[RouteState]:
    """resolve the forward_channel_id for a given route

    TODO: We don't have ``forward_channel_id``, anymore. Does this function still make sense?
    """

    resolvable = []
    for route_metadata in routes:
        if len(route_metadata.route) < 2:
            continue

        channel_state = views.get_channelstate_by_token_network_and_partner(
            chain_state=chain_state,
            token_network_address=token_network_address,
            partner_address=route_metadata.route[1],
        )

        if channel_state is not None:
            resolvable.append(
                RouteState(
                    route=route_metadata.route,
                    # This is only used in the mediator, so fees are set to 0
                    estimated_fee=FeeAmount(0),
                ))
    return resolvable
Beispiel #22
0
def test_stats_endpoint(api_sut_with_debug: PFSApi, api_url: str,
                        token_network_model: TokenNetwork):
    database = api_sut_with_debug.pathfinding_service.database
    default_path = [Address(b"1" * 20), Address(b"2" * 20), Address(b"3" * 20)]
    feedback_token = FeedbackToken(token_network_model.address)
    estimated_fee = FeeAmount(0)

    def check_response(num_all: int, num_only_feedback: int,
                       num_only_success: int) -> None:
        url = api_url + "/v1/_debug/stats"
        response = requests.get(url)

        assert response.status_code == 200

        data = response.json()
        assert data["total_calculated_routes"] == num_all
        assert data["total_feedback_received"] == num_only_feedback
        assert data["total_successful_routes"] == num_only_success

    database.prepare_feedback(feedback_token, default_path, estimated_fee)
    check_response(1, 0, 0)

    database.update_feedback(feedback_token, default_path, False)
    check_response(1, 1, 0)

    default_path2 = default_path[1:]
    feedback_token2 = FeedbackToken(token_network_model.address)

    database.prepare_feedback(feedback_token2, default_path2, estimated_fee)
    check_response(2, 1, 0)

    database.update_feedback(feedback_token2, default_path2, True)
    check_response(2, 2, 1)
Beispiel #23
0
def test_mfee4():
    """ Unit test for the fee calculation in the mfee4_combined_fees scenario """
    amount = PaymentAmount(500_000_000_000_000_000)
    deposit = 1_000_000_000_000_000_000
    prop_fee = ppm_fee_per_channel(ProportionalFeeAmount(10_000))
    imbalance_penalty = calculate_imbalance_fees(
        TokenAmount(deposit * 2), ProportionalFeeAmount(20_000)
    )
    fee_schedule = FeeScheduleState(
        flat=FeeAmount(100 // 2),
        proportional=prop_fee,
        imbalance_penalty=imbalance_penalty,
        cap_fees=False,
    )
    channels = make_channel_pair(fee_schedule, deposit, deposit)

    # 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=[channels, channels]
    )
    assert fee_calculation

    amount_with_margin = calculate_safe_amount_with_fee(
        amount, FeeAmount(sum(fee_calculation.mediation_fees))
    )

    # Calculate mediation fees for both mediators
    med_fees = []
    incoming_amount = amount_with_margin
    for _ in range(2):
        outgoing_amount = get_amount_without_fees(
            amount_with_fees=incoming_amount, channel_in=channels[0], channel_out=channels[1]
        )
        assert outgoing_amount
        med_fees.append(incoming_amount - outgoing_amount)
        incoming_amount = outgoing_amount

    assert amount_with_margin == 543_503_066_141_505_551

    # print values for scenario
    print("{:_} {:_}".format(deposit - amount_with_margin, deposit + amount_with_margin))
    for med_fee in running_sum(med_fees):
        print(
            "{:_} {:_}".format(
                deposit - amount_with_margin + med_fee, deposit + amount_with_margin - med_fee
            )
        )
Beispiel #24
0
def test_imbalance_penalty():
    r""" Test an imbalance penalty by moving back and forth

    The imbalance fee looks like

    20 |         /
       |        /
    10 |\.     /
       |  \.  /
     0 |    \/
    ---------------
       0    50  100

    For each input, we first assume the channel is used to forward tokens to a
    payee, which moves the capacity from x1 to x2. The we assume the same
    amount is mediated in the opposite direction (moving from x2 to x1) and
    check that the calculated fee is the same as before just with the opposite
    sign.
    """
    v_schedule = FeeScheduleState(imbalance_penalty=[
        (TokenAmount(0), FeeAmount(10)),
        (TokenAmount(50), FeeAmount(0)),
        (TokenAmount(100), FeeAmount(20)),
    ])

    for x1, amount, expected_fee_payee, expected_fee_payer in [
        (0, 50, -6, 10),
        (50, 50, 12, -20),
        (0, 10, -2, 2),
        (10, 10, -2, 2),
        (0, 20, -5, 4),
        (40, 15, 0, 0),
    ]:
        x2 = x1 + amount
        assert v_schedule.fee_payee(
            balance=Balance(100 - x1), amount=PaymentWithFeeAmount(
                amount)) == FeeAmount(expected_fee_payee)
        assert v_schedule.fee_payer(
            balance=Balance(100 - x2), amount=PaymentWithFeeAmount(
                amount)) == FeeAmount(expected_fee_payer)

    with pytest.raises(UndefinedMediationFee):
        v_schedule.fee_payee(balance=Balance(0),
                             amount=PaymentWithFeeAmount(1))
    with pytest.raises(UndefinedMediationFee):
        v_schedule.fee_payer(balance=Balance(100),
                             amount=PaymentWithFeeAmount(1))
Beispiel #25
0
def fee_sender(
    fee_schedule: FeeScheduleState, balance: Balance, amount: PaymentWithFeeAmount
) -> FeeAmount:
    """Returns the mediation fee for this channel when transferring the given amount"""
    imbalance_fee = imbalance_fee_sender(fee_schedule=fee_schedule, amount=amount, balance=balance)
    flat_fee = fee_schedule.flat
    prop_fee = int(round(amount * fee_schedule.proportional / 1e6))
    return FeeAmount(flat_fee + prop_fee + imbalance_fee)
Beispiel #26
0
    def fee_payer(self, amount: PaymentWithFeeAmount,
                  balance: Balance) -> FeeAmount:
        imbalance_fee = self.imbalance_fee(
            amount=PaymentWithFeeAmount(-amount), balance=balance)

        flat_fee = self.flat
        prop_fee = int(round(amount * self.proportional / 1e6))
        return FeeAmount(flat_fee + prop_fee + imbalance_fee)
def test_service_registry_random_pfs(service_registry_address, private_keys,
                                     web3, contract_manager):
    addresses = [privatekey_to_address(key) for key in private_keys]
    c1_service_proxy, urls = deploy_service_registry_and_set_urls(
        private_keys=private_keys,
        web3=web3,
        contract_manager=contract_manager,
        service_registry_address=service_registry_address,
    )
    assert c1_service_proxy.ever_made_deposits_len(BLOCK_ID_LATEST) == 3

    # Test that getting the url for each service address works
    for idx, address in enumerate(addresses):
        assert c1_service_proxy.get_service_url(BLOCK_ID_LATEST,
                                                address) == urls[idx]
    # Test that getting the url for a non-existing service address returns None
    assert c1_service_proxy.get_service_url(BLOCK_ID_LATEST, HOP1) is None

    # Test that get_service_address by index works
    for idx, address in enumerate(addresses):
        assert c1_service_proxy.ever_made_deposits(BLOCK_ID_LATEST,
                                                   idx) == address

    # Test that getting the address for an index out of bounds returns None
    assert not c1_service_proxy.ever_made_deposits(BLOCK_ID_LATEST, 9999)

    mock_get_pfs_info = Mock()
    mock_get_pfs_info.return_value.price = 100
    with patch("raiden.network.pathfinding.get_pfs_info", mock_get_pfs_info):
        # Make sure that too expensive PFSes are not considered valid
        assert not get_valid_pfs_url(c1_service_proxy,
                                     0,
                                     BLOCK_ID_LATEST,
                                     pathfinding_max_fee=FeeAmount(99))

        # ...but ones with the expected price are fine
        assert (get_valid_pfs_url(
            c1_service_proxy,
            0,
            BLOCK_ID_LATEST,
            pathfinding_max_fee=FeeAmount(100)) == urls[0])

        # Test that getting a random service from the proxy works
        assert (get_random_pfs(c1_service_proxy,
                               BLOCK_ID_LATEST,
                               pathfinding_max_fee=FeeAmount(100)) in urls)
Beispiel #28
0
 def backwards_fee_sender(self, balance: Balance,
                          amount: PaymentWithFeeAmount) -> FeeAmount:
     """Returns the mediation fee for this channel when transferring the given amount"""
     imbalance_fee = self.fee_schedule_sender.imbalance_fee_sender(
         amount=amount, balance=balance)
     flat_fee = self.fee_schedule_sender.flat
     prop_fee = int(
         round(amount * self.fee_schedule_sender.proportional / 1e6))
     return FeeAmount(flat_fee + prop_fee + imbalance_fee)
Beispiel #29
0
 def fee_in(imbalance_fee: FeeAmount) -> FeeAmount:
     return FeeAmount(
         round(
             (
                 (amount + fee_schedule.flat + imbalance_fee)
                 / (1 - fee_schedule.proportional / 1e6)
             )
             - amount
         )
     )
Beispiel #30
0
 def reversed(self) -> "FeeSchedule":
     if not self.imbalance_penalty:
         return self
     max_penalty = max(penalty for x, penalty in self.imbalance_penalty)
     return FeeSchedule(
         flat=self.flat,
         proportional=self.proportional,
         imbalance_penalty=[(x, FeeAmount(max_penalty - penalty))
                            for x, penalty in self.imbalance_penalty],
     )