def test_get_privkey(keystore_mock):
    account_manager = AccountManager(keystore_mock)
    assert '0xf696ecb5c767263c797a035db6f6008d38d852960ed33a491a58390b003fb605' == encode_hex(
        account_manager.get_privkey('0d5a0e4fece4b84365b9b8dba6e6d41348c73645',
                                    '123'), )
    assert '0xf696ecb5c767263c797a035db6f6008d38d852960ed33a491a58390b003fb605' == encode_hex(
        account_manager.get_privkey(
            '0x0d5a0e4fece4b84365b9b8dba6e6d41348c73645', '123'), )
    assert '0x36fa966441f259501110ba88f8212dfd7f8bacb07862a7d5cf8f31c1a64551e5' == encode_hex(
        account_manager.get_privkey('3593403033d18b82f7b4a0f18e1ed24623d23b20',
                                    '123'), )
    assert '0x36fa966441f259501110ba88f8212dfd7f8bacb07862a7d5cf8f31c1a64551e5' == encode_hex(
        account_manager.get_privkey(
            '0x3593403033d18b82f7b4a0f18e1ed24623d23b20', '123'), )

    # failures
    with pytest.raises(ValueError) as exc:
        account_manager.get_privkey(
            '0x3593403033d18b82f7b4a0f18e1ed24623d23b20', '456')
    assert 'MAC mismatch' in str(exc.value)
    with pytest.raises(ValueError) as exc:
        account_manager.get_privkey('a05934d3033d18b82f7b4adf18e1ed24e3d23b19',
                                    '123')
    assert (
        'Keystore file not found for 0xa05934d3033d18b82f7b4adf18e1ed24e3d23b19'
        in str(exc.value))
Exemple #2
0
def get_private_key(keystore):
    accmgr = AccountManager(keystore)
    if not accmgr.accounts:
        raise RuntimeError("No Ethereum accounts found in the user's system")

    addresses = list(accmgr.accounts.keys())
    return accmgr.get_privkey(addresses[0], DEFAULT_PASSPHRASE)
Exemple #3
0
def get_private_key():
    keystore = os.path.join(os.environ['RST_DATADIR'], 'keystore')
    accmgr = AccountManager(keystore)
    if not accmgr.accounts:
        raise RuntimeError('No Ethereum accounts found in the user\'s system')

    addresses = list(accmgr.accounts.keys())
    return accmgr.get_privkey(addresses[0], TEST_ACCOUNT_PASSWORD)
Exemple #4
0
def get_private_key():
    keystore = os.path.join(os.environ['RST_DATADIR'], 'keystore')
    accmgr = AccountManager(keystore)
    if not accmgr.accounts:
        raise RuntimeError('No Ethereum accounts found in the user\'s system')

    addresses = list(accmgr.accounts.keys())
    return accmgr.get_privkey(addresses[0], TEST_ACCOUNT_PASSWORD)
Exemple #5
0
def matrix_api_shell(address, password, server):
    am = AccountManager(os.path.expanduser("~/.ethereum/keystore"))
    signer = LocalSigner(am.get_privkey(to_checksum_address(address),
                                        password))
    server_name = server.split("//")[1]
    matrix_password = encode_hex(signer.sign(server_name.encode()))

    api = GMatrixHttpApi(server)
    resp = api.login("m.login.password",
                     user=to_normalized_address(address),
                     password=matrix_password)
    api.token = resp["access_token"]
    IPython.embed(
        header=f"Use the `api` object to interact with matrix on {server}.")
Exemple #6
0
def get_login(address, password) -> None:
    path = os.path.expanduser("~/.ethereum/keystore")
    if sys.platform.startswith("darwin"):
        path = os.path.expanduser("~/Library/Ethereum/keystore")

    am = AccountManager(path)
    signer = LocalSigner(am.get_privkey(to_checksum_address(address), password))

    print(f"Username: {to_normalized_address(address)}")
    print("Password:"******"\ttransport {i:02d}:",
            encode_hex(signer.sign(f"transport{i:02d}.raiden.network".encode())),
        )
Exemple #7
0
def check_account(account_manager: AccountManager, address_hex: AddressHex) -> None:
    if not account_manager.address_in_keystore(address_hex):
        click.secho(
            f"Account '{address_hex}' could not be found on the system. Aborting ...",
            fg='red',
        )
        sys.exit(1)
Exemple #8
0
def test_get_account_in_keystore():
    account_manager = AccountManager(test_keystore)
    assert account_manager.address_in_keystore('0d5a0e4fece4b84365b9b8dba6e6d41348c73645')
    assert account_manager.address_in_keystore('0x0d5a0e4fece4b84365b9b8dba6e6d41348c73645')
    assert account_manager.address_in_keystore('3593403033d18b82f7b4a0f18e1ed24623d23b20')
    assert account_manager.address_in_keystore('0x3593403033d18b82f7b4a0f18e1ed24623d23b20')
    assert not account_manager.address_in_keystore('a05934d3033d18b82f7b4adf18e1ed24e3d23b19')
Exemple #9
0
def unlock_account_with_passwordfile(
    account_manager: AccountManager, address_hex: AddressHex, password_file: TextIO
) -> PrivateKey:
    password = password_file.read().strip("\r\n")

    try:
        return account_manager.get_privkey(address_hex, password.strip())
    except ValueError:
        raise KeystoreAuthenticationError(f"Incorrect password for {address_hex} in file.")
def test_unlock_account_with_passwordfile(keystore_mock):
    account_manager = AccountManager(keystore_mock)
    password_file_path = os.path.join(keystore_mock, "passwordfile.txt")

    with open(password_file_path, "r") as password_file:
        privkey = unlock_account_with_passwordfile(
            account_manager=account_manager,
            address_hex="0x0d5a0e4FECE4b84365b9B8DbA6e6D41348C73645",
            password_file=password_file,
        )
    assert privkey
Exemple #11
0
def unlock_account_with_passwordfile(account_manager: AccountManager,
                                     address_hex: AddressHex,
                                     password_file: TextIO) -> PrivateKey:
    password = password_file.read().strip("\r\n")

    try:
        return account_manager.get_privkey(address_hex, password.strip())
    except ValueError:
        click.secho(
            f"Incorrect password for {address_hex} in file. Aborting ...",
            fg="red")
        sys.exit(1)
Exemple #12
0
def test_get_accounts(test_keystore):
    account_manager = AccountManager(test_keystore)
    expected_accounts = {
        '0d5a0e4fece4b84365b9b8dba6e6d41348c73645':
        os.path.join(
            test_keystore,
            'UTC--2016-10-26T16-55-53.551024336Z--0d5a0e4fece4b84365b9b8dba6e6d41348c73645'
        ),
        '3593403033d18b82f7b4a0f18e1ed24623d23b20':
        os.path.join(test_keystore, 'valid_keystorefile_with_unexpected_name')
    }
    assert expected_accounts == account_manager.accounts
def test_get_accounts(keystore_mock):
    account_manager = AccountManager(keystore_mock)
    expected_accounts = {
        "0x0d5a0e4FECE4b84365b9B8DbA6e6D41348C73645": os.path.join(
            keystore_mock,
            "UTC--2016-10-26T16-55-53.551024336Z--0d5a0e4fece4b84365b9b8dba6e6d41348c73645",
        ),
        "0x3593403033d18b82f7b4a0F18e1ED24623D23b20": os.path.join(
            keystore_mock, "valid_keystorefile_with_unexpected_name"
        ),
    }
    assert expected_accounts == account_manager.accounts
Exemple #14
0
def test_get_account_in_keystore(keystore_mock):
    account_manager = AccountManager(keystore_mock)
    assert account_manager.address_in_keystore(
        "0d5a0e4fece4b84365b9b8dba6e6d41348c73645")
    assert account_manager.address_in_keystore(
        "0x0d5a0e4fece4b84365b9b8dba6e6d41348c73645")
    assert account_manager.address_in_keystore(
        "0x0D5A0E4fece4b84365b9b8dba6e6d41348c73645")
    assert account_manager.address_in_keystore(
        "3593403033d18b82f7b4a0f18e1ed24623d23b20")
    assert account_manager.address_in_keystore(
        "0x3593403033d18b82f7b4a0f18e1ed24623d23b20")
    assert not account_manager.address_in_keystore(
        "a05934d3033d18b82f7b4adf18e1ed24e3d23b19")
def test_account_manager_invalid_directory(caplog):
    with patch.object(os, 'listdir') as mock_listdir:
        mock_listdir.side_effect = OSError
        AccountManager('/some/path')

    for msg, path, reason in [('Unable to list the specified directory',
                               '/some/path', '')]:
        for record in caplog.records:
            message = record.getMessage()
            if msg in message and path in message and reason in message:
                break
        else:
            assert False, "'{}' not in log messages".format(msg)
Exemple #16
0
def test_account_manager_invalid_files(test_keystore, caplog):
    with caplog.at_level(logging.DEBUG):
        AccountManager(test_keystore)

    for msg, file_name, reason in [
        ('Invalid account file', KEYFILE_INVALID, 'Expecting value: line 1 column 1 (char 0)'),
        ('Can not read account file', KEYFILE_INACCESSIBLE, 'Permission denied')
    ]:
        for record in caplog.records:
            message = record.getMessage()
            if msg in message and file_name in message and reason in message:
                break
        else:
            assert False, "'{}' not in log messages".format(msg)
def test_account_manager_invalid_directory(caplog):
    with patch.object(os, "listdir") as mock_listdir:
        mock_listdir.side_effect = OSError
        AccountManager("/some/path")

    logs = [("Unable to list the specified directory", "/some/path", "")]

    for msg, path, reason in logs:
        for record in caplog.records:
            message = record.getMessage()
            if msg in message and path in message and reason in message:
                break
        else:
            assert False, f"'{msg}' not in log messages"
Exemple #18
0
def test_get_account_in_keystore(keystore_mock):
    account_manager = AccountManager(keystore_mock)
    assert account_manager.address_in_keystore(
        "0x0d5a0e4FECE4b84365b9B8DbA6e6D41348C73645")
    assert account_manager.address_in_keystore(
        "0x3593403033d18b82f7b4a0F18e1ED24623D23b20")
    assert not account_manager.address_in_keystore(
        "0xa05934d3033D18b82F7b4AdF18E1eD24E3D23b19")
    assert not account_manager.address_in_keystore(None)
Exemple #19
0
def unlock_account_with_passwordfile(
        account_manager: AccountManager,
        address_hex: AddressHex,
        password_file: io.RawIOBase,
) -> PrivateKey:
    password = password_file.read()

    try:
        return account_manager.get_privkey(address_hex, password)
    except ValueError:
        click.secho(
            f'Incorrect password for {address_hex} in file. Aborting ...',
            fg='red',
        )
        sys.exit(1)
Exemple #20
0
def test_account_manager_invalid_files(test_keystore, caplog):
    with caplog.at_level(logging.DEBUG):
        AccountManager(test_keystore)

    for msg, file_name, reason in [
        ("Invalid account file", KEYFILE_INVALID, "No JSON object"),
        ("Can not read account file", KEYFILE_INACCESSIBLE,
         "Permission denied")
    ]:
        for record in caplog.records:
            message = record.getMessage()
            if msg in message and file_name in message and reason in message:
                break
        else:
            assert False, "'{}' not in log messages".format(msg)
Exemple #21
0
def unlock_account_with_passwordprompt(account_manager: AccountManager,
                                       address_hex: AddressHex) -> PrivateKey:
    tries = 3
    for current in range(tries):
        try:
            checksum_address = to_checksum_address(address_hex)
            password = getpass.getpass(
                f"Enter the password to unlock {checksum_address}: ")
            return account_manager.get_privkey(address_hex, password)
        except ValueError:
            print(f"Incorrect passphrase to unlock the private key. "
                  f"{current} out of {tries} tries. "
                  f"Please try again or kill the process to quit. "
                  f"Usually Ctrl-c.")

    sys.exit(1)
def test_get_privkey(keystore_mock):
    account_manager = AccountManager(keystore_mock)
    assert "0xf696ecb5c767263c797a035db6f6008d38d852960ed33a491a58390b003fb605" == encode_hex(
        account_manager.get_privkey("0x0d5a0e4FECE4b84365b9B8DbA6e6D41348C73645", "123")
    )
    assert "0x36fa966441f259501110ba88f8212dfd7f8bacb07862a7d5cf8f31c1a64551e5" == encode_hex(
        account_manager.get_privkey("0x3593403033d18b82f7b4a0F18e1ED24623D23b20", "123")
    )

    # failures
    with pytest.raises(ValueError) as exc:
        account_manager.get_privkey("0x3593403033d18b82f7b4a0F18e1ED24623D23b20", "456")
    assert "MAC mismatch" in str(exc.value)
    with pytest.raises(KeystoreFileNotFound) as exc:
        account_manager.get_privkey("0xa05934d3033D18b82F7b4AdF18E1eD24E3D23b19", "123")
    assert "Keystore file not found for 0xa05934d3033D18b82F7b4AdF18E1eD24E3D23b19" in str(
        exc.value
    )
def test_get_privkey():
    account_manager = AccountManager(test_keystore)
    'f696ecb5c767263c797a035db6f6008d38d852960ed33a491a58390b003fb605' == encode_hex(
        account_manager.get_privkey('0d5a0e4fece4b84365b9b8dba6e6d41348c73645', '123')
    )
    'f696ecb5c767263c797a035db6f6008d38d852960ed33a491a58390b003fb605' == encode_hex(
        account_manager.get_privkey('0x0d5a0e4fece4b84365b9b8dba6e6d41348c73645', '123')
    )
    '36fa966441f259501110ba88f8212dfd7f8bacb07862a7d5cf8f31c1a64551e5' == encode_hex(
        account_manager.get_privkey('3593403033d18b82f7b4a0f18e1ed24623d23b20', '123')
    )
    '36fa966441f259501110ba88f8212dfd7f8bacb07862a7d5cf8f31c1a64551e5' == encode_hex(
        account_manager.get_privkey('0x3593403033d18b82f7b4a0f18e1ed24623d23b20', '123')
    )

    # failures
    with pytest.raises(ValueError) as exc:
        account_manager.get_privkey('0x3593403033d18b82f7b4a0f18e1ed24623d23b20', '456')
    assert 'MAC mismatch. Password incorrect?' in exc.value
    with pytest.raises(ValueError) as exc:
        account_manager.get_privkey('a05934d3033d18b82f7b4adf18e1ed24e3d23b19', '123')
    assert 'Keystore file not found for a05934d3033d18b82f7b4adf18e1ed24e3d23b19' in exc.value
def test_account_manager_invalid_files(keystore_mock, caplog):
    with caplog.at_level(logging.DEBUG):
        AccountManager(keystore_mock)

    logs = [
        (
            "The account file is not valid JSON format",
            KEYFILE_INVALID,
            "Expecting value: line 1 column 1 (char 0)",
        ),
        ("Can not read account file (errno=13)", KEYFILE_INACCESSIBLE, "Permission denied"),
    ]

    for msg, file_name, reason in logs:
        for record in caplog.records:
            message = record.getMessage()
            if msg in message and file_name in message and reason in message:
                break
        else:
            assert False, f"'{msg}' not in log messages"
Exemple #25
0
def unlock_account_with_passwordprompt(
        account_manager: AccountManager,
        address_hex: AddressHex,
) -> PrivateKey:
    tries = 3
    for current in range(tries):
        try:
            password = getpass.getpass(
                f'Enter the password to unlock {address_hex}: ',
            )
            return account_manager.get_privkey(address_hex, password)
        except ValueError:
            print(
                f'Incorrect passphrase to unlock the private key. '
                f'{current} out of {tries} tries. '
                f'Please try again or kill the process to quit. '
                f'Usually Ctrl-c.',
            )

    sys.exit(1)
Exemple #26
0
def app(address, keystore_path, eth_rpc_endpoint, registry_contract_address,
        discovery_contract_address, listen_address, logging, logfile,
        max_unresponsive_time, send_ping_time):

    slogging.configure(logging, log_file=logfile)

    # config_file = args.config_file
    (listen_host, listen_port) = split_endpoint(listen_address)

    config = App.default_config.copy()
    config['host'] = listen_host
    config['port'] = listen_port
    config['max_unresponsive_time'] = max_unresponsive_time
    config['send_ping_time'] = send_ping_time

    accmgr = AccountManager(keystore_path)
    if not accmgr.accounts:
        raise RuntimeError('No Ethereum accounts found in the user\'s system')

    if not accmgr.address_in_keystore(address):
        addresses = list(accmgr.accounts.keys())
        formatted_addresses = [
            '[{:3d}] - 0x{}'.format(idx, addr)
            for idx, addr in enumerate(addresses)
        ]

        should_prompt = True
        while should_prompt:
            idx = click.prompt(
                "The following accounts were found in your machine:\n\n{}"
                "\nSelect one of them by index to continue: ".format(
                    "\n".join(formatted_addresses)),
                type=int)
            if idx >= 0 and idx < len(addresses):
                should_prompt = False
            else:
                print("\nError: Provided index '{}' is out of bounds\n".format(
                    idx))

        address = addresses[idx]

    privatekey = accmgr.get_privkey(address)
    config['privatekey_hex'] = encode_hex(privatekey)

    endpoint = eth_rpc_endpoint

    if eth_rpc_endpoint.startswith("http://"):
        endpoint = eth_rpc_endpoint[len("http://"):]
        rpc_port = 80
    elif eth_rpc_endpoint.startswith("https://"):
        endpoint = eth_rpc_endpoint[len("https://"):]
        rpc_port = 443

    if ':' not in endpoint:  # no port was given in url
        rpc_host = endpoint
    else:
        rpc_host, rpc_port = split_endpoint(endpoint)

    blockchain_service = BlockChainService(
        privatekey,
        decode_hex(registry_contract_address),
        host=rpc_host,
        port=rpc_port,
    )

    discovery = ContractDiscovery(
        blockchain_service.node_address,
        blockchain_service.discovery(discovery_contract_address))

    return App(config, blockchain_service, discovery)
Exemple #27
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 ..."
        )
Exemple #28
0
def run_app(
    address,
    keystore_path,
    gas_price,
    eth_rpc_endpoint,
    tokennetwork_registry_contract_address,
    secret_registry_contract_address,
    service_registry_contract_address,
    endpoint_registry_contract_address,
    user_deposit_contract_address,
    listen_address,
    mapped_socket,
    max_unresponsive_time,
    api_address,
    rpc,
    sync_check,
    console,
    password_file,
    web_ui,
    datadir,
    transport,
    matrix_server,
    network_id,
    environment_type,
    unrecoverable_error_should_crash,
    pathfinding_service_address,
    pathfinding_eth_address,
    pathfinding_max_paths,
    enable_monitoring,
    resolver_endpoint,
    routing_mode,
    config=None,
    extra_config=None,
    **kwargs,
):
    # pylint: disable=too-many-locals,too-many-branches,too-many-statements,unused-argument

    from raiden.app import App

    check_sql_version()

    if transport == "udp" and not mapped_socket:
        raise RuntimeError("Missing socket")

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

    account_manager = AccountManager(keystore_path)
    check_has_accounts(account_manager)

    if not address:
        address_hex = prompt_account(account_manager)
    else:
        address_hex = to_normalized_address(address)

    if password_file:
        privatekey_bin = unlock_account_with_passwordfile(
            account_manager=account_manager,
            address_hex=address_hex,
            password_file=password_file)
    else:
        privatekey_bin = unlock_account_with_passwordprompt(
            account_manager=account_manager, address_hex=address_hex)

    address = to_canonical_address(address_hex)

    (listen_host, listen_port) = split_endpoint(listen_address)
    (api_host, api_port) = split_endpoint(api_address)

    config["transport"]["udp"]["host"] = listen_host
    config["transport"]["udp"]["port"] = listen_port
    config["console"] = console
    config["rpc"] = rpc
    config["web_ui"] = rpc and web_ui
    config["api_host"] = api_host
    config["api_port"] = api_port
    config["resolver_endpoint"] = resolver_endpoint
    if mapped_socket:
        config["socket"] = mapped_socket.socket
        config["transport"]["udp"]["external_ip"] = mapped_socket.external_ip
        config["transport"]["udp"][
            "external_port"] = mapped_socket.external_port
    config["transport_type"] = transport
    config["transport"]["matrix"]["server"] = matrix_server
    config["transport"]["udp"][
        "nat_keepalive_retries"] = DEFAULT_NAT_KEEPALIVE_RETRIES
    timeout = max_unresponsive_time / DEFAULT_NAT_KEEPALIVE_RETRIES
    config["transport"]["udp"]["nat_keepalive_timeout"] = timeout
    config[
        "unrecoverable_error_should_crash"] = unrecoverable_error_should_crash
    config["services"]["pathfinding_max_paths"] = pathfinding_max_paths
    config["services"]["monitoring_enabled"] = enable_monitoring

    parsed_eth_rpc_endpoint = urlparse(eth_rpc_endpoint)
    if not parsed_eth_rpc_endpoint.scheme:
        eth_rpc_endpoint = f"http://{eth_rpc_endpoint}"

    web3 = Web3(HTTPProvider(eth_rpc_endpoint))
    check_ethereum_version(web3)
    check_network_id(network_id, web3)
    config["chain_id"] = network_id

    setup_environment(config, environment_type)

    contracts = setup_contracts_or_exit(config, network_id)

    rpc_client = JSONRPCClient(
        web3,
        privatekey_bin,
        gas_price_strategy=gas_price,
        block_num_confirmations=DEFAULT_NUMBER_OF_BLOCK_CONFIRMATIONS,
        uses_infura="infura.io" in eth_rpc_endpoint,
    )

    blockchain_service = BlockChainService(jsonrpc_client=rpc_client,
                                           contract_manager=ContractManager(
                                               config["contracts_path"]))

    if sync_check:
        check_synced(blockchain_service)

    proxies = setup_proxies_or_exit(
        config=config,
        tokennetwork_registry_contract_address=
        tokennetwork_registry_contract_address,
        secret_registry_contract_address=secret_registry_contract_address,
        endpoint_registry_contract_address=endpoint_registry_contract_address,
        user_deposit_contract_address=user_deposit_contract_address,
        service_registry_contract_address=service_registry_contract_address,
        blockchain_service=blockchain_service,
        contracts=contracts,
        routing_mode=routing_mode,
        pathfinding_service_address=pathfinding_service_address,
        pathfinding_eth_address=pathfinding_eth_address,
    )

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

    print("\nYou are connected to the '{}' network and the DB path is: {}".
          format(ID_TO_NETWORKNAME.get(network_id, network_id), database_path))

    discovery = None
    if transport == "udp":
        transport, discovery = setup_udp_or_exit(
            config, blockchain_service, address, contracts,
            endpoint_registry_contract_address)
    elif transport == "matrix":
        transport = _setup_matrix(config)
    else:
        raise RuntimeError(f'Unknown transport type "{transport}" given')

    raiden_event_handler = RaidenEventHandler()

    message_handler = MessageHandler()

    try:
        start_block = 0
        if "TokenNetworkRegistry" in contracts:
            start_block = contracts["TokenNetworkRegistry"]["block_number"]

        raiden_app = App(
            config=config,
            chain=blockchain_service,
            query_start_block=start_block,
            default_registry=proxies.token_network_registry,
            default_secret_registry=proxies.secret_registry,
            default_service_registry=proxies.service_registry,
            transport=transport,
            raiden_event_handler=raiden_event_handler,
            message_handler=message_handler,
            discovery=discovery,
            user_deposit=proxies.user_deposit,
        )
    except RaidenError as e:
        click.secho(f"FATAL: {e}", fg="red")
        sys.exit(1)

    try:
        raiden_app.start()
    except RuntimeError as e:
        click.secho(f"FATAL: {e}", fg="red")
        sys.exit(1)
    except filelock.Timeout:
        name_or_id = ID_TO_NETWORKNAME.get(network_id, network_id)
        click.secho(
            f"FATAL: Another Raiden instance already running for account {address_hex} on "
            f"network id {name_or_id}",
            fg="red",
        )
        sys.exit(1)

    return raiden_app
Exemple #29
0
def prompt_account(address_hex, keystore_path, password_file):
    accmgr = AccountManager(keystore_path)
    if not accmgr.accounts:
        print(
            'No Ethereum accounts found in the provided keystore directory {}. '
            'Please provide a directory containing valid ethereum account '
            'files.'.format(keystore_path),
        )
        sys.exit(1)

    if not accmgr.address_in_keystore(address_hex):
        # check if an address has been passed
        if address_hex is not None:
            print("Account '{}' could not be found on the system. Aborting ...".format(
                address_hex))
            sys.exit(1)

        addresses = list(accmgr.accounts.keys())
        formatted_addresses = [
            '[{:3d}] - {}'.format(idx, to_checksum_address(addr))
            for idx, addr in enumerate(addresses)
        ]

        should_prompt = True

        print('The following accounts were found in your machine:')
        print('')
        print('\n'.join(formatted_addresses))
        print('')

        while should_prompt:
            idx = click.prompt('Select one of them by index to continue', type=int)

            if 0 <= idx < len(addresses):
                should_prompt = False
            else:
                print('\nError: Provided index "{}" is out of bounds\n'.format(idx))

        address_hex = addresses[idx]

    password = None
    if password_file:
        password = password_file.read()
        if password != '':
            password = password.splitlines()[0]
    if password is not None:
        try:
            privatekey_bin = accmgr.get_privkey(address_hex, password)
        except ValueError:
            # ValueError exception raised if the password is incorrect
            print('Incorrect password for {} in file. Aborting ...'.format(address_hex))
            sys.exit(1)
    else:
        unlock_tries = 3
        while True:
            try:
                privatekey_bin = accmgr.get_privkey(address_hex)
                break
            except ValueError:
                # ValueError exception raised if the password is incorrect
                if unlock_tries == 0:
                    print(
                        'Exhausted passphrase unlock attempts for {}. Aborting ...'
                        .format(address_hex),
                    )
                    sys.exit(1)

                print(
                    'Incorrect passphrase to unlock the private key. {} tries remaining. '
                    'Please try again or kill the process to quit. '
                    'Usually Ctrl-c.'.format(unlock_tries),
                )
                unlock_tries -= 1

    return address_hex, privatekey_bin
Exemple #30
0
def app(
        address,  # pylint: disable=too-many-arguments,too-many-locals
        keystore_path,
        eth_rpc_endpoint,
        registry_contract_address,
        discovery_contract_address,
        listen_address,
        socket,
        logging,
        logfile,
        max_unresponsive_time,
        send_ping_time,
        api_port,
        rpc,
        console):

    slogging.configure(logging, log_file=logfile)

    # config_file = args.config_file
    (listen_host, listen_port) = split_endpoint(listen_address)

    config = App.default_config.copy()
    config['host'] = listen_host
    config['port'] = listen_port
    config['max_unresponsive_time'] = max_unresponsive_time
    config['send_ping_time'] = send_ping_time
    config['console'] = console
    config['rpc'] = rpc
    config['api_port'] = api_port
    config['socket'] = socket

    accmgr = AccountManager(keystore_path)
    if not accmgr.accounts:
        raise RuntimeError('No Ethereum accounts found in the user\'s system')

    if not accmgr.address_in_keystore(address):
        addresses = list(accmgr.accounts.keys())
        formatted_addresses = [
            '[{:3d}] - 0x{}'.format(idx, addr)
            for idx, addr in enumerate(addresses)
        ]

        should_prompt = True

        print('The following accounts were found in your machine:')
        print('')
        print('\n'.join(formatted_addresses))
        print('')

        while should_prompt:
            idx = click.prompt('Select one of them by index to continue',
                               type=int)

            if idx >= 0 and idx < len(addresses):
                should_prompt = False
            else:
                print("\nError: Provided index '{}' is out of bounds\n".format(
                    idx))

        address = addresses[idx]

    unlock_tries = 3
    while True:
        try:
            privatekey_bin = accmgr.get_privkey(address)
            break
        except ValueError as e:
            # ValueError exception raised if the password is incorrect
            if unlock_tries == 0:
                print(
                    'Exhausted passphrase unlock attempts for {}. Aborting ...'
                    .format(address))
                sys.exit(1)

            print(
                'Incorrect passphrase to unlock the private key. {} tries remaining. '
                'Please try again or kill the process to quit. '
                'Usually Ctrl-c.'.format(unlock_tries))
            unlock_tries -= 1

    privatekey_hex = privatekey_bin.encode('hex')
    config['privatekey_hex'] = privatekey_hex

    endpoint = eth_rpc_endpoint

    if eth_rpc_endpoint.startswith("http://"):
        endpoint = eth_rpc_endpoint[len("http://"):]
        rpc_port = 80
    elif eth_rpc_endpoint.startswith("https://"):
        endpoint = eth_rpc_endpoint[len("https://"):]
        rpc_port = 443

    if ':' not in endpoint:  # no port was given in url
        rpc_host = endpoint
    else:
        rpc_host, rpc_port = split_endpoint(endpoint)

    # user may have provided registry and discovery contracts with leading 0x
    registry_contract_address = address_decoder(registry_contract_address)
    discovery_contract_address = address_decoder(discovery_contract_address)

    try:
        blockchain_service = BlockChainService(
            privatekey_bin,
            registry_contract_address,
            host=rpc_host,
            port=rpc_port,
        )
    except ValueError as e:
        # ValueError exception raised if:
        # - The registry contract address doesn't have code, this might happen
        # if the connected geth process is not synced or if the wrong address
        # is provided (e.g. using the address from a smart contract deployed on
        # ropsten with a geth node connected to morden)
        print(e.message)
        sys.exit(1)

    discovery = ContractDiscovery(
        blockchain_service.node_address,
        blockchain_service.discovery(discovery_contract_address))

    # default database directory
    raiden_directory = os.path.join(os.path.expanduser('~'), '.raiden')
    if not os.path.exists(raiden_directory):
        os.makedirs(raiden_directory)
    database_path = os.path.join(raiden_directory, 'log.db')
    config['database_path'] = database_path

    return App(config, blockchain_service, discovery)
Exemple #31
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
Exemple #32
0
def run_app(
    address,
    keystore_path,
    gas_price,
    eth_rpc_endpoint,
    tokennetwork_registry_contract_address,
    secret_registry_contract_address,
    service_registry_contract_address,
    endpoint_registry_contract_address,
    user_deposit_contract_address,
    listen_address,
    mapped_socket,
    max_unresponsive_time,
    api_address,
    rpc,
    sync_check,
    console,
    password_file,
    web_ui,
    datadir,
    transport,
    matrix_server,
    network_id,
    environment_type,
    unrecoverable_error_should_crash,
    pathfinding_service_address,
    pathfinding_eth_address,
    pathfinding_max_paths,
    enable_monitoring,
    routing_mode,
    config=None,
    extra_config=None,
    **kwargs,
):
    # pylint: disable=too-many-locals,too-many-branches,too-many-statements,unused-argument

    from raiden.app import App

    _assert_sql_version()

    if transport == 'udp' and not mapped_socket:
        raise RuntimeError('Missing socket')

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

    account_manager = AccountManager(keystore_path)
    check_has_accounts(account_manager)

    if not address:
        address_hex = prompt_account(account_manager)
    else:
        address_hex = to_normalized_address(address)

    if password_file:
        privatekey_bin = unlock_account_with_passwordfile(
            account_manager,
            address_hex,
            password_file,
        )
    else:
        privatekey_bin = unlock_account_with_passwordprompt(
            account_manager,
            address_hex,
        )

    address = to_canonical_address(address_hex)

    (listen_host, listen_port) = split_endpoint(listen_address)
    (api_host, api_port) = split_endpoint(api_address)

    config['transport']['udp']['host'] = listen_host
    config['transport']['udp']['port'] = listen_port
    config['console'] = console
    config['rpc'] = rpc
    config['web_ui'] = rpc and web_ui
    config['api_host'] = api_host
    config['api_port'] = api_port
    if mapped_socket:
        config['socket'] = mapped_socket.socket
        config['transport']['udp']['external_ip'] = mapped_socket.external_ip
        config['transport']['udp'][
            'external_port'] = mapped_socket.external_port
    config['transport_type'] = transport
    config['transport']['matrix']['server'] = matrix_server
    config['transport']['udp'][
        'nat_keepalive_retries'] = DEFAULT_NAT_KEEPALIVE_RETRIES
    timeout = max_unresponsive_time / DEFAULT_NAT_KEEPALIVE_RETRIES
    config['transport']['udp']['nat_keepalive_timeout'] = timeout
    config[
        'unrecoverable_error_should_crash'] = unrecoverable_error_should_crash
    config['services']['pathfinding_max_paths'] = pathfinding_max_paths
    config['services']['monitoring_enabled'] = enable_monitoring

    parsed_eth_rpc_endpoint = urlparse(eth_rpc_endpoint)
    if not parsed_eth_rpc_endpoint.scheme:
        eth_rpc_endpoint = f'http://{eth_rpc_endpoint}'

    web3 = _setup_web3(eth_rpc_endpoint)
    node_network_id, known_node_network_id = setup_network_id_or_exit(
        config, network_id, web3)

    environment_type = setup_environment(config, environment_type)

    contracts, contract_addresses_known = setup_contracts_or_exit(
        config, node_network_id)

    rpc_client = JSONRPCClient(
        web3,
        privatekey_bin,
        gas_price_strategy=gas_price,
        block_num_confirmations=DEFAULT_NUMBER_OF_BLOCK_CONFIRMATIONS,
        uses_infura='infura.io' in eth_rpc_endpoint,
    )

    blockchain_service = BlockChainService(
        jsonrpc_client=rpc_client,
        contract_manager=ContractManager(config['contracts_path']),
    )

    if sync_check:
        check_synced(blockchain_service, known_node_network_id)

    proxies = setup_proxies_or_exit(
        config=config,
        tokennetwork_registry_contract_address=
        tokennetwork_registry_contract_address,
        secret_registry_contract_address=secret_registry_contract_address,
        endpoint_registry_contract_address=endpoint_registry_contract_address,
        user_deposit_contract_address=user_deposit_contract_address,
        service_registry_contract_address=service_registry_contract_address,
        contract_addresses_known=contract_addresses_known,
        blockchain_service=blockchain_service,
        contracts=contracts,
        routing_mode=routing_mode,
        pathfinding_service_address=pathfinding_service_address,
        pathfinding_eth_address=pathfinding_eth_address,
    )

    database_path = os.path.join(
        datadir,
        f'node_{pex(address)}',
        f'netid_{node_network_id}',
        f'network_{pex(proxies.token_network_registry.address)}',
        f'v{RAIDEN_DB_VERSION}_log.db',
    )
    config['database_path'] = database_path

    print(
        '\nYou are connected to the \'{}\' network and the DB path is: {}'.
        format(
            ID_TO_NETWORKNAME.get(node_network_id, node_network_id),
            database_path,
        ), )

    discovery = None
    if transport == 'udp':
        transport, discovery = setup_udp_or_exit(
            config,
            blockchain_service,
            address,
            contracts,
            endpoint_registry_contract_address,
        )
    elif transport == 'matrix':
        transport = _setup_matrix(config)
    else:
        raise RuntimeError(f'Unknown transport type "{transport}" given')

    raiden_event_handler = RaidenEventHandler()

    message_handler = MessageHandler()

    try:
        start_block = 0
        if 'TokenNetworkRegistry' in contracts:
            start_block = contracts['TokenNetworkRegistry']['block_number']

        raiden_app = App(
            config=config,
            chain=blockchain_service,
            query_start_block=start_block,
            default_registry=proxies.token_network_registry,
            default_secret_registry=proxies.secret_registry,
            default_service_registry=proxies.service_registry,
            transport=transport,
            raiden_event_handler=raiden_event_handler,
            message_handler=message_handler,
            discovery=discovery,
            user_deposit=proxies.user_deposit,
        )
    except RaidenError as e:
        click.secho(f'FATAL: {e}', fg='red')
        sys.exit(1)

    try:
        raiden_app.start()
    except RuntimeError as e:
        click.secho(f'FATAL: {e}', fg='red')
        sys.exit(1)
    except filelock.Timeout:
        name_or_id = ID_TO_NETWORKNAME.get(node_network_id, node_network_id)
        click.secho(
            f'FATAL: Another Raiden instance already running for account {address_hex} on '
            f'network id {name_or_id}',
            fg='red',
        )
        sys.exit(1)

    return raiden_app
Exemple #33
0
def prompt_account(address_hex, keystore_path, password_file):
    accmgr = AccountManager(keystore_path)
    if not accmgr.accounts:
        raise RuntimeError('No Ethereum accounts found in the user\'s system')

    if not accmgr.address_in_keystore(address_hex):
        # check if an address has been passed
        if address_hex is not None:
            print(
                "Account '{}' could not be found on the system. Aborting ...".
                format(address_hex))
            sys.exit(1)

        addresses = list(accmgr.accounts.keys())
        formatted_addresses = [
            '[{:3d}] - 0x{}'.format(idx, addr)
            for idx, addr in enumerate(addresses)
        ]

        should_prompt = True

        print('The following accounts were found in your machine:')
        print('')
        print('\n'.join(formatted_addresses))
        print('')

        while should_prompt:
            idx = click.prompt('Select one of them by index to continue',
                               type=int)

            if idx >= 0 and idx < len(addresses):
                should_prompt = False
            else:
                print("\nError: Provided index '{}' is out of bounds\n".format(
                    idx))

        address_hex = addresses[idx]

    password = None
    if password_file:
        password = password_file.read().splitlines()[0]
    if password:
        try:
            privatekey_bin = accmgr.get_privkey(address_hex, password)
        except ValueError:
            # ValueError exception raised if the password is incorrect
            print('Incorrect password for {} in file. Aborting ...'.format(
                address_hex))
            sys.exit(1)
    else:
        unlock_tries = 3
        while True:
            try:
                privatekey_bin = accmgr.get_privkey(address_hex)
                break
            except ValueError:
                # ValueError exception raised if the password is incorrect
                if unlock_tries == 0:
                    print(
                        'Exhausted passphrase unlock attempts for {}. Aborting ...'
                        .format(address_hex))
                    sys.exit(1)

                print(
                    'Incorrect passphrase to unlock the private key. {} tries remaining. '
                    'Please try again or kill the process to quit. '
                    'Usually Ctrl-c.'.format(unlock_tries))
                unlock_tries -= 1

    return address_hex, privatekey_bin
Exemple #34
0
def app(address,
        keystore_path,
        eth_rpc_endpoint,
        registry_contract_address,
        discovery_contract_address,
        listen_address,
        logging,
        logfile,
        max_unresponsive_time,
        send_ping_time):

    slogging.configure(logging, log_file=logfile)

    # config_file = args.config_file
    (listen_host, listen_port) = split_endpoint(listen_address)

    config = App.default_config.copy()
    config['host'] = listen_host
    config['port'] = listen_port
    config['max_unresponsive_time'] = max_unresponsive_time
    config['send_ping_time'] = send_ping_time

    accmgr = AccountManager(keystore_path)
    if not accmgr.accounts:
        raise RuntimeError('No Ethereum accounts found in the user\'s system')

    if not accmgr.address_in_keystore(address):
        addresses = list(accmgr.accounts.keys())
        formatted_addresses = [
            '[{:3d}] - 0x{}'.format(idx, addr)
            for idx, addr in enumerate(addresses)
        ]

        should_prompt = True
        while should_prompt:
            idx = click.prompt(
                "The following accounts were found in your machine:\n\n{}"
                "\nSelect one of them by index to continue: ".format(
                    "\n".join(formatted_addresses)),
                type=int
            )
            if idx >= 0 and idx < len(addresses):
                should_prompt = False
            else:
                print("\nError: Provided index '{}' is out of bounds\n".format(idx))

        address = addresses[idx]

    privatekey = accmgr.get_privkey(address)
    config['privatekey_hex'] = encode_hex(privatekey)

    endpoint = eth_rpc_endpoint

    if eth_rpc_endpoint.startswith("http://"):
        endpoint = eth_rpc_endpoint[len("http://"):]
        rpc_port = 80
    elif eth_rpc_endpoint.startswith("https://"):
        endpoint = eth_rpc_endpoint[len("https://"):]
        rpc_port = 443

    if ':' not in endpoint:  # no port was given in url
        rpc_host = endpoint
    else:
        rpc_host, rpc_port = split_endpoint(endpoint)

    blockchain_service = BlockChainService(
        privatekey,
        decode_hex(registry_contract_address),
        host=rpc_host,
        port=rpc_port,
    )

    discovery = ContractDiscovery(
        blockchain_service,
        decode_hex(discovery_contract_address)  # FIXME: double encoding
    )

    return App(config, blockchain_service, discovery)