Example #1
0
    def channel_open(
            self,
            registry_address,
            token_address,
            partner_address,
            settle_timeout=None,
            reveal_timeout=None,
            poll_timeout=DEFAULT_POLL_TIMEOUT,
            retry_timeout=DEFAULT_RETRY_TIMEOUT,
    ):
        """ Open a channel with the peer at `partner_address`
        with the given `token_address`.
        """
        if reveal_timeout is None:
            reveal_timeout = self.raiden.config['reveal_timeout']

        if settle_timeout is None:
            settle_timeout = self.raiden.config['settle_timeout']

        if settle_timeout <= reveal_timeout:
            raise InvalidSettleTimeout(
                'reveal_timeout can not be larger-or-equal to settle_timeout',
            )

        if not is_binary_address(registry_address):
            raise InvalidAddress('Expected binary address format for registry in channel open')

        if not is_binary_address(token_address):
            raise InvalidAddress('Expected binary address format for token in channel open')

        if not is_binary_address(partner_address):
            raise InvalidAddress('Expected binary address format for partner in channel open')

        registry = self.raiden.chain.token_network_registry(registry_address)
        token_network = registry.token_network_by_token(token_address)
        channel_identifier = token_network.new_netting_channel(
            partner_address,
            settle_timeout,
        )

        msg = 'After {} seconds the channel was not properly created.'.format(
            poll_timeout,
        )

        with gevent.Timeout(poll_timeout, EthNodeCommunicationError(msg)):
            waiting.wait_for_newchannel(
                self.raiden,
                registry_address,
                token_address,
                partner_address,
                retry_timeout,
            )

        return channel_identifier
Example #2
0
    def call(self, method, *args):
        """ Do the request and return the result.

        Args:
            method (str): The RPC method.
            args: The encoded arguments expected by the method.
                - Object arguments must be supplied as a dictionary.
                - Quantity arguments must be hex encoded starting with '0x' and
                without left zeros.
                - Data arguments must be hex encoded starting with '0x'
        """
        request = self.protocol.create_request(method, args)
        reply = self.transport.send_message(request.serialize())

        jsonrpc_reply = self.protocol.parse_reply(reply)
        if isinstance(jsonrpc_reply, JSONRPCSuccessResponse):
            return jsonrpc_reply.result
        elif isinstance(jsonrpc_reply, JSONRPCErrorResponse):
            raise EthNodeCommunicationError(jsonrpc_reply.error)
        else:
            raise EthNodeCommunicationError('Unknown type of JSONRPC reply')
Example #3
0
    def deposit(self, token_address, partner_address, amount):
        """ Deposit `amount` in the channel with the peer at `partner_address` and the
        given `token_address` in order to be able to do transfers.
        """
        if not isaddress(token_address):
            raise InvalidAddress(
                'Expected binary address format for token in channel deposit')

        if not isaddress(partner_address):
            raise InvalidAddress(
                'Expected binary address format for partner in channel deposit'
            )

        graph = self.raiden.token_to_channelgraph[token_address]
        channel = graph.partneraddress_to_channel[partner_address]
        netcontract_address = channel.external_state.netting_channel.address
        assert len(netcontract_address)

        # Obtain a reference to the token and approve the amount for funding
        token = self.raiden.chain.token(token_address)
        balance = token.balance_of(self.raiden.address.encode('hex'))

        if not balance >= amount:
            msg = "Not enough balance for token'{}' [{}]: have={}, need={}".format(
                token.proxy.name(), pex(token_address), balance, amount)
            raise InsufficientFunds(msg)

        token.approve(netcontract_address, amount)

        # Obtain the netting channel and fund it by depositing the amount
        old_balance = channel.contract_balance
        netting_channel = self.raiden.chain.netting_channel(
            netcontract_address)
        # actually make the deposit, and wait for it to be mined
        netting_channel.deposit(amount)

        # Wait until the balance has been updated via a state transition triggered
        # by processing the `ChannelNewBalance` event.
        # Usually, it'll take a single gevent.sleep, as we already have waited
        # for it to be mined, and only need to give the event handling greenlet
        # the chance to process the event, but let's wait 10s to be safe
        wait_timeout_secs = 10  # FIXME: hardcoded timeout
        if not wait_until(
                lambda: channel.contract_balance != old_balance,
                wait_timeout_secs,
                self.raiden.alarm.wait_time,
        ):
            raise EthNodeCommunicationError(
                'After {} seconds the deposit was not properly processed.'.
                format(wait_timeout_secs))

        return channel
Example #4
0
    def deposit(self, token_address, partner_address, amount):
        """ Deposit `amount` in the channel with the peer at `partner_address` and the
        given `token_address` in order to be able to do transfers.
        """
        if not isaddress(token_address):
            raise InvalidAddress('Expected binary address format for token in channel deposit')

        if not isaddress(partner_address):
            raise InvalidAddress('Expected binary address format for partner in channel deposit')

        graph = self.raiden.token_to_channelgraph[token_address]
        channel = graph.partneraddress_to_channel[partner_address]
        netcontract_address = channel.external_state.netting_channel.address
        assert len(netcontract_address)

        # Obtain a reference to the token and approve the amount for funding
        token = self.raiden.chain.token(token_address)
        balance = token.balance_of(self.raiden.address.encode('hex'))

        if not balance >= amount:
            msg = "Not enough balance for token'{}' [{}]: have={}, need={}".format(
                token.proxy.name(), pex(token_address), balance, amount
            )
            raise InsufficientFunds(msg)

        token.approve(netcontract_address, amount)

        # Obtain the netting channel and fund it by depositing the amount
        old_balance = channel.contract_balance
        netting_channel = self.raiden.chain.netting_channel(netcontract_address)
        netting_channel.deposit(amount)
        # Wait until the balance has been updated via a state transition triggered
        # by processing the `ChannelNewBalance` event
        wait_for = gevent.spawn(
            channel.wait_for_balance_update,
            old_balance,
            self.raiden.alarm.wait_time
        )
        wait_timeout_secs = 60
        gevent.wait([wait_for], timeout=wait_timeout_secs)

        # If balance is still the same then that means we did not get the event
        # after `timeout` seconds.
        if old_balance == channel.contract_balance:
            raise EthNodeCommunicationError(
                'After {} seconds the deposit event was not seen by the ethereum node.'.format(
                    wait_timeout_secs
                )
            )

        return channel
Example #5
0
    def token_network_register(
        self,
        registry_address,
        token_address,
        poll_timeout=DEFAULT_POLL_TIMEOUT,
        retry_timeout=DEFAULT_RETRY_TIMEOUT,
    ) -> typing.TokenNetworkAddress:
        """Register the `token_address` in the blockchain. If the address is already
           registered but the event has not been processed this function will block
           until the next block to make sure the event is processed.

        Raises:
            InvalidAddress: If the registry_address or token_address is not a valid address.
            AlreadyRegisteredTokenAddress: If the token is already registered.
            TransactionThrew: If the register transaction failed, this may
                happen because the account has not enough balance to pay for the
                gas or this register call raced with another transaction and lost.
        """

        if not is_binary_address(registry_address):
            raise InvalidAddress(
                'registry_address must be a valid address in binary')

        if not is_binary_address(token_address):
            raise InvalidAddress(
                'token_address must be a valid address in binary')

        if token_address in self.get_tokens_list(registry_address):
            raise AlreadyRegisteredTokenAddress('Token already registered')

        try:
            registry = self.raiden.chain.token_network_registry(
                registry_address)

            msg = 'After {} seconds the channel was not properly created.'.format(
                poll_timeout, )

            with gevent.Timeout(poll_timeout, EthNodeCommunicationError(msg)):
                return registry.add_token(token_address)
        finally:
            # Assume the transaction failed because the token is already
            # registered with the smart contract and this node has not yet
            # polled for the event (otherwise the check above would have
            # failed).
            #
            # To provide a consistent view to the user, wait one block, this
            # will guarantee that the events have been processed.
            next_block = self.raiden.get_block_number() + 1
            waiting.wait_for_block(self.raiden, next_block, retry_timeout)
Example #6
0
    def channel_open(self,
                     token_address,
                     partner_address,
                     settle_timeout=None,
                     reveal_timeout=None,
                     poll_timeout=DEFAULT_POLL_TIMEOUT):
        """ Open a channel with the peer at `partner_address`
        with the given `token_address`.
        """
        if reveal_timeout is None:
            reveal_timeout = self.raiden.config['reveal_timeout']

        if settle_timeout is None:
            settle_timeout = self.raiden.config['settle_timeout']

        if settle_timeout <= reveal_timeout:
            raise InvalidSettleTimeout(
                'reveal_timeout can not be larger-or-equal to settle_timeout')

        if not isaddress(token_address):
            raise InvalidAddress(
                'Expected binary address format for token in channel open')

        if not isaddress(partner_address):
            raise InvalidAddress(
                'Expected binary address format for partner in channel open')

        channel_manager = self.raiden.default_registry.manager_by_token(
            token_address)
        netcontract_address = channel_manager.new_netting_channel(
            partner_address,
            settle_timeout,
        )

        msg = 'After {} seconds the channel was not properly created.'.format(
            poll_timeout)

        registry_address = self.raiden.default_registry.address
        with gevent.Timeout(poll_timeout, EthNodeCommunicationError(msg)):
            waiting.wait_for_newchannel(
                self.raiden,
                registry_address,
                token_address,
                partner_address,
                self.raiden.alarm.wait_time,
            )

        return netcontract_address
Example #7
0
File: app.py Project: onyb/raiden
def _setup_web3(eth_rpc_endpoint):
    web3 = Web3(HTTPProvider(eth_rpc_endpoint))

    try:
        node_version = web3.version.node  # pylint: disable=no-member
    except ConnectTimeout:
        raise EthNodeCommunicationError("Couldn't connect to the ethereum node")

    supported, _ = is_supported_client(node_version)
    if not supported:
        click.secho(
            'You need a Byzantium enabled ethereum node. Parity >= 1.7.6 or Geth >= 1.7.2',
            fg='red',
        )
        sys.exit(1)
    return web3
Example #8
0
    def loop_until_stop(self):
        # The AlarmTask must have completed its first_run() before starting
        # the background greenlet.
        #
        # This is required because the first run will synchronize the node with
        # the blockchain since the last run.
        msg = "Only start the AlarmTask after it has been primed with the first_run"
        assert self.is_primed(), msg

        sleep_time = self.sleep_time
        while self._stop_event.wait(sleep_time) is not True:
            try:
                latest_block = self.chain.get_block(block_identifier="latest")
            except JSONDecodeError as e:
                raise EthNodeCommunicationError(str(e))

            self._maybe_run_callbacks(latest_block)
Example #9
0
    def loop_until_stop(self):
        # The AlarmTask must have completed its first_run() before starting
        # the background greenlet.
        #
        # This is required because the first run will synchronize the node with
        # the blockchain since the last run.
        assert self.chain_id, 'chain_id not set'
        assert self.known_block_number is not None, 'known_block_number not set'

        sleep_time = self.sleep_time
        while self._stop_event.wait(sleep_time) is not True:
            try:
                latest_block = self.chain.get_block(block_identifier='latest')
            except JSONDecodeError as e:
                raise EthNodeCommunicationError(str(e))

            self._maybe_run_callbacks(latest_block)
Example #10
0
    def __init__(
        self,
        web3: Web3,
        privkey: bytes,
        gasprice: int = None,
        nonce_offset: int = 0,
    ):
        if privkey is None or len(privkey) != 32:
            raise ValueError('Invalid private key')

        monkey_patch_web3(web3, self)

        try:
            version = web3.version.node
        except ConnectTimeout:
            raise EthNodeCommunicationError('couldnt reach the ethereum node')

        _, eth_node = is_supported_client(version)

        sender = privatekey_to_address(privkey)
        transaction_count = web3.eth.getTransactionCount(
            to_checksum_address(sender), 'pending')
        _available_nonce = transaction_count + nonce_offset

        self.eth_node = eth_node
        self.given_gas_price = gasprice
        self.privkey = privkey
        self.sender = sender
        # Needs to be initialized to None in the beginning since JSONRPCClient
        # gets constructed before the RaidenService Object.
        self.stop_event = None
        self.web3 = web3

        self._gaslimit_cache = TTLCache(maxsize=16, ttl=RPC_CACHE_TTL)
        self._gasprice_cache = TTLCache(maxsize=16, ttl=RPC_CACHE_TTL)
        self._available_nonce = _available_nonce
        self._nonce_lock = Semaphore()
        self._nonce_offset = nonce_offset

        log.debug(
            'JSONRPCClient created',
            sender=pex(self.sender),
            available_nonce=_available_nonce,
        )
Example #11
0
def check_ethereum_client_is_supported(web3: Web3) -> None:
    try:
        node_version = web3.version.node  # pylint: disable=no-member
    except ConnectTimeout:
        raise EthNodeCommunicationError(
            "Couldn't connect to the ethereum node")
    except ValueError:
        raise EthNodeInterfaceError(
            "The underlying ethereum node does not have the web3 rpc interface "
            "enabled. Please run it with --rpcapi eth,net,web3,txpool for geth "
            "and --jsonrpc-apis=eth,net,web3,parity for parity.")

    supported, _ = is_supported_client(node_version)
    if not supported:
        click.secho(
            "You need a Byzantium enabled ethereum node. Parity >= 1.7.6 or Geth >= 1.7.2",
            fg="red",
        )
        sys.exit(1)
Example #12
0
def _setup_web3(eth_rpc_endpoint):
    web3 = Web3(HTTPProvider(eth_rpc_endpoint))

    try:
        node_version = web3.version.node  # pylint: disable=no-member
    except ConnectTimeout:
        raise EthNodeCommunicationError(
            "Couldn't connect to the ethereum node")
    except ValueError:
        raise EthNodeInterfaceError(
            'The underlying ethereum node does not have the web3 rpc interface '
            'enabled. Please run it with --rpcapi eth,net,web3,txpool for geth '
            'and --jsonrpc-apis=eth,net,web3,parity for parity.', )

    supported, _ = is_supported_client(node_version)
    if not supported:
        click.secho(
            'You need a Byzantium enabled ethereum node. Parity >= 1.7.6 or Geth >= 1.7.2',
            fg='red',
        )
        sys.exit(1)
    return web3
Example #13
0
    def __init__(
        self,
        web3: Web3,
        privkey: bytes,
        gas_price_strategy: Callable = rpc_gas_price_strategy,
        nonce_offset: int = 0,
    ):
        if privkey is None or len(privkey) != 32:
            raise ValueError('Invalid private key')

        monkey_patch_web3(web3, self, gas_price_strategy)

        try:
            version = web3.version.node
        except ConnectTimeout:
            raise EthNodeCommunicationError('couldnt reach the ethereum node')

        _, eth_node = is_supported_client(version)

        sender = privatekey_to_address(privkey)
        transaction_count = web3.eth.getTransactionCount(
            to_checksum_address(sender), 'pending')
        _available_nonce = transaction_count + nonce_offset

        self.eth_node = eth_node
        self.privkey = privkey
        self.sender = sender
        self.web3 = web3

        self._gasprice_cache = TTLCache(maxsize=16, ttl=RPC_CACHE_TTL)
        self._available_nonce = _available_nonce
        self._nonce_lock = Semaphore()
        self._nonce_offset = nonce_offset

        log.debug(
            'JSONRPCClient created',
            sender=pex(self.sender),
            available_nonce=_available_nonce,
        )
Example #14
0
    def deposit(self,
                token_address,
                partner_address,
                amount,
                poll_timeout=DEFAULT_POLL_TIMEOUT):
        """ Deposit `amount` in the channel with the peer at `partner_address` and the
        given `token_address` in order to be able to do transfers.

        Raises:
            InvalidAddress: If either token_address or partner_address is not
            20 bytes long.
            TransactionThrew: May happen for multiple reasons:
                - If the token approval fails, e.g. the token may validate if
                  account has enough balance for the allowance.
                - The deposit failed, e.g. the allowance did not set the token
                  aside for use and the user spent it before deposit was called.
                - The channel was closed/settled between the allowance call and
                  the deposit call.
            AddressWithoutCode: The channel was settled during the deposit
            execution.
        """
        if not isaddress(token_address):
            raise InvalidAddress(
                'Expected binary address format for token in channel deposit')

        if not isaddress(partner_address):
            raise InvalidAddress(
                'Expected binary address format for partner in channel deposit'
            )

        graph = self.raiden.token_to_channelgraph.get(token_address)
        if graph is None:
            raise InvalidAddress('Unknown token address')

        channel = graph.partneraddress_to_channel.get(partner_address)
        if channel is None:
            raise InvalidAddress(
                'No channel with partner_address for the given token')

        if channel.token_address != token_address:
            raise InvalidAddress(
                'token_address does not match the netting channel attribute')

        token = self.raiden.chain.token(token_address)
        netcontract_address = channel.external_state.netting_channel.address
        old_balance = channel.contract_balance

        # Checking the balance is not helpful since this requires multiple
        # transactions that can race, e.g. the deposit check succeed but the
        # user spent his balance before deposit.
        balance = token.balance_of(hexlify(self.raiden.address))
        if not balance >= amount:
            msg = 'Not enough balance to deposit. {} Available={} Tried={}'.format(
                pex(token_address),
                balance,
                amount,
            )
            raise InsufficientFunds(msg)
        token.approve(netcontract_address, amount)

        channel_proxy = self.raiden.chain.netting_channel(netcontract_address)
        channel_proxy.deposit(amount)

        # Wait until the `ChannelNewBalance` event is processed.
        #
        # Usually a single sleep is sufficient, since the `deposit` waits for
        # the transaction to be polled.
        sucess = wait_until(
            lambda: channel.contract_balance != old_balance,
            poll_timeout,
            self.raiden.alarm.wait_time,
        )

        if not sucess:
            raise EthNodeCommunicationError(
                'After {} seconds the deposit was not properly processed.'.
                format(poll_timeout))

        return channel
Example #15
0
def run_app(
    address,
    keystore_path,
    gas_price,
    eth_rpc_endpoint,
    registry_contract_address,
    secret_registry_contract_address,
    discovery_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,
    extra_config=None,
    **kwargs,
):
    # pylint: disable=too-many-locals,too-many-branches,too-many-statements,unused-argument

    from raiden.app import App

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

    address_hex = to_normalized_address(address) if address else None
    address_hex, privatekey_bin = prompt_account(address_hex, keystore_path,
                                                 password_file)
    address = to_canonical_address(address_hex)

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

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

    config = deepcopy(App.DEFAULT_CONFIG)
    if extra_config:
        merge_dict(config, extra_config)

    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

    privatekey_hex = hexlify(privatekey_bin)
    config['privatekey_hex'] = privatekey_hex

    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))

    try:
        node_version = web3.version.node  # pylint: disable=no-member
    except ConnectTimeout:
        raise EthNodeCommunicationError(
            "Couldn't connect to the ethereum node")

    supported, _ = is_supported_client(node_version)
    if not supported:
        print(
            'You need a Byzantium enabled ethereum node. Parity >= 1.7.6 or Geth >= 1.7.2'
        )
        sys.exit(1)

    rpc_client = JSONRPCClient(
        web3,
        privatekey_bin,
        gasprice=gas_price,
    )

    blockchain_service = BlockChainService(privatekey_bin, rpc_client)

    net_id = blockchain_service.network_id
    if net_id != network_id:
        if network_id in constants.ID_TO_NETWORKNAME and net_id in constants.ID_TO_NETWORKNAME:
            print((
                "The chosen ethereum network '{}' differs from the ethereum client '{}'. "
                'Please update your settings.').format(
                    constants.ID_TO_NETWORKNAME[network_id],
                    constants.ID_TO_NETWORKNAME[net_id]))
        else:
            print((
                "The chosen ethereum network id '{}' differs from the ethereum client '{}'. "
                'Please update your settings.').format(network_id, net_id))
        sys.exit(1)

    config['chain_id'] = network_id

    if sync_check:
        check_synced(blockchain_service)

    database_path = os.path.join(datadir, 'netid_%s' % net_id, address_hex[:8],
                                 'log.db')
    config['database_path'] = database_path
    print(
        '\nYou are connected to the \'{}\' network and the DB path is: {}'.
        format(
            constants.ID_TO_NETWORKNAME.get(net_id) or net_id,
            database_path,
        ), )

    contract_addresses_given = (registry_contract_address is not None and
                                secret_registry_contract_address is not None
                                and discovery_contract_address is not None)
    contract_addresses_known = net_id in constants.ID_TO_NETWORK_CONFIG

    if not contract_addresses_given and not contract_addresses_known:
        print((
            "There are known contract addresses for network id '{}'. Please provide "
            'them in the command line or the configuration file.'
        ).format(net_id))
        sys.exit(1)

    contract_addresses = constants.ID_TO_NETWORK_CONFIG.get(net_id, dict())

    try:
        token_network_registry = blockchain_service.token_network_registry(
            registry_contract_address
            or contract_addresses[CONTRACT_TOKEN_NETWORK_REGISTRY], )
    except ContractVersionMismatch:
        handle_contract_version_mismatch('token network registry',
                                         registry_contract_address)
    except AddressWithoutCode:
        handle_contract_no_code('token network registry',
                                registry_contract_address)
    except AddressWrongContract:
        handle_contract_wrong_address('token network registry',
                                      registry_contract_address)

    try:
        secret_registry = blockchain_service.secret_registry(
            secret_registry_contract_address
            or contract_addresses[CONTRACT_SECRET_REGISTRY], )
    except ContractVersionMismatch:
        handle_contract_version_mismatch('secret registry',
                                         secret_registry_contract_address)
    except AddressWithoutCode:
        handle_contract_no_code('secret registry',
                                secret_registry_contract_address)
    except AddressWrongContract:
        handle_contract_wrong_address('secret registry',
                                      secret_registry_contract_address)

    discovery = None
    if transport == 'udp':
        check_discovery_registration_gas(blockchain_service, address)
        try:
            dicovery_proxy = blockchain_service.discovery(
                discovery_contract_address
                or contract_addresses[CONTRACT_ENDPOINT_REGISTRY], )
            discovery = ContractDiscovery(
                blockchain_service.node_address,
                dicovery_proxy,
            )
        except ContractVersionMismatch:
            handle_contract_version_mismatch('discovery',
                                             discovery_contract_address)
        except AddressWithoutCode:
            handle_contract_no_code('discovery', discovery_contract_address)
        except AddressWrongContract:
            handle_contract_wrong_address('discovery',
                                          discovery_contract_address)

        throttle_policy = TokenBucket(
            config['transport']['udp']['throttle_capacity'],
            config['transport']['udp']['throttle_fill_rate'],
        )

        transport = UDPTransport(
            discovery,
            mapped_socket.socket,
            throttle_policy,
            config['transport']['udp'],
        )
    elif transport == 'matrix':
        try:
            transport = MatrixTransport(config['transport']['matrix'])
        except RaidenError as ex:
            click.secho(f'FATAL: {ex}', fg='red')
            sys.exit(1)
    else:
        raise RuntimeError(f'Unknown transport type "{transport}" given')

    try:
        chain_config = constants.ID_TO_NETWORK_CONFIG.get(net_id, {})
        start_block = chain_config.get(constants.START_QUERY_BLOCK_KEY, 0)
        raiden_app = App(
            config=config,
            chain=blockchain_service,
            query_start_block=start_block,
            default_registry=token_network_registry,
            default_secret_registry=secret_registry,
            transport=transport,
            discovery=discovery,
        )
    except RaidenError as e:
        click.secho(f'FATAL: {e}', fg='red')
        sys.exit(1)

    try:
        raiden_app.start()
    except filelock.Timeout:
        name_or_id = constants.ID_TO_NETWORKNAME.get(network_id, network_id)
        print(
            f'FATAL: Another Raiden instance already running for account {address_hex} on '
            f'network id {name_or_id}', )
        sys.exit(1)

    return raiden_app
Example #16
0
    def __init__(
        self,
        web3: Web3,
        privkey: bytes,
        gas_price_strategy: Callable = rpc_gas_price_strategy,
        gas_estimate_correction: Callable = lambda gas: gas,
        block_num_confirmations: int = 0,
        uses_infura=False,
    ):
        if privkey is None or len(privkey) != 32:
            raise ValueError("Invalid private key")

        if block_num_confirmations < 0:
            raise ValueError("Number of confirmations has to be positive")

        monkey_patch_web3(web3, gas_price_strategy)

        try:
            version = web3.version.node
        except ConnectTimeout:
            raise EthNodeCommunicationError("couldnt reach the ethereum node")

        _, eth_node = is_supported_client(version)

        address = privatekey_to_address(privkey)
        address_checksumed = to_checksum_address(address)

        if uses_infura:
            warnings.warn(
                "Infura does not provide an API to "
                "recover the latest used nonce. This may cause the Raiden node "
                "to error on restarts.\n"
                "The error will manifest while there is a pending transaction "
                "from a previous execution in the Ethereum's client pool. When "
                "Raiden restarts the same transaction with the same nonce will "
                "be retried and *rejected*, because the nonce is already used."
            )
            # The first valid nonce is 0, therefore the count is already the next
            # available nonce
            available_nonce = web3.eth.getTransactionCount(
                address_checksumed, "pending")

        elif eth_node is constants.EthClient.PARITY:
            parity_assert_rpc_interfaces(web3)
            available_nonce = parity_discover_next_available_nonce(
                web3, address_checksumed)

        elif eth_node is constants.EthClient.GETH:
            geth_assert_rpc_interfaces(web3)
            available_nonce = geth_discover_next_available_nonce(
                web3, address_checksumed)

        else:
            raise EthNodeInterfaceError(
                f"Unsupported Ethereum client {version}")

        self.eth_node = eth_node
        self.privkey = privkey
        self.address = address
        self.web3 = web3
        self.default_block_num_confirmations = block_num_confirmations

        self._available_nonce = available_nonce
        self._nonce_lock = Semaphore()
        self._gas_estimate_correction = gas_estimate_correction

        log.debug(
            "JSONRPCClient created",
            node=pex(self.address),
            available_nonce=available_nonce,
            client=version,
        )
Example #17
0
    def channel_batch_close(self,
                            token_address,
                            partner_addresses,
                            poll_timeout=DEFAULT_POLL_TIMEOUT):
        """Close a channel opened with `partner_address` for the given
        `token_address`.

        Race condition, this can fail if channel was closed externally.
        """

        if not isaddress(token_address):
            raise InvalidAddress(
                'Expected binary address format for token in channel close')

        if not all(map(isaddress, partner_addresses)):
            raise InvalidAddress(
                'Expected binary address format for partner in channel close')

        valid_tokens = views.get_token_network_addresses_for(
            views.state_from_raiden(self.raiden),
            self.raiden.default_registry.address,
        )
        if token_address not in valid_tokens:
            raise UnknownTokenAddress('Token address is not known.')

        registry_address = self.raiden.default_registry.address
        node_state = views.state_from_raiden(self.raiden)
        channels_to_close = views.filter_channels_by_partneraddress(
            node_state,
            registry_address,
            token_address,
            partner_addresses,
        )

        # If concurrent operations are happening on one of the channels, fail entire
        # request.
        with ExitStack() as stack:
            # Put all the locks in this outer context so that the netting channel functions
            # don't release the locks when their context goes out of scope
            for channel_state in channels_to_close:
                channel = self.raiden.chain.netting_channel(
                    channel_state.identifier)

                # Check if we can acquire the lock. If we can't raise an exception, which
                # will cause the ExitStack to exit, releasing all locks acquired so far
                if not channel.channel_operations_lock.acquire(blocking=False):
                    raise ChannelBusyError(
                        f'Channel with id {channel_state.identifier} is '
                        f'busy with another ongoing operation.')

                stack.push(channel.channel_operations_lock)

            for channel_state in channels_to_close:
                channel_close = ActionChannelClose(
                    registry_address,
                    token_address,
                    channel_state.identifier,
                )

                self.raiden.handle_state_change(channel_close)

            msg = 'After {} seconds the deposit was not properly processed.'.format(
                poll_timeout)

            channel_ids = [
                channel_state.identifier for channel_state in channels_to_close
            ]

            with gevent.Timeout(poll_timeout, EthNodeCommunicationError(msg)):
                waiting.wait_for_close(
                    self.raiden,
                    registry_address,
                    token_address,
                    channel_ids,
                    self.raiden.alarm.wait_time,
                )
Example #18
0
    def channel_deposit(self,
                        token_address,
                        partner_address,
                        amount,
                        poll_timeout=DEFAULT_POLL_TIMEOUT):
        """ Deposit `amount` in the channel with the peer at `partner_address` and the
        given `token_address` in order to be able to do transfers.

        Raises:
            InvalidAddress: If either token_address or partner_address is not
            20 bytes long.
            TransactionThrew: May happen for multiple reasons:
                - If the token approval fails, e.g. the token may validate if
                  account has enough balance for the allowance.
                - The deposit failed, e.g. the allowance did not set the token
                  aside for use and the user spent it before deposit was called.
                - The channel was closed/settled between the allowance call and
                  the deposit call.
            AddressWithoutCode: The channel was settled during the deposit
            execution.
        """
        node_state = views.state_from_raiden(self.raiden)
        registry_address = self.raiden.default_registry.address

        token_networks = views.get_token_network_addresses_for(
            node_state,
            registry_address,
        )
        channel_state = views.get_channelstate_for(
            node_state,
            registry_address,
            token_address,
            partner_address,
        )

        if not isaddress(token_address):
            raise InvalidAddress(
                'Expected binary address format for token in channel deposit')

        if not isaddress(partner_address):
            raise InvalidAddress(
                'Expected binary address format for partner in channel deposit'
            )

        if token_address not in token_networks:
            raise UnknownTokenAddress('Unknown token address')

        if channel_state is None:
            raise InvalidAddress(
                'No channel with partner_address for the given token')

        token = self.raiden.chain.token(token_address)
        balance = token.balance_of(hexlify(self.raiden.address))

        # If this check succeeds it does not imply the the `deposit` will
        # succeed, since the `deposit` transaction may race with another
        # transaction.
        if not balance >= amount:
            msg = 'Not enough balance to deposit. {} Available={} Tried={}'.format(
                pex(token_address),
                balance,
                amount,
            )
            raise InsufficientFunds(msg)

        netcontract_address = channel_state.identifier
        channel_proxy = self.raiden.chain.netting_channel(netcontract_address)

        # If concurrent operations are happening on the channel, fail the request
        if not channel_proxy.channel_operations_lock.acquire(blocking=False):
            raise ChannelBusyError(
                f'Channel with id {channel_state.identifier} is '
                f'busy with another ongoing operation')

        with releasing(channel_proxy.channel_operations_lock):
            token.approve(netcontract_address, amount)
            channel_proxy.deposit(amount)

            old_balance = channel_state.our_state.contract_balance
            target_balance = old_balance + amount

            msg = 'After {} seconds the deposit was not properly processed.'.format(
                poll_timeout)

            # Wait until the `ChannelNewBalance` event is processed.
            with gevent.Timeout(poll_timeout, EthNodeCommunicationError(msg)):
                waiting.wait_for_newbalance(
                    self.raiden,
                    registry_address,
                    token_address,
                    partner_address,
                    target_balance,
                    self.raiden.alarm.wait_time,
                )
Example #19
0
    def channel_open(
        self,
        registry_address,
        token_address,
        partner_address,
        settle_timeout=None,
        reveal_timeout=None,
        poll_timeout=DEFAULT_POLL_TIMEOUT,
        retry_timeout=DEFAULT_RETRY_TIMEOUT,
    ):
        """ Open a channel with the peer at `partner_address`
        with the given `token_address`.
        """
        if reveal_timeout is None:
            reveal_timeout = self.raiden.config['reveal_timeout']

        if settle_timeout is None:
            settle_timeout = self.raiden.config['settle_timeout']

        if settle_timeout <= reveal_timeout:
            raise InvalidSettleTimeout(
                'reveal_timeout can not be larger-or-equal to settle_timeout',
            )

        if not is_binary_address(registry_address):
            raise InvalidAddress(
                'Expected binary address format for registry in channel open')

        if not is_binary_address(token_address):
            raise InvalidAddress(
                'Expected binary address format for token in channel open')

        if not is_binary_address(partner_address):
            raise InvalidAddress(
                'Expected binary address format for partner in channel open')

        chain_state = views.state_from_raiden(self.raiden)
        channel_state = views.get_channelstate_for(
            chain_state,
            registry_address,
            token_address,
            partner_address,
        )

        if channel_state:
            raise DuplicatedChannelError(
                'Channel with given partner address already exists')

        registry = self.raiden.chain.token_network_registry(registry_address)
        token_network = self.raiden.chain.token_network(
            registry.get_token_network(token_address), )

        try:
            token_network.new_netting_channel(
                partner_address,
                settle_timeout,
            )
        except DuplicatedChannelError:
            log.info('partner opened channel first')

        msg = 'After {} seconds the channel was not properly created.'.format(
            poll_timeout, )

        with gevent.Timeout(poll_timeout, EthNodeCommunicationError(msg)):
            waiting.wait_for_newchannel(
                self.raiden,
                registry_address,
                token_address,
                partner_address,
                retry_timeout,
            )
        chain_state = views.state_from_raiden(self.raiden)
        channel_state = views.get_channelstate_for(
            chain_state,
            registry_address,
            token_address,
            partner_address,
        )

        return channel_state.identifier
Example #20
0
    def set_total_channel_deposit(
        self,
        registry_address,
        token_address,
        partner_address,
        total_deposit,
        poll_timeout=DEFAULT_POLL_TIMEOUT,
        retry_timeout=DEFAULT_RETRY_TIMEOUT,
    ):
        """ Set the `total_deposit` in the channel with the peer at `partner_address` and the
        given `token_address` in order to be able to do transfers.

        Raises:
            InvalidAddress: If either token_address or partner_address is not
            20 bytes long.
            TransactionThrew: May happen for multiple reasons:
                - If the token approval fails, e.g. the token may validate if
                  account has enough balance for the allowance.
                - The deposit failed, e.g. the allowance did not set the token
                  aside for use and the user spent it before deposit was called.
                - The channel was closed/settled between the allowance call and
                  the deposit call.
            AddressWithoutCode: The channel was settled during the deposit
            execution.
            DepositOverLimit: The total deposit amount is higher than the limit.
        """
        chain_state = views.state_from_raiden(self.raiden)

        token_networks = views.get_token_network_addresses_for(
            chain_state,
            registry_address,
        )
        channel_state = views.get_channelstate_for(
            chain_state,
            registry_address,
            token_address,
            partner_address,
        )

        if not is_binary_address(token_address):
            raise InvalidAddress(
                'Expected binary address format for token in channel deposit')

        if not is_binary_address(partner_address):
            raise InvalidAddress(
                'Expected binary address format for partner in channel deposit'
            )

        if token_address not in token_networks:
            raise UnknownTokenAddress('Unknown token address')

        if channel_state is None:
            raise InvalidAddress(
                'No channel with partner_address for the given token')

        token = self.raiden.chain.token(token_address)
        netcontract_address = channel_state.identifier
        token_network_registry = self.raiden.chain.token_network_registry(
            registry_address)
        token_network_address = token_network_registry.get_token_network(
            token_address)
        token_network_proxy = self.raiden.chain.token_network(
            token_network_address)
        channel_proxy = self.raiden.chain.payment_channel(
            token_network_proxy.address,
            netcontract_address,
        )

        balance = token.balance_of(self.raiden.address)

        deposit_limit = token_network_proxy.proxy.contract.functions.deposit_limit(
        ).call()
        if total_deposit > deposit_limit:
            raise DepositOverLimit(
                'The deposit of {} is bigger than the current limit of {}'.
                format(
                    total_deposit,
                    deposit_limit,
                ), )

        if total_deposit <= channel_state.our_state.contract_balance:
            # no action required
            return

        addendum = total_deposit - channel_state.our_state.contract_balance

        # If this check succeeds it does not imply the the `deposit` will
        # succeed, since the `deposit` transaction may race with another
        # transaction.
        if not balance >= addendum:
            msg = 'Not enough balance to deposit. {} Available={} Needed={}'.format(
                pex(token_address),
                balance,
                addendum,
            )
            raise InsufficientFunds(msg)

        # If concurrent operations are happening on the channel, fail the request
        with channel_proxy.lock_or_raise():
            # set_total_deposit calls approve
            # token.approve(netcontract_address, addendum)
            channel_proxy.set_total_deposit(total_deposit)

            msg = 'After {} seconds the deposit was not properly processed.'.format(
                poll_timeout, )

            # Wait until the `ChannelNewBalance` event is processed.
            with gevent.Timeout(poll_timeout, EthNodeCommunicationError(msg)):
                target_address = self.raiden.address
                waiting.wait_for_participant_newbalance(
                    self.raiden,
                    registry_address,
                    token_address,
                    partner_address,
                    target_address,
                    total_deposit,
                    retry_timeout,
                )
Example #21
0
    def channel_batch_close(
        self,
        registry_address,
        token_address,
        partner_addresses,
        poll_timeout=DEFAULT_POLL_TIMEOUT,
        retry_timeout=DEFAULT_RETRY_TIMEOUT,
    ):
        """Close a channel opened with `partner_address` for the given
        `token_address`.

        Race condition, this can fail if channel was closed externally.
        """

        if not is_binary_address(token_address):
            raise InvalidAddress(
                'Expected binary address format for token in channel close')

        if not all(map(is_binary_address, partner_addresses)):
            raise InvalidAddress(
                'Expected binary address format for partner in channel close')

        valid_tokens = views.get_token_network_addresses_for(
            views.state_from_raiden(self.raiden),
            registry_address,
        )
        if token_address not in valid_tokens:
            raise UnknownTokenAddress('Token address is not known.')

        chain_state = views.state_from_raiden(self.raiden)
        channels_to_close = views.filter_channels_by_partneraddress(
            chain_state,
            registry_address,
            token_address,
            partner_addresses,
        )
        token_network_identifier = views.get_token_network_identifier_by_token_address(
            views.state_from_raiden(self.raiden),
            registry_address,
            token_address,
        )

        # If concurrent operations are happening on one of the channels, fail entire
        # request.
        with ExitStack() as stack:
            # Put all the locks in this outer context so that the netting channel functions
            # don't release the locks when their context goes out of scope
            for channel_state in channels_to_close:
                channel = self.raiden.chain.payment_channel(
                    token_network_identifier,
                    channel_state.identifier,
                )
                stack.enter_context(channel.lock_or_raise())

            for channel_state in channels_to_close:
                channel_close = ActionChannelClose(
                    token_network_identifier,
                    channel_state.identifier,
                )

                self.raiden.handle_state_change(channel_close)

            msg = 'After {} seconds the closing transactions were not properly processed.'.format(
                poll_timeout, )

            channel_ids = [
                channel_state.identifier for channel_state in channels_to_close
            ]

            with gevent.Timeout(poll_timeout, EthNodeCommunicationError(msg)):
                waiting.wait_for_close(
                    self.raiden,
                    registry_address,
                    token_address,
                    channel_ids,
                    retry_timeout,
                )