Beispiel #1
0
def check_pfs_for_production(service_registry: Optional[ServiceRegistry],
                             pfs_info: PFSInfo) -> None:
    """ Checks that the PFS in `pfs_info` is registered in the service registry
    and that the URL matches.

    Should only be called in production mode.
    """
    if service_registry is None:
        raise RaidenError(
            f"Cannot verify registration of Pathfinding Service because no Service Registry "
            f"is set. Raiden will shut down. Please select a Service Registry."
        )

    pfs_registered = service_registry.has_valid_registration(
        block_identifier=service_registry.client.get_confirmed_blockhash(),
        service_address=pfs_info.payment_address,
    )
    registered_pfs_url = service_registry.get_service_url(
        block_identifier=service_registry.client.get_confirmed_blockhash(),
        service_address=pfs_info.payment_address,
    )
    pfs_url_matches = registered_pfs_url == pfs_info.url
    if not (pfs_registered and pfs_url_matches):
        raise RaidenError(
            f"The Pathfinding Service at {pfs_info.url} is not registered "
            f"with the Service Registry at {to_checksum_address(service_registry.address)} "
            f"or the registered URL ({registered_pfs_url}) doesn't match the given URL "
            f"{pfs_info.url}. "
            f"Raiden will shut down. Please select a registered Pathfinding Service."
        )
Beispiel #2
0
    def __post_init__(self) -> None:
        block_identifier = "latest"
        user_deposit_address = self.user_deposit.address
        token_address = self.user_deposit.token_address(block_identifier)

        monitoring_service_address = self.user_deposit.monitoring_service_address(block_identifier)

        # Validation should only be done if monitoring is enabled or PFS is used
        if (
            self.monitoring_service is None
            or self.service_registry is None
            or self.one_to_n is None
        ):
            return

        if monitoring_service_address != self.monitoring_service.address:
            click.secho(
                f"Monitoring service address linked with the user deposit contract "
                f"{to_checksum_address(monitoring_service_address)} does not match "
                f"the address provided by the monitoring service proxy "
                f"{to_checksum_address(self.monitoring_service.address)}"
            )
        one_to_n_address = self.user_deposit.one_to_n_address(block_identifier)
        if one_to_n_address != self.one_to_n.address:
            click.secho(
                f"OneToN address linked with the user deposit contract "
                f"{to_checksum_address(one_to_n_address)} does not match "
                f"the address provided by the OneToN proxy "
                f"{to_checksum_address(self.one_to_n.address)}"
            )
        service_registry_address = self.monitoring_service.service_registry_address(
            block_identifier
        )
        if service_registry_address != self.service_registry.address:
            click.secho(
                f"The service registry address linked with the monitoring service contract "
                f"{to_checksum_address(service_registry_address)} does not match "
                f"the address provided by the service registry proxy "
                f"{to_checksum_address(self.service_registry.address)}"
            )

        token_address_matches_monitoring_service = (
            token_address == self.monitoring_service.token_address(block_identifier)
        )
        if not token_address_matches_monitoring_service:
            raise RaidenError(
                f"The token used in the provided user deposit contract "
                f"{user_deposit_address} does not match the one in the "
                f"MonitoringService contract {monitoring_service_address}."
            )

        token_address_matches_service_registry = (
            token_address == self.service_registry.token_address(block_identifier)
        )
        if not token_address_matches_service_registry:
            raise RaidenError(
                f"The token used in the provided user deposit contract "
                f"{user_deposit_address} does not match the one in the ServiceRegistry "
                f"contract {monitoring_service_address}."
            )
Beispiel #3
0
def check_ethereum_network_id(given_network_id: ChainID, web3: Web3) -> None:
    """
    Takes the given network id and checks it against the connected network

    If they don't match, exits the program with an error. If they do adds it
    to the configuration and then returns it and whether it is a known network
    """
    node_network_id = ChainID(int(web3.version.network))  # pylint: disable=no-member

    if node_network_id != given_network_id:
        given_name = ID_TO_NETWORKNAME.get(given_network_id)
        network_name = ID_TO_NETWORKNAME.get(node_network_id)

        given_description = f'{given_name or "Unknown"} (id {given_network_id})'
        network_description = f'{network_name or "Unknown"} (id {node_network_id})'

        # TODO: fix cyclic import
        from raiden.ui.cli import ETH_NETWORKID_OPTION

        raise RaidenError(
            f"The configured network {given_description} differs "
            f"from the Ethereum client's network {network_description}. The "
            f"network_id can be configured using the flag {ETH_NETWORKID_OPTION}"
            f"Please check your settings."
        )
Beispiel #4
0
def check_pfs_configuration(pathfinding_service_address: str) -> None:
    if not pathfinding_service_address:
        raise RaidenError(
            "Requested PFS routing mode but no specific pathfinding "
            "service address is provided. Please provide it via the "
            "--pathfinding-service-address argument"
        )
Beispiel #5
0
def check_ethereum_has_accounts(account_manager: AccountManager) -> None:
    if not account_manager.accounts:
        raise RaidenError(
            f"No Ethereum accounts found in the provided keystore directory "
            f"{account_manager.keystore_path}. Please provide a directory "
            f"containing valid ethereum account files."
        )
Beispiel #6
0
def check_raiden_environment(network_id: ChainID,
                             environment_type: Environment) -> None:
    warn = (  # mainnet --development is only for tests
        network_id == 1 and environment_type == Environment.DEVELOPMENT)
    if warn:
        raise RaidenError(
            f"The chosen network ({ID_TO_CHAINNAME[network_id]}) is not a testnet, "
            f'but the "development" environment was selected.\n'
            f"This crashes the node often. Please start again with a safe environment setting "
            f"(--environment-type production).")
Beispiel #7
0
def check_deployed_contracts_data(
    environment_type: Environment,
    node_network_id: ChainID,
    contracts: Dict[str, Address],
    required_contracts: List[str],
) -> None:
    """ This function only checks if all necessary contracts are indeed in the deployment JSON
    from Raiden Contracts. It does not check anything else, especially not if those contracts
    are consistent or in fact Raiden contracts.
    """
    for name in required_contracts:
        if name not in contracts:
            raise RaidenError(
                f"There are no known contract addresses for network id '{node_network_id}'. and "
                f"environment type {environment_type} for contract {name}.")
Beispiel #8
0
def check_ethereum_client_is_supported(web3: Web3) -> None:
    try:
        node_version = web3.clientVersion
    except ValueError:
        raise EthNodeInterfaceError(
            "The underlying ethereum node does not have the web3 rpc interface "
            "enabled. Please run it with --rpcapi eth,net,web3 for geth "
            "and --jsonrpc-apis=eth,net,web3,parity for parity.")

    supported, our_client, our_version = is_supported_client(node_version)
    if not supported:
        raise RaidenError(
            f"You need a Byzantium enabled ethereum node. Parity >= "
            f"{LOWEST_SUPPORTED_PARITY_VERSION} <= {HIGHEST_SUPPORTED_PARITY_VERSION}"
            f" or Geth >= {LOWEST_SUPPORTED_GETH_VERSION} <= {HIGHEST_SUPPORTED_GETH_VERSION}"
            f" but you have {our_version} {our_client}")
Beispiel #9
0
def check_synced(rpc_client: JSONRPCClient) -> None:
    network_id = ChainID(int(rpc_client.web3.version.network))
    network_name = ID_TO_NETWORKNAME.get(network_id)

    if network_name is None:
        raise RaidenError(
            f"Your ethereum client is connected to a non-recognized private "
            f"network with network-ID {network_id}. Since we can not check if the "
            f"client is synced please restart raiden with the --no-sync-check "
            f"argument."
        )

    url = ETHERSCAN_API.format(
        network=network_name if network_id != 1 else "api", action="eth_blockNumber"
    )
    wait_for_sync(
        rpc_client=rpc_client, url=url, tolerance=ORACLE_BLOCKNUMBER_DRIFT_TOLERANCE, sleep=3
    )
Beispiel #10
0
def check_ethereum_confirmed_block_is_not_pruned(
    jsonrpc_client: JSONRPCClient, secret_registry: SecretRegistry, confirmation_blocks: int
) -> None:
    """Checks the Ethereum client is not pruning data too aggressively, because
    in some circunstances it is necessary for a node to fetch additional data
    from the smart contract.
    """
    unconfirmed_block_number = jsonrpc_client.block_number()

    # This is a small error margin. It is possible during normal operation for:
    #
    # - AlarmTask sees a new block and calls RaidenService._callback_new_block
    # - The service gets the current latest block number and computes the
    #   confirmed block number.
    # - The service fetches every filter, this can take a while.
    # - While the above is happening, it is possible for a `few_blocks` to be
    #   mined.
    # - The decode function is called, and tries to access what it thinks is
    #   the latest_confirmed_block, but it is in reality `few_blocks` older.
    #
    # This value bellow is the expected drift, that allows the decode function
    # mentioned above to work properly.
    maximum_delay_to_process_a_block = 2

    minimum_available_history = confirmation_blocks + maximum_delay_to_process_a_block
    target_confirmed_block = unconfirmed_block_number - minimum_available_history

    try:
        # Using the secret registry is arbitrary, any proxy with an `eth_call`
        # would work here.
        secret_registry.get_secret_registration_block_by_secrethash(
            EMPTY_SECRETHASH, block_identifier=target_confirmed_block
        )
    except ValueError:
        # If this exception is raised the Ethereum node is too aggressive with
        # the block pruning.
        raise RaidenError(
            f"The ethereum client does not have the necessary data available. "
            f"The client can not operate because the prunning strategy is too "
            f"agressive. Please make sure that at very minimum "
            f"{minimum_available_history} blocks of history are available."
        )
Beispiel #11
0
def check_pfs_transport_configuration(
    pfs_info: PFSInfo,
    pfs_was_autoselected: bool,
    transport_pfs_broadcast_room_id: str,
    matrix_server_url: str,
    matrix_server_was_autoselected: bool,
) -> None:
    if pfs_info.matrix_room_id is None:
        # Special case until all PFSs are upgraded to >= 0.12.0
        log.warning(
            "Can't check PFS transport configuration",
            pfs_version=pfs_info.version,
            min_required_version="0.12.0",
            pfs_url=pfs_info.url,
        )
        return
    if pfs_info.matrix_room_id != transport_pfs_broadcast_room_id:
        msg = (
            f"The Pathfinding Service at {pfs_info.url} is not connected to the "
            f"same Matrix transport federation as this Raiden node."
        )
        if matrix_server_was_autoselected and pfs_was_autoselected:
            msg += (
                f"\nBoth the Matrix transport server at {matrix_server_url} and the PFS were "
                f"automatically selected. This points to a server-side misconfiguration. "
                f"Please report this issue at "
                f"https://github.com/raiden-network/raiden/issues/new?template=bug_report.md"
            )
        else:
            msg += (
                "\nPlease verify that the Matrix transport server and PFS service you selected "
                "are intended to be used together or use the automatic server selection."
            )
        msg += (
            f"\n\n"
            f"- Transport server PFS broadcast room-id: {transport_pfs_broadcast_room_id}\n"
            f"- PFS server broadcast room-id: {pfs_info.matrix_room_id}"
        )

        raise RaidenError(msg)
Beispiel #12
0
def handle_contract_no_code(name: str, address: Address) -> None:
    hex_addr = to_checksum_address(address)
    raise RaidenError(f"Error: Provided {name} {hex_addr} contract does not contain code")
Beispiel #13
0
def check_account(account_manager: AccountManager, address_hex: Address) -> None:
    if not account_manager.address_in_keystore(to_checksum_address(address_hex)):
        raise RaidenError(
            f"Account '{address_hex}' could not be found on the system. Aborting ..."
        )
Beispiel #14
0
def check_sql_version() -> None:
    if not assert_sqlite_version():
        version = "{}.{}.{}".format(*SQLITE_MIN_REQUIRED_VERSION)
        raise RaidenError(f"SQLite3 should be at least version {version}")
Beispiel #15
0
def handle_contract_wrong_address(name: str, address: Address) -> None:
    hex_addr = to_checksum_address(address)
    raise RaidenError(
        f"Error: Provided address {hex_addr} for {name} contract"
        " does not contain expected code."
    )
Beispiel #16
0
def run_app(
        address: Address,
        keystore_path: str,
        gas_price: Callable,
        eth_rpc_endpoint: str,
        user_deposit_contract_address: Optional[UserDepositAddress],
        api_address: Endpoint,
        rpc: bool,
        rpccorsdomain: str,
        sync_check: bool,
        console: bool,
        password_file: TextIO,
        web_ui: bool,
        datadir: Optional[str],
        matrix_server: str,
        network_id: ChainID,
        environment_type: Environment,
        unrecoverable_error_should_crash: bool,
        pathfinding_service_address: str,
        pathfinding_max_paths: int,
        enable_monitoring: bool,
        resolver_endpoint: str,
        default_reveal_timeout: BlockTimeout,
        default_settle_timeout: BlockTimeout,
        routing_mode: RoutingMode,
        flat_fee: Tuple[Tuple[TokenAddress, FeeAmount], ...],
        proportional_fee: Tuple[Tuple[TokenAddress, ProportionalFeeAmount],
                                ...],
        proportional_imbalance_fee: Tuple[Tuple[TokenAddress,
                                                ProportionalFeeAmount], ...],
        blockchain_query_interval: float,
        cap_mediation_fees: bool,
        **
    kwargs: Any,  # FIXME: not used here, but still receives stuff in smoketest
) -> App:
    # pylint: disable=too-many-locals,too-many-branches,too-many-statements,unused-argument

    token_network_registry_deployed_at: Optional[BlockNumber]
    smart_contracts_start_at: BlockNumber

    if datadir is None:
        datadir = os.path.join(os.path.expanduser("~"), ".raiden")

    account_manager = AccountManager(keystore_path)
    web3 = Web3(HTTPProvider(rpc_normalized_endpoint(eth_rpc_endpoint)))

    check_sql_version()
    check_ethereum_has_accounts(account_manager)
    check_ethereum_client_is_supported(web3)
    check_ethereum_network_id(network_id, web3)

    address, privatekey = get_account_and_private_key(account_manager, address,
                                                      password_file)

    api_host, api_port = split_endpoint(api_address)

    if not api_port:
        api_port = DEFAULT_HTTP_SERVER_PORT

    domain_list = []
    if rpccorsdomain:
        if "," in rpccorsdomain:
            for domain in rpccorsdomain.split(","):
                domain_list.append(str(domain))
        else:
            domain_list.append(str(rpccorsdomain))

    # Set up config
    fee_config = prepare_mediation_fee_config(
        cli_token_to_flat_fee=flat_fee,
        cli_token_to_proportional_fee=proportional_fee,
        cli_token_to_proportional_imbalance_fee=proportional_imbalance_fee,
        cli_cap_mediation_fees=cap_mediation_fees,
    )
    rest_api_config = RestApiConfig(
        rest_api_enabled=rpc,
        web_ui_enabled=rpc and web_ui,
        cors_domain_list=domain_list,
        eth_rpc_endpoint=eth_rpc_endpoint,
        host=api_host,
        port=api_port,
    )

    config = RaidenConfig(
        chain_id=network_id,
        environment_type=environment_type,
        reveal_timeout=default_reveal_timeout,
        settle_timeout=default_settle_timeout,
        console=console,
        mediation_fees=fee_config,
        unrecoverable_error_should_crash=unrecoverable_error_should_crash,
        resolver_endpoint=resolver_endpoint,
        rest_api=rest_api_config,
    )
    config.blockchain.query_interval = blockchain_query_interval
    config.services.monitoring_enabled = enable_monitoring
    config.services.pathfinding_max_paths = pathfinding_max_paths
    config.transport.server = matrix_server

    contracts = load_deployed_contracts_data(config, network_id)

    rpc_client = JSONRPCClient(
        web3=web3,
        privkey=privatekey,
        gas_price_strategy=gas_price,
        block_num_confirmations=DEFAULT_NUMBER_OF_BLOCK_CONFIRMATIONS,
    )

    token_network_registry_deployed_at = None
    if "TokenNetworkRegistry" in contracts:
        token_network_registry_deployed_at = BlockNumber(
            contracts[CONTRACT_TOKEN_NETWORK_REGISTRY]["block_number"])

    if token_network_registry_deployed_at is None:
        smart_contracts_start_at = get_smart_contracts_start_at(network_id)
    else:
        smart_contracts_start_at = token_network_registry_deployed_at

    proxy_manager = ProxyManager(
        rpc_client=rpc_client,
        contract_manager=ContractManager(config.contracts_path),
        metadata=ProxyManagerMetadata(
            token_network_registry_deployed_at=
            token_network_registry_deployed_at,
            filters_start_at=smart_contracts_start_at,
        ),
    )

    api_server: Optional[APIServer] = None
    if config.rest_api.rest_api_enabled:
        api_server = start_api_server(rpc_client=rpc_client,
                                      config=config.rest_api,
                                      eth_rpc_endpoint=eth_rpc_endpoint)

    if sync_check:
        check_synced(rpc_client)

    # The user has the option to launch Raiden with a custom
    # user deposit contract address. This can be used to load
    # the addresses for the rest of the deployed contracts.
    # The steps done here make sure that if a UDC address is provided,
    # the address has to be valid and all the connected contracts
    # are configured properly.
    # If a UDC address was not provided, Raiden would fall back
    # to using the ones deployed and provided by the raiden-contracts package.
    if user_deposit_contract_address is not None:
        if not is_address(user_deposit_contract_address):
            raise RaidenError("The user deposit address is invalid")

        deployed_addresses = load_deployment_addresses_from_udc(
            proxy_manager=proxy_manager,
            user_deposit_address=user_deposit_contract_address,
            block_identifier=BLOCK_ID_LATEST,
        )
    else:
        deployed_addresses = load_deployment_addresses_from_contracts(
            contracts=contracts)

    # Load the available matrix servers when no matrix server is given
    # The list is used in a PFS check
    if config.transport.server == MATRIX_AUTO_SELECT_SERVER:
        fetch_available_matrix_servers(config.transport, environment_type)

    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,
        deployed_addresses=deployed_addresses,
        proxy_manager=proxy_manager,
        routing_mode=routing_mode,
        pathfinding_service_address=pathfinding_service_address,
        enable_monitoring=enable_monitoring,
    )

    check_ethereum_confirmed_block_is_not_pruned(
        jsonrpc_client=rpc_client,
        secret_registry=raiden_bundle.secret_registry,
        confirmation_blocks=config.blockchain.confirmation_blocks,
    )

    database_path = Path(
        os.path.join(
            datadir,
            f"node_{pex(address)}",
            f"netid_{network_id}",
            f"network_{pex(raiden_bundle.token_network_registry.address)}",
            f"v{RAIDEN_DB_VERSION}_log.db",
        ))
    config.database_path = database_path

    print(f"Raiden is running in {environment_type.value.lower()} mode")
    print("\nYou are connected to the '{}' network and the DB path is: {}".
          format(ID_TO_CHAINNAME.get(network_id, network_id), database_path))

    matrix_transport = setup_matrix(config.transport, config.services,
                                    environment_type, routing_mode)

    event_handler: EventHandler = RaidenEventHandler()

    # User should be told how to set fees, if using default fee settings
    log.debug("Fee Settings", fee_settings=fee_config)
    has_default_fees = (len(fee_config.token_to_flat_fee) == 0
                        and len(fee_config.token_to_proportional_fee) == 0
                        and len(fee_config.token_to_proportional_imbalance_fee)
                        == 0)
    if has_default_fees:
        click.secho(
            "Default fee settings are used. "
            "If you want use Raiden with mediation fees - flat, proportional and imbalance fees - "
            "see https://raiden-network.readthedocs.io/en/latest/overview_and_guide.html#firing-it-up",  # noqa: E501
            fg="yellow",
        )

    # Only send feedback when PFS is used
    if routing_mode == RoutingMode.PFS:
        event_handler = PFSFeedbackEventHandler(event_handler)

    message_handler = MessageHandler()

    one_to_n_address = (services_bundle.one_to_n.address
                        if services_bundle.one_to_n is not None else None)
    monitoring_service_address = (services_bundle.monitoring_service.address
                                  if services_bundle.monitoring_service
                                  is not None else None)
    raiden_app = App(
        config=config,
        rpc_client=rpc_client,
        proxy_manager=proxy_manager,
        query_start_block=smart_contracts_start_at,
        default_registry=raiden_bundle.token_network_registry,
        default_secret_registry=raiden_bundle.secret_registry,
        default_service_registry=services_bundle.service_registry,
        default_user_deposit=services_bundle.user_deposit,
        default_one_to_n_address=one_to_n_address,
        default_msc_address=monitoring_service_address,
        transport=matrix_transport,
        raiden_event_handler=event_handler,
        message_handler=message_handler,
        routing_mode=routing_mode,
        api_server=api_server,
    )

    raiden_app.start()

    return raiden_app
Beispiel #17
0
def configure_pfs_or_exit(
    pfs_url: str,
    routing_mode: RoutingMode,
    service_registry: Optional[ServiceRegistry],
    node_network_id: ChainID,
    token_network_registry_address: TokenNetworkRegistryAddress,
    pathfinding_max_fee: TokenAmount,
) -> PFSInfo:
    """
    Take in the given pfs_address argument, the service registry and find out a
    pfs address to use.

    If pfs_url is provided we use that.
    If pfs_url is 'auto' then we randomly choose a PFS address from the registry
    """
    msg = "Invalid code path; configure_pfs needs routing mode PFS"
    assert routing_mode == RoutingMode.PFS, msg

    msg = "With PFS routing mode we shouldn't get to configure_pfs with pfs_address being None"
    assert pfs_url, msg
    if pfs_url == "auto":
        if service_registry is None:
            raise RaidenError(
                "Raiden was started with routing mode set to PFS, the pathfinding service address "
                "set to 'auto' but no service registry address was given. Either specifically "
                "provide a PFS address or provide a service registry address.")

        block_hash = service_registry.client.get_confirmed_blockhash()
        maybe_pfs_url = get_random_pfs(
            service_registry=service_registry,
            block_identifier=block_hash,
            pathfinding_max_fee=pathfinding_max_fee,
        )
        if maybe_pfs_url is None:
            raise RaidenError(
                "The Service Registry has no registered Pathfinding Service "
                "and basic routing is not used.")
        else:
            pfs_url = maybe_pfs_url

    try:
        pathfinding_service_info = get_pfs_info(pfs_url)
    except ServiceRequestFailed as e:
        raise RaidenError(
            f"There was an error with the Pathfinding Service with address "
            f"{pfs_url}. Raiden will shut down. Please try a different Pathfinding Service. \n"
            f"Error Message: {str(e)}")

    if pathfinding_service_info.price > 0 and not pathfinding_service_info.payment_address:
        raise RaidenError(
            f"The Pathfinding Service at {pfs_url} did not provide a payment address. "
            f"Raiden will shut down. Please try a different Pathfinding Service."
        )

    if not node_network_id == pathfinding_service_info.chain_id:
        raise RaidenError(
            f"Invalid reply from Pathfinding Service {pfs_url}\n"
            f"Pathfinding Service is not operating on the same network "
            f"({pathfinding_service_info.chain_id}) as your node is ({node_network_id}).\n"
            f"Raiden will shut down. Please choose a different Pathfinding Service."
        )

    if pathfinding_service_info.token_network_registry_address != token_network_registry_address:
        raise RaidenError(
            f"Invalid reply from Pathfinding Service {pfs_url}"
            f"Pathfinding Service is not operating on the same Token Network Registry "
            f"({to_checksum_address(pathfinding_service_info.token_network_registry_address)})"
            f" as your node ({to_checksum_address(token_network_registry_address)}).\n"
            f"Raiden will shut down. Please choose a different Pathfinding Service."
        )
    click.secho(
        f"You have chosen the Pathfinding Service at {pfs_url}.\n"
        f"Operator: {pathfinding_service_info.operator}, "
        f"running version: {pathfinding_service_info.version}, "
        f"chain_id: {pathfinding_service_info.chain_id}.\n"
        f"Fees will be paid to {to_checksum_address(pathfinding_service_info.payment_address)}. "
        f"Each request costs {to_rdn(pathfinding_service_info.price)} RDN.\n"
        f"Message from the Pathfinding Service:\n{pathfinding_service_info.message}"
    )

    log.info("Using Pathfinding Service", pfs_info=pathfinding_service_info)

    return pathfinding_service_info
Beispiel #18
0
def handle_contract_code_mismatch(mismatch_exception: ContractCodeMismatch) -> None:
    raise RaidenError(f"{str(mismatch_exception)}. Please update your Raiden installation.")