예제 #1
0
def reclaim_erc20(
    reclamation_candidates: List[ReclamationCandidate],
    token_address: TokenAddress,
    contract_manager: ContractManager,
    account: Account,
    web3: Web3,
):
    reclaim_amount = 0

    log.info("Checking chain for claimable tokens",
             token_address=token_address)
    for node in reclamation_candidates:
        client = node.get_client(web3)
        confirmed_block_hash = client.get_confirmed_blockhash()
        token = CustomToken(client, token_address, contract_manager,
                            confirmed_block_hash)

        balance = token.balance_of(to_canonical_address(node.address))
        log.debug(
            "balance",
            token=to_checksum_address(token_address),
            balance=balance,
            address=node.address,
        )
        if balance > 0:
            drain_amount = balance
            log.info(
                "Reclaiming tokens",
                from_address=node.address,
                amount=drain_amount.__format__(",d"),
            )
            reclaim_amount += drain_amount
            assert account.address
            try:
                token.transfer(account.address, TokenAmount(drain_amount))
            except InsufficientEth:
                log.warning(
                    "Not sufficient eth in node wallet to reclaim",
                    address=node.address,
                    token_address=token_address,
                )
                continue

    if reclaim_amount:
        log.info(
            "Reclaimed",
            reclaim_amount=reclaim_amount.__format__(",d"),
            token_address=to_checksum_address(token.address),
        )
예제 #2
0
def test_received_lockedtransfer_closedchannel(
    raiden_network: List[RaidenService], reveal_timeout, token_addresses, deposit
):
    app0, app1 = raiden_network
    registry_address = app0.default_registry.address
    token_address = token_addresses[0]
    token_network_address = views.get_token_network_address_by_token_address(
        views.state_from_raiden(app0), app0.default_registry.address, token_address
    )
    assert token_network_address
    channel0 = get_channelstate(app0, app1, token_network_address)

    RaidenAPI(app1).channel_close(registry_address, token_address, app0.address)

    app0.rpc_client.wait_until_block(
        target_block_number=BlockNumber(app0.rpc_client.block_number() + 1)
    )

    # Now receive one mediated transfer for the closed channel
    lock_amount = LockedAmount(10)
    payment_identifier = PaymentID(1)
    expiration = reveal_timeout * 2
    mediated_transfer_message = LockedTransfer(
        chain_id=UNIT_CHAIN_ID,
        message_identifier=make_message_identifier(),
        payment_identifier=payment_identifier,
        nonce=Nonce(1),
        token_network_address=token_network_address,
        token=token_address,
        channel_identifier=channel0.identifier,
        transferred_amount=TokenAmount(0),
        locked_amount=lock_amount,
        recipient=app1.address,
        locksroot=make_locksroot(),
        lock=Lock(
            amount=PaymentWithFeeAmount(lock_amount),
            expiration=expiration,
            secrethash=UNIT_SECRETHASH,
        ),
        target=TargetAddress(app1.address),
        initiator=InitiatorAddress(app0.address),
        signature=EMPTY_SIGNATURE,
        metadata=Metadata(routes=[RouteMetadata(route=[app1.address])]),
    )

    sign_and_inject(mediated_transfer_message, app0.signer, app1)

    # The local state must not change since the channel is already closed
    assert_synced_channel_state(token_network_address, app0, deposit, [], app1, deposit, [])
예제 #3
0
def sequential_transfers(
    server_from: APIServer,
    server_to: APIServer,
    number_of_transfers: int,
    token_address: TokenAddress,
    identifier_generator: Iterator[int],
) -> None:
    for _ in range(number_of_transfers):
        transfer_and_assert(
            server_from=server_from,
            server_to=server_to,
            token_address=token_address,
            identifier=next(identifier_generator),
            amount=TokenAmount(1),
        )
예제 #4
0
def test_process_payment(
    pathfinding_service_web3_mock,
    deposit_to_udc,
    create_account,
    get_private_key,
    make_iou,
    one_to_n_contract,
    web3,
):
    pfs = pathfinding_service_web3_mock
    service_fee = TokenAmount(1)
    sender = create_account()
    privkey = get_private_key(sender)
    deposit_to_udc(sender, round(1 * UDC_SECURITY_MARGIN_FACTOR_PFS))
    web3.testing.mine(pathfinding_service_web3_mock.required_confirmations)
    one_to_n_address = to_canonical_address(one_to_n_contract.address)

    # Make payment
    iou = make_iou(privkey, pfs.address, amount=1)
    process_payment(iou, pfs, service_fee, one_to_n_address)

    # The same payment can't be reused
    with pytest.raises(exceptions.InsufficientServicePayment):
        process_payment(iou, pfs, service_fee, one_to_n_address)

    # Increasing the amount would make the payment work again, if we had enough
    # deposit. But we set the deposit one token too low.
    deposit_to_udc(sender, round(2 * UDC_SECURITY_MARGIN_FACTOR_PFS) - 1)
    iou = make_iou(privkey, pfs.address, amount=2)
    with pytest.raises(exceptions.DepositTooLow):
        process_payment(iou, pfs, service_fee, one_to_n_address)

    # With the higher amount and enough deposit, it works again!
    deposit_to_udc(sender, round(2 * UDC_SECURITY_MARGIN_FACTOR_PFS))
    web3.testing.mine(pathfinding_service_web3_mock.required_confirmations)
    iou = make_iou(privkey, pfs.address, amount=2)
    process_payment(iou, pfs, service_fee, one_to_n_address)

    # Make sure the client does not create new sessions unnecessarily
    iou = make_iou(privkey, pfs.address, expiration_block=20000)
    with pytest.raises(exceptions.UseThisIOU):
        process_payment(iou, pfs, service_fee, one_to_n_address)

    # Complain if the IOU has been claimed
    iou = make_iou(privkey, pfs.address, amount=3)
    pfs.database.conn.execute("UPDATE iou SET claimed=1")
    with pytest.raises(exceptions.IOUAlreadyClaimed):
        process_payment(iou, pfs, service_fee, one_to_n_address)
예제 #5
0
def get_initial_amount_for_amount_after_fees(
    amount_after_fees: PaymentAmount,
    channels: List[Tuple[NettingChannelState, NettingChannelState]],
) -> Optional[FeesCalculation]:
    """ Calculates the payment amount including fees to be supplied to the given
    channel configuration, so that `amount_after_fees` arrives at the target.

    Note: The channels have to be from the view of the mediator, so for the case
        A -> B -> C this should be [(B->A, B->C)]
    """
    assert len(channels) >= 1, "Need at least one channel pair"

    # Backpropagate fees in mediation scenario
    total = PaymentWithFeeAmount(amount_after_fees)
    fees: List[FeeAmount] = []
    try:
        for channel_in, channel_out in reversed(channels):
            assert isinstance(channel_in, NettingChannelState)
            assert isinstance(channel_out, NettingChannelState)

            balance_in = get_balance(channel_in.our_state,
                                     channel_in.partner_state)
            balance_out = get_balance(channel_out.our_state,
                                      channel_out.partner_state)
            receivable_amount = TokenAmount(channel_in.our_total_deposit +
                                            channel_in.partner_total_deposit -
                                            balance_in)

            before_fees = get_amount_with_fees(
                amount_without_fees=total,
                balance_in=balance_in,
                balance_out=balance_out,
                schedule_in=channel_in.fee_schedule,
                schedule_out=channel_out.fee_schedule,
                receivable_amount=receivable_amount,
            )

            if before_fees is None:
                return None

            fee = FeeAmount(before_fees - total)
            total = PaymentWithFeeAmount(total + fee)
            fees.append(fee)
    except UndefinedMediationFee:
        return None

    return FeesCalculation(total_amount=PaymentWithFeeAmount(total),
                           mediation_fees=fees)
예제 #6
0
def test_pfs_rejects_capacity_update_with_impossible_other_capacity(
    addresses: List[Address],
    pathfinding_service_mocked_listeners: PathfindingService,
):
    pathfinding_service_mocked_listeners.chain_id = 1

    token_network = TokenNetwork(
        token_network_address=DEFAULT_TOKEN_NETWORK_ADDRESS,
        token_address=DEFAULT_TOKEN_ADDRESS,
    )

    pathfinding_service_mocked_listeners.token_networks[
        token_network.address] = token_network

    token_network.handle_channel_opened_event(
        channel_identifier=ChannelIdentifier(0),
        participant1=addresses[0],
        participant2=addresses[1],
        settle_timeout=15,
    )

    token_network.handle_channel_new_deposit_event(
        channel_identifier=ChannelIdentifier(0),
        receiver=addresses[0],
        total_deposit=100,
    )

    token_network.handle_channel_new_deposit_event(
        channel_identifier=ChannelIdentifier(0),
        receiver=addresses[1],
        total_deposit=100,
    )

    # Check that the new channel has id == 0
    assert token_network.channel_id_to_addresses[ChannelIdentifier(0)] == (
        addresses[0],
        addresses[1],
    )

    message = get_updatepfs_message(
        updating_participant=addresses[0],
        other_participant=addresses[1],
        other_capacity=TokenAmount(UINT256_MAX + 1),
    )

    with pytest.raises(InvalidCapacityUpdate) as exinfo:
        pathfinding_service_mocked_listeners.on_pfs_update(message)
    assert 'with impossible other_capacity' in str(exinfo.value)
예제 #7
0
def test_routing_result_order(
    token_network_model: TokenNetwork,
    populate_token_network_case_1: None,
    addresses: List[Address],
):
    paths = token_network_model.get_paths(addresses[0],
                                          addresses[2],
                                          value=TokenAmount(10),
                                          max_paths=5,
                                          hop_bias=1)
    # 5 paths requested, but only 1 is available
    assert len(paths) == 1
    assert paths[0] == {
        "path": [addresses[0], addresses[1], addresses[2]],
        "estimated_fee": 0
    }
예제 #8
0
    def f(chain_id=1, **kwargs):
        balance_proof = make_balance_proof(signer=signer, **kwargs)
        balance_proof.chain_id = chain_id
        partner_signed_balance_proof = SignedBlindedBalanceProof.from_balance_proof_signed_state(
            balance_proof
        )
        request_monitoring = RequestMonitoring(
            onchain_balance_proof=partner_signed_balance_proof, reward_amount=TokenAmount(55)
        )
        request_monitoring.sign(non_closing_signer)

        # usually not a property of RequestMonitoring, but added for convenience in these tests
        request_monitoring.non_closing_signer = to_checksum_address(  # type: ignore
            non_closing_signer.address
        )
        return request_monitoring
예제 #9
0
def test_monitor_reward_claimed_event_handler(context: Context, log):
    context = setup_state_with_closed_channel(context)

    claim_event = ReceiveMonitoringRewardClaimedEvent(
        ms_address=context.ms_state.address,
        amount=TokenAmount(1),
        reward_identifier="REWARD",
        block_number=BlockNumber(23),
    )

    monitor_reward_claim_event_handler(claim_event, context)
    assert log.has("Successfully claimed reward")

    claim_event.ms_address = Address(bytes([3] * 20))
    monitor_reward_claim_event_handler(claim_event, context)
    assert log.has("Another MS claimed reward")
예제 #10
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
            )
        )
예제 #11
0
def test_setup_proxies_all_addresses_are_known():
    """
    Test that startup for proxies works fine if all addresses are given and routing is basic
    """
    chain_id = ChainID(5)
    config = RaidenConfig(chain_id=chain_id,
                          environment_type=Environment.DEVELOPMENT)
    config.transport.available_servers = ["http://matrix.example.com"]
    contracts = load_deployed_contracts_data(config, chain_id)
    proxy_manager = MockProxyManager(node_address=make_address())
    PFS_INFO = PFSInfo(
        url="my-pfs",
        price=TokenAmount(12),
        chain_id=ChainID(5),
        token_network_registry_address=to_canonical_address(
            contracts[CONTRACT_TOKEN_NETWORK_REGISTRY]["address"]),
        user_deposit_address=user_deposit_address_test_default,
        payment_address=pfs_payment_address_default,
        confirmed_block_number=BlockNumber(1),
        message="This is your favorite pathfinding service",
        operator="John Doe",
        version="0.0.3",
        matrix_server="http://matrix.example.com",
        matrix_room_id="!room-id:matrix.example.com",
    )
    deployed_addresses = load_deployment_addresses_from_contracts(contracts)
    with patch.object(pathfinding, "get_pfs_info", return_value=PFS_INFO):
        raiden_bundle = raiden_bundle_from_contracts_deployment(
            proxy_manager=proxy_manager,
            token_network_registry_address=deployed_addresses.
            token_network_registry_address,
            secret_registry_address=deployed_addresses.secret_registry_address,
        )
        services_bundle = services_bundle_from_contracts_deployment(
            config=config,
            proxy_manager=proxy_manager,
            deployed_addresses=deployed_addresses,
            routing_mode=RoutingMode.PFS,
            pathfinding_service_address="my-pfs",
            enable_monitoring=False,
        )
    assert raiden_bundle
    assert services_bundle
    assert raiden_bundle.token_network_registry
    assert raiden_bundle.secret_registry
    assert services_bundle.user_deposit
    assert services_bundle.service_registry
예제 #12
0
    def _deposit_preconditions(
        self,
        total_deposit: TokenAmount,
        beneficiary: Address,
        token: Token,
        block_identifier: BlockSpecification,
    ) -> Tuple[TokenAmount, Dict]:
        if not isinstance(total_deposit, int):
            raise ValueError("total_deposit needs to be an integer number.")

        previous_total_deposit = self.get_total_deposit(
            address=beneficiary, block_identifier=block_identifier
        )
        amount_to_deposit = TokenAmount(total_deposit - previous_total_deposit)

        log_details = {
            "user_deposit_address": pex(self.address),
            "node": pex(self.node_address),
            "beneficiary": pex(beneficiary),
            "new_total_deposit": total_deposit,
            "previous_total_deposit": previous_total_deposit,
        }

        if total_deposit <= previous_total_deposit:
            msg = (
                f"Current total deposit {previous_total_deposit} is already larger "
                f"than the requested total deposit amount {total_deposit}"
            )
            log.info("deposit failed", reason=msg, **log_details)
            raise DepositMismatch(msg)

        current_balance = token.balance_of(
            address=self.node_address, block_identifier=block_identifier
        )
        if current_balance < amount_to_deposit:
            msg = (
                f"new_total_deposit - previous_total_deposit =  {amount_to_deposit} can not "
                f"be larger than the available balance {current_balance}, "
                f"for token at address {pex(token.address)}"
            )
            log.info("deposit failed", reason=msg, **log_details)
            raise DepositMismatch(msg)

        token.approve(allowed_address=Address(self.address), allowance=amount_to_deposit)

        return amount_to_deposit, log_details
예제 #13
0
def test_monitor_reward_claimed_event_handler(context: Context, log):
    metrics_state = save_metrics_state(metrics.REGISTRY)

    context = setup_state_with_closed_channel(context)

    claim_event = ReceiveMonitoringRewardClaimedEvent(
        ms_address=context.ms_state.address,
        amount=TokenAmount(1),
        reward_identifier="REWARD",
        block_number=BlockNumber(23),
    )

    monitor_reward_claim_event_handler(claim_event, context)

    assert (
        metrics_state.get_delta(
            "economics_reward_claims_successful_total", labels=metrics.Who.US.to_label_dict()
        )
        == 1.0
    )
    assert (
        metrics_state.get_delta(
            "economics_reward_claims_token_total", labels=metrics.Who.US.to_label_dict()
        )
        == 1.0
    )

    assert log.has("Successfully claimed reward")

    claim_event = dataclasses.replace(claim_event, ms_address=Address(bytes([3] * 20)))
    monitor_reward_claim_event_handler(claim_event, context)

    assert (
        metrics_state.get_delta(
            "economics_reward_claims_successful_total", labels=metrics.Who.THEY.to_label_dict()
        )
        == 1.0
    )
    assert (
        metrics_state.get_delta(
            "economics_reward_claims_token_total", labels=metrics.Who.THEY.to_label_dict()
        )
        == 1.0
    )

    assert log.has("Another MS claimed reward")
예제 #14
0
def test_event_filter_for_payments():
    secret = factories.make_secret()
    identifier = PaymentID(1)
    target = TargetAddress(factories.make_address())
    event1 = EventPaymentSentSuccess(
        token_network_registry_address=UNIT_TOKEN_NETWORK_REGISTRY_ADDRESS,
        token_network_address=UNIT_TOKEN_NETWORK_ADDRESS,
        identifier=identifier,
        amount=PaymentAmount(5),
        target=target,
        secret=secret,
        route=[],
    )
    assert event_filter_for_payments(event=event1, partner_address=None)
    assert event_filter_for_payments(event=event1,
                                     partner_address=Address(target))
    assert not event_filter_for_payments(
        event=event1, partner_address=factories.make_address())

    initiator = InitiatorAddress(factories.make_address())
    event2 = EventPaymentReceivedSuccess(
        token_network_registry_address=UNIT_TOKEN_NETWORK_REGISTRY_ADDRESS,
        token_network_address=UNIT_TOKEN_NETWORK_ADDRESS,
        identifier=identifier,
        amount=TokenAmount(5),
        initiator=initiator,
    )
    assert event_filter_for_payments(event=event2, partner_address=None)
    assert event_filter_for_payments(event=event2,
                                     partner_address=Address(initiator))
    assert not event_filter_for_payments(
        event=event2, partner_address=factories.make_address())

    event3 = EventPaymentSentFailed(
        token_network_registry_address=UNIT_TOKEN_NETWORK_REGISTRY_ADDRESS,
        token_network_address=UNIT_TOKEN_NETWORK_ADDRESS,
        identifier=identifier,
        target=target,
        reason="whatever",
    )
    assert event_filter_for_payments(event=event3, partner_address=None)
    assert event_filter_for_payments(event=event3,
                                     partner_address=Address(target))
    assert not event_filter_for_payments(
        event=event3, partner_address=factories.make_address())
예제 #15
0
def deploy_token_and_return_proxy(deploy_client: JSONRPCClient,
                                  contract_manager: ContractManager,
                                  token_contract_name: str) -> Token:
    token_contract = deploy_token(
        deploy_client=deploy_client,
        contract_manager=contract_manager,
        initial_amount=TokenAmount(1000 * 10**18),
        decimals=0,
        token_name="TKN",
        token_symbol="TKN",
        token_contract_name=token_contract_name,
    )

    return Token(
        jsonrpc_client=deploy_client,
        token_address=TokenAddress(token_contract.contract_address),
        contract_manager=contract_manager,
    )
예제 #16
0
파일: token.py 프로젝트: sekmet/raiden
    def total_supply(
        self, block_identifier: BlockIdentifier = BLOCK_ID_LATEST
    ) -> Optional[TokenAmount]:
        """Return the total supply of the token at the given block identifier.

        Because Token is just an interface, it is not possible to check the
        bytecode during the proxy instantiation. This means it is possible for
        the proxy to be instantiated with a a smart contrat address of the
        wrong type (a non ERC20 contract), or a partial implementation of the
        ERC20 standard (the function totalSupply is missing). If that happens
        this method will return `None`.
        """
        total_supply = self.proxy.functions.totalSupply().call(block_identifier=block_identifier)

        if isinstance(total_supply, int):
            return TokenAmount(total_supply)

        return None
예제 #17
0
def test_routing_mocked_pfs_happy_path_with_updated_iou(
        happy_path_fixture, one_to_n_address, our_address):
    addresses, chain_state, channel_states, response, token_network_state = happy_path_fixture
    _, address2, _, address4 = addresses
    _, channel_state2 = channel_states

    iou = make_iou(
        pfs_config=PFS_CONFIG,
        our_address=factories.UNIT_TRANSFER_SENDER,
        one_to_n_address=one_to_n_address,
        privkey=PRIVKEY,
        block_number=BlockNumber(10),
        chain_id=ChainID(5),
        offered_fee=TokenAmount(1),
    )
    last_iou = copy(iou)

    with patch.object(requests, "post", return_value=response) as patched:
        routes, feedback_token = get_best_routes_with_iou_request_mocked(
            chain_state=chain_state,
            token_network_state=token_network_state,
            one_to_n_address=one_to_n_address,
            from_address=our_address,
            to_address=address4,
            amount=50,
            iou_json_data=dict(last_iou=last_iou.as_json()),
        )

    assert_checksum_address_in_url(patched.call_args[0][0])

    assert routes[0].next_hop_address == address2
    assert routes[0].forward_channel_id == channel_state2.identifier
    assert feedback_token == DEFAULT_FEEDBACK_TOKEN

    # Check for iou arguments in request payload
    payload = patched.call_args[1]["json"]
    pfs_config = CONFIG["pfs_config"]
    old_amount = last_iou.amount
    assert old_amount < payload["iou"][
        "amount"] <= pfs_config.maximum_fee + old_amount
    assert payload["iou"]["expiration_block"] == last_iou.expiration_block
    assert payload["iou"]["sender"] == to_checksum_address(last_iou.sender)
    assert payload["iou"]["receiver"] == to_checksum_address(last_iou.receiver)
    assert "signature" in payload["iou"]
예제 #18
0
 def update(self, amount, lock):
     self._pending_locks = channel.compute_locks_with(
         self._pending_locks, lock)
     assert self._pending_locks
     if self.properties:
         self.properties = factories.replace(
             self.properties,
             locked_amount=self.properties.locked_amount + amount,
             locksroot=compute_locksroot(self._pending_locks),
             nonce=self.properties.nonce + 1,
         )
     else:
         self.properties = factories.BalanceProofProperties(
             transferred_amount=TokenAmount(0),
             locked_amount=amount,
             nonce=Nonce(1),
             locksroot=compute_locksroot(self._pending_locks),
             canonical_identifier=self._canonical_identifier,
         )
예제 #19
0
def test_routing_result_order(
    token_network_model: TokenNetwork,
    address_to_reachability: Dict[Address, AddressReachability],
    addresses: List[Address],
):
    hex_addrs = [to_checksum_address(addr) for addr in addresses]
    paths = token_network_model.get_paths(
        addresses[0],
        addresses[2],
        value=TokenAmount(10),
        max_paths=5,
        address_to_reachability=address_to_reachability,
    )
    # 5 paths requested, but only 1 is available
    assert len(paths) == 1
    assert paths[0] == {
        "path": [hex_addrs[0], hex_addrs[1], hex_addrs[2]],
        "estimated_fee": 0
    }
예제 #20
0
def test_rebalancing_fee_calculation():
    sample = calculate_imbalance_fees(TokenAmount(200), ProportionalFeeAmount(50_000))  # 5%
    assert sample is not None
    assert len(sample) == NUM_DISCRETISATION_POINTS
    assert all(0 <= x <= 200 for x, _ in sample)
    assert max(x for x, _ in sample) == 200
    assert all(0 <= y <= 10 for _, y in sample)
    assert max(y for _, y in sample) == 10  # 5% of the 200 TokenAmount capacity

    sample = calculate_imbalance_fees(TokenAmount(100), ProportionalFeeAmount(20_000))  # 2%
    assert sample is not None
    assert len(sample) == NUM_DISCRETISATION_POINTS
    assert all(0 <= x <= 100 for x, _ in sample)
    assert max(x for x, _ in sample) == 100
    assert all(0 <= y <= 2 for _, y in sample)
    assert max(y for _, y in sample) == 2  # 2% of the 100 TokenAmount capacity

    sample = calculate_imbalance_fees(TokenAmount(15), ProportionalFeeAmount(50_000))  # 5%
    assert sample is not None
    assert len(sample) == 16
    assert all(0 <= x <= 16 for x, _ in sample)
    assert max(x for x, _ in sample) == 15
    assert all(0 <= y <= 1 for _, y in sample)
    assert max(y for _, y in sample) == 1  # 5% of the 5 rounded up

    # test rounding of the max_balance_fee calculation
    sample = calculate_imbalance_fees(TokenAmount(1000), ProportionalFeeAmount(5_490))  # 0.549%
    assert sample is not None
    assert len(sample) == NUM_DISCRETISATION_POINTS
    assert all(0 <= x <= 1000 for x, _ in sample)
    assert max(x for x, _ in sample) == 1000
    assert all(0 <= y <= 5 for _, y in sample)
    assert max(y for _, y in sample) == 5  # 5.49 is rounded to 5

    sample = calculate_imbalance_fees(TokenAmount(1000), ProportionalFeeAmount(5_500))  # 0.55%
    assert sample is not None
    assert len(sample) == NUM_DISCRETISATION_POINTS
    assert all(0 <= x <= 1000 for x, _ in sample)
    assert max(x for x, _ in sample) == 1000
    assert all(0 <= y <= 6 for _, y in sample)
    assert max(y for _, y in sample) == 6  # 5.5 is rounded to 6

    # test cases where no imbalance fee is created
    assert calculate_imbalance_fees(TokenAmount(0), ProportionalFeeAmount(1)) is None
    assert calculate_imbalance_fees(TokenAmount(10), ProportionalFeeAmount(0)) is None
예제 #21
0
def test_setup_proxies_no_service_registry_but_pfs():
    """
    Test that if no service registry is provided but a manual pfs address is given then startup
    still works

    Regression test for https://github.com/raiden-network/raiden/issues/3740
    """
    chain_id = ChainID(5)
    config = RaidenConfig(
        chain_id=chain_id,
        environment_type=Environment.DEVELOPMENT,
        services=ServiceConfig(pathfinding_max_fee=100,
                               pathfinding_iou_timeout=500,
                               pathfinding_max_paths=5),
    )
    contracts = load_deployed_contracts_data(config, chain_id)
    proxy_manager = MockProxyManager(node_address=make_address())

    PFS_INFO = PFSInfo(
        url="my-pfs",
        price=TokenAmount(12),
        chain_id=ChainID(5),
        token_network_registry_address=to_canonical_address(
            contracts[CONTRACT_TOKEN_NETWORK_REGISTRY]["address"]),
        user_deposit_address=user_deposit_address_test_default,
        confirmed_block_number=BlockNumber(1),
        payment_address=pfs_payment_address_default,
        message="This is your favorite pathfinding service",
        operator="John Doe",
        version="0.0.3",
    )
    deployed_addresses = load_deployment_addresses_from_contracts(contracts)
    with patch.object(pathfinding, "get_pfs_info", return_value=PFS_INFO):
        services_bundle = services_bundle_from_contracts_deployment(
            config=config,
            proxy_manager=proxy_manager,
            deployed_addresses=deployed_addresses,
            routing_mode=RoutingMode.PFS,
            pathfinding_service_address="my-pfs",
            enable_monitoring=True,
        )
    assert services_bundle
예제 #22
0
def test_action_monitoring_triggered_event_handler_does_not_trigger_monitor_call_when_nonce_to_small(  # noqa
        context: Context, ):
    context = setup_state_with_closed_channel(context)

    event3 = ReceiveMonitoringNewBalanceProofEvent(
        token_network_address=DEFAULT_TOKEN_NETWORK_ADDRESS,
        channel_identifier=DEFAULT_CHANNEL_IDENTIFIER,
        reward_amount=TokenAmount(1),
        nonce=Nonce(5),
        ms_address=Address('C'),
        raiden_node_address=DEFAULT_PARTICIPANT2,
        block_number=BlockNumber(23),
    )

    channel = context.db.get_channel(event3.token_network_address,
                                     event3.channel_identifier)
    assert channel
    assert channel.update_status is None

    monitor_new_balance_proof_event_handler(event3, context)

    # add MR to DB, with nonce being smaller than in event3
    context.db.upsert_monitor_request(
        get_signed_monitor_request(nonce=Nonce(4)))

    event4 = ActionMonitoringTriggeredEvent(
        token_network_address=DEFAULT_TOKEN_NETWORK_ADDRESS,
        channel_identifier=DEFAULT_CHANNEL_IDENTIFIER,
        non_closing_participant=DEFAULT_PARTICIPANT2,
    )

    channel = context.db.get_channel(event4.token_network_address,
                                     event4.channel_identifier)
    assert channel
    assert channel.update_status is not None
    assert channel.closing_tx_hash is None

    action_monitoring_triggered_event_handler(event4, context)

    assert context.db.channel_count() == 1
    assert channel
    assert channel.closing_tx_hash is None
예제 #23
0
def test_monitor_new_balance_proof_event_handler_idempotency(context: Context):
    context = setup_state_with_closed_channel(context)

    new_balance_event = ReceiveMonitoringNewBalanceProofEvent(
        token_network_address=DEFAULT_TOKEN_NETWORK_ADDRESS,
        channel_identifier=DEFAULT_CHANNEL_IDENTIFIER,
        reward_amount=TokenAmount(1),
        nonce=Nonce(2),
        ms_address=Address(bytes([3] * 20)),
        raiden_node_address=DEFAULT_PARTICIPANT2,
        block_number=BlockNumber(23),
    )

    channel = context.database.get_channel(
        new_balance_event.token_network_address,
        new_balance_event.channel_identifier)
    assert channel
    assert channel.update_status is None

    monitor_new_balance_proof_event_handler(new_balance_event, context)

    assert context.database.scheduled_event_count() == 1
    assert context.database.channel_count() == 1
    channel = context.database.get_channel(
        new_balance_event.token_network_address,
        new_balance_event.channel_identifier)
    assert channel
    assert channel.update_status is not None
    assert channel.update_status.nonce == 2
    assert channel.update_status.update_sender_address == bytes([3] * 20)

    monitor_new_balance_proof_event_handler(new_balance_event, context)

    assert context.database.scheduled_event_count() == 1
    assert context.database.channel_count() == 1
    channel = context.database.get_channel(
        new_balance_event.token_network_address,
        new_balance_event.channel_identifier)
    assert channel
    assert channel.update_status is not None
    assert channel.update_status.nonce == 2
    assert channel.update_status.update_sender_address == bytes([3] * 20)
예제 #24
0
def userdeposit_maybe_deposit(
    userdeposit_proxy: UserDeposit,
    mint_greenlets: Set[Greenlet],
    target_address: Address,
    minimum_effective_deposit: TokenAmount,
    maximum_funding: TokenAmount,
) -> None:
    """Make a deposit at the given `target_address`.

    The amount of tokens depends on the scenario definition's settings.

    If the target address has a sufficient deposit, this is a no-op.

    TODO: Allow setting max funding parameter, similar to the token `funding_min` setting.
    """
    effective_balance = userdeposit_proxy.effective_balance(
        target_address, "latest")
    current_total_deposit = userdeposit_proxy.get_total_deposit(
        target_address, "latest")

    if maximum_funding < minimum_effective_deposit:
        raise ValueError(
            f"max_funding must be larger than minimum_effective_deposit, "
            f"otherwise the constraint can never be satisfied. Given "
            f"max_funding={maximum_funding} "
            f"minimum_effective_deposit={minimum_effective_deposit}")

    # Only do a deposit if the current effective balance is bellow the minimum.
    # When doing the deposit, top-up to max_funding to save transactions on the
    # next iterations.
    if effective_balance < minimum_effective_deposit:
        topup_amount = maximum_funding - effective_balance
        new_total_deposit = TokenAmount(current_total_deposit + topup_amount)

        # Wait for mint transactions, if necessary
        gevent.joinall(mint_greenlets, raise_error=True)

        userdeposit_proxy.deposit(
            target_address,
            new_total_deposit,
            userdeposit_proxy.client.get_confirmed_blockhash(),
        )
def test_receive_lockedtransfer_invalidrecipient(raiden_network,
                                                 token_addresses,
                                                 reveal_timeout, deposit):
    app0, app1 = raiden_network
    token_address = token_addresses[0]
    token_network_address = views.get_token_network_address_by_token_address(
        views.state_from_app(app0), app0.raiden.default_registry.address,
        token_address)
    assert token_network_address
    channel0 = get_channelstate(app0, app1, token_network_address)

    payment_identifier = PaymentID(1)
    invalid_recipient = make_address()
    lock_amount = LockedAmount(10)
    expiration = reveal_timeout * 2
    mediated_transfer_message = LockedTransfer(
        chain_id=UNIT_CHAIN_ID,
        message_identifier=make_message_identifier(),
        payment_identifier=payment_identifier,
        nonce=Nonce(1),
        token_network_address=token_network_address,
        token=token_address,
        channel_identifier=channel0.identifier,
        transferred_amount=TokenAmount(0),
        locked_amount=lock_amount,
        recipient=invalid_recipient,
        locksroot=make_locksroot(),
        lock=Lock(
            amount=PaymentWithFeeAmount(lock_amount),
            expiration=expiration,
            secrethash=UNIT_SECRETHASH,
        ),
        target=app1.raiden.address,
        initiator=app0.raiden.address,
        signature=EMPTY_SIGNATURE,
        metadata=Metadata(routes=[RouteMetadata(route=[app1.raiden.address])]),
    )

    sign_and_inject(mediated_transfer_message, app0.raiden.signer, app1)

    assert_synced_channel_state(token_network_address, app0, deposit, [], app1,
                                deposit, [])
예제 #26
0
def create_square_network_topology(
    token_network_state, our_address
) -> typing.Tuple[
    TokenNetworkState, typing.List[typing.Address], typing.List[NettingChannelState]
]:
    address1 = factories.make_address()
    address2 = factories.make_address()
    address3 = factories.make_address()
    address4 = factories.make_address()

    # Create a network with the following topology
    #
    # our  ----- 50 ---->  (1) <------50------
    #  |                                    |
    #  |                                    |
    # 100                                  (4)
    #  |                                    ^
    #  v                                    |
    # (2)  ----- 100 --->  (3) <-------100---

    routes = [
        factories.RouteProperties(
            address1=our_address, address2=address1, capacity1to2=TokenAmount(50)
        ),
        factories.RouteProperties(
            address1=our_address, address2=address2, capacity1to2=TokenAmount(100)
        ),
        factories.RouteProperties(
            address1=address4, address2=address1, capacity1to2=TokenAmount(50)
        ),
        factories.RouteProperties(
            address1=address2, address2=address3, capacity1to2=TokenAmount(100)
        ),
        factories.RouteProperties(
            address1=address3,
            address2=address4,
            capacity1to2=TokenAmount(100),
            capacity2to1=TokenAmount(100),
        ),
    ]

    new_state, channels = factories.create_network(
        token_network_state=token_network_state,
        our_address=our_address,
        routes=routes,
        block_number=factories.make_block_number(),
    )

    return new_state, [address1, address2, address3, address4], channels
예제 #27
0
def pack_withdraw(
    canonical_identifier: CanonicalIdentifier,
    participant: Address,
    total_withdraw: WithdrawAmount,
    expiration_block: BlockExpiration,
) -> bytes:
    """Packs withdraw data to be signed

    Packs the given arguments in a byte array in the same configuration the
    contracts expect the signed data to have.
    """
    return proofs.pack_withdraw_message(
        token_network_address=to_hex_address(
            canonical_identifier.token_network_address),
        chain_identifier=canonical_identifier.chain_identifier,
        channel_identifier=canonical_identifier.channel_identifier,
        participant=to_hex_address(participant),
        amount_to_withdraw=TokenAmount(total_withdraw),
        expiration_block=expiration_block,
    )
예제 #28
0
def get_paths(  # pylint: disable=too-many-arguments
    token_network_model: TokenNetwork,
    address_to_reachability: Dict[Address, AddressReachability],
    addresses: List[Address],
    source_index: int = 0,
    target_index: int = 8,
    value: TokenAmount = TokenAmount(10),
    max_paths: int = 5,
    diversity_penalty: float = DIVERSITY_PEN_DEFAULT,
) -> List:
    paths = token_network_model.get_paths(
        diversity_penalty=diversity_penalty,
        source=addresses[source_index],
        target=addresses[target_index],
        value=value,
        max_paths=max_paths,
        address_to_reachability=address_to_reachability,
    )
    index_paths = [addresses_to_indexes(p["path"], addresses) for p in paths]
    return index_paths
예제 #29
0
def test_receive_lockedtransfer_invalidsender(
    raiden_network: List[RaidenService], token_addresses, deposit, reveal_timeout
):
    app0, app1 = raiden_network
    token_address = token_addresses[0]
    other_key, other_address = make_privkey_address()

    token_network_address = views.get_token_network_address_by_token_address(
        views.state_from_raiden(app0), app0.default_registry.address, token_address
    )
    assert token_network_address
    channel0 = get_channelstate(app0, app1, token_network_address)
    lock_amount = LockedAmount(10)
    expiration = reveal_timeout * 2
    mediated_transfer_message = LockedTransfer(
        chain_id=UNIT_CHAIN_ID,
        message_identifier=make_message_identifier(),
        payment_identifier=PaymentID(1),
        nonce=Nonce(1),
        token_network_address=token_network_address,
        token=token_address,
        channel_identifier=channel0.identifier,
        transferred_amount=TokenAmount(0),
        locked_amount=lock_amount,
        recipient=app0.address,
        locksroot=make_locksroot(),
        lock=Lock(
            amount=PaymentWithFeeAmount(lock_amount),
            expiration=expiration,
            secrethash=UNIT_SECRETHASH,
        ),
        target=TargetAddress(app0.address),
        initiator=InitiatorAddress(other_address),
        signature=EMPTY_SIGNATURE,
        metadata=Metadata(routes=[RouteMetadata(route=[app0.address])]),
    )

    sign_and_inject(mediated_transfer_message, LocalSigner(other_key), app0)

    assert_synced_channel_state(token_network_address, app0, deposit, [], app1, deposit, [])
예제 #30
0
def calculate_imbalance_fees(
    channel_capacity: TokenAmount,
    proportional_imbalance_fee: ProportionalFeeAmount
) -> Optional[List[Tuple[TokenAmount, FeeAmount]]]:
    """ Calculates a U-shaped imbalance curve

    The penalty term takes the following value at the extrema:
    channel_capacity * (proportional_imbalance_fee / 1_000_000)
    """
    assert channel_capacity >= 0
    assert proportional_imbalance_fee >= 0

    if proportional_imbalance_fee == 0:
        return None

    if channel_capacity == 0:
        return None

    MAXIMUM_SLOPE = 0.1
    max_imbalance_fee = channel_capacity * proportional_imbalance_fee / 1e6

    assert proportional_imbalance_fee / 1e6 <= MAXIMUM_SLOPE / 2, "Too high imbalance fee"

    # calculate function parameters
    s = MAXIMUM_SLOPE
    c = max_imbalance_fee
    o = channel_capacity / 2
    b = s * o / c
    b = min(b, 10)  # limit exponent to keep numerical stability
    a = c / o**b

    def f(x: TokenAmount) -> FeeAmount:
        return FeeAmount(int(round(a * abs(x - o)**b)))

    # calculate discrete function points
    num_base_points = min(NUM_DISCRETISATION_POINTS, channel_capacity + 1)
    x_values = linspace(TokenAmount(0), channel_capacity, num_base_points)
    y_values = [f(x) for x in x_values]

    return list(zip(x_values, y_values))