# Buggy code
def _poll_until_target(self, target_block_number: BlockNumber) -> None:
    """Poll blockchain events up to `target_block_number`.

    Multiple queries may be necessary on restarts, because the node may
    have been offline for an extend period of time. During normal
    operation, this must not happen, because in this case the node may have
    missed important events, like a channel close, while the transport
    layer is running, this can lead to loss of funds.

    It is very important for `confirmed_target_block_number` to be an
    confirmed block, otherwise reorgs may cause havoc. This is problematic
    since some operations are irreversible, namely sending a balance proof.
    Once a node accepts a deposit, these tokens can be used to do mediated
    transfers, and if a reorg removes the deposit tokens could be lost.

    This function takes care of fetching blocks in batches and confirming
    their result. This is important to keep memory usage low and to speed
    up restarts. Memory usage can get a hit if the node is asleep for a
    long period of time and on the first run, since all the missing
    confirmed blocks have to be fetched before the node is in a working
    state. Restarts get a hit if the node is closed while it was
    synchronizing, without regularly saving that work, if the node is
    killed while synchronizing, it only gets gradually slower.

    Returns:
        int: number of polling queries required to synchronized with
        `target_block_number`.
    """
    msg = (
        f"The blockchain event handler has to be instantiated before the "
        f"alarm task is started. node:{self!r}"
    )
    assert self.blockchain_events, msg
    log.debug(
        "Poll until target",
        target=target_block_number,
        last_fetched_block=self.blockchain_events.last_fetched_block,
    )

    sync_start = datetime.now()

    while self.blockchain_events.last_fetched_block < target_block_number:
        self._log_sync_progress(target_block_number)

        poll_result = self.blockchain_events.fetch_logs_in_batch(target_block_number)
        if poll_result is None:
            # No blocks could be fetched (due to timeout), retry
            continue

        pendingtokenregistration: Dict[
            TokenNetworkAddress, Tuple[TokenNetworkRegistryAddress, TokenAddress]
        ] = dict()

        state_changes: List[StateChange] = list()
        for event in poll_result.events:
            state_changes.extend(
                blockchainevent_to_statechange(
                    self,
                    event,
                    poll_result.polled_block_number,
                    pendingtokenregistration,
                )
            )

        # On restarts the node has to pick up all events generated since the
        # last run. To do this the node will set the filters' from_block to
        # the value of the latest block number known to have *all* events
        # processed.
        #
        # To guarantee the above the node must either:
        #
        # - Dispatch the state changes individually, leaving the Block
        # state change last, so that it knows all the events for the
        # given block have been processed. On restarts this can result in
        # the same event being processed twice.
        # - Dispatch all the smart contract events together with the Block
        # state change in a single transaction, either all or nothing will
        # be applied, and on a restart the node picks up from where it
        # left.
        #
        # The approach used below is to dispatch the Block and the
        # blockchain events in a single transaction. This is the preferred
        # approach because it guarantees that no events will be missed and
        # it fixes race conditions on the value of the block number value,
        # that can lead to crashes.
        #
        # Example: The user creates a new channel with an initial deposit
        # of X tokens. This is done with two operations, the first is to
        # open the new channel, the second is to deposit the requested
        # tokens in it. Once the node fetches the event for the new channel,
        # it will immediately request the deposit, which leaves a window for
        # a race condition. If the Block state change was not yet
        # processed, the block hash used as the triggering block for the
        # deposit will be off-by-one, and it will point to the block
        # immediately before the channel existed. This breaks a proxy
        # precondition which crashes the client.
        block_state_change = Block(
            block_number=poll_result.polled_block_number,
            gas_limit=poll_result.polled_block_gas_limit,
            block_hash=poll_result.polled_block_hash,
        )
        state_changes.append(block_state_change)

        # It's important to /not/ block here, because this function can
        # be called from the alarm task greenlet, which should not
        # starve. This was a problem when the node decided to send a new
        # transaction, since the proxies block until the transaction is
        # mined and confirmed (e.g. the settle window is over and the
        # node sends the settle transaction).
        self.handle_and_track_state_changes(state_changes)

    sync_end = datetime.now()
    log.debug(
        "Synchronized to a new confirmed block",
        event_filters_qty=len(self.blockchain_events._address_to_filters),
        sync_elapsed=sync_end - sync_start,
    )


# Traceback (urllib3.exceptions.ReadTimeoutError)
Traceback (most recent call last):
File "/Users/ulo/Envs/raiden/lib/python3.7/site-packages/urllib3/connectionpool.py", line 387, in _make_request
six.raise_from(e, None)
File "<string>", line 2, in raise_from
File "/Users/ulo/Envs/raiden/lib/python3.7/site-packages/urllib3/connectionpool.py", line 383, in _make_request
httplib_response = conn.getresponse()
File "/usr/local/Cellar/python/3.7.7/Frameworks/Python.framework/Versions/3.7/lib/python3.7/http/client.py", line 1344, in getresponse
response.begin()
File "/usr/local/Cellar/python/3.7.7/Frameworks/Python.framework/Versions/3.7/lib/python3.7/http/client.py", line 306, in begin
version, status, reason = self._read_status()
File "/usr/local/Cellar/python/3.7.7/Frameworks/Python.framework/Versions/3.7/lib/python3.7/http/client.py", line 267, in _read_status
line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1")
File "/usr/local/Cellar/python/3.7.7/Frameworks/Python.framework/Versions/3.7/lib/python3.7/socket.py", line 589, in readinto
return self._sock.recv_into(b)
File "/Users/ulo/Envs/raiden/lib/python3.7/site-packages/gevent/_socket3.py", line 496, in recv_into
self._wait(self._read_event)
File "src/gevent/_hub_primitives.py", line 290, in gevent.__hub_primitives.wait_on_socket
File "src/gevent/_hub_primitives.py", line 295, in gevent.__hub_primitives.wait_on_socket
File "src/gevent/_hub_primitives.py", line 286, in gevent.__hub_primitives._primitive_wait
File "src/gevent/_hub_primitives.py", line 287, in gevent.__hub_primitives._primitive_wait
File "src/gevent/_hub_primitives.py", line 46, in gevent.__hub_primitives.WaitOperationsGreenlet.wait
File "src/gevent/_hub_primitives.py", line 46, in gevent.__hub_primitives.WaitOperationsGreenlet.wait
File "src/gevent/_hub_primitives.py", line 55, in gevent.__hub_primitives.WaitOperationsGreenlet.wait
File "src/gevent/_waiter.py", line 151, in gevent.__waiter.Waiter.get
File "src/gevent/_greenlet_primitives.py", line 61, in gevent.__greenlet_primitives.SwitchOutGreenletWithLoop.switch
File "src/gevent/_greenlet_primitives.py", line 61, in gevent.__greenlet_primitives.SwitchOutGreenletWithLoop.switch
File "src/gevent/_greenlet_primitives.py", line 65, in gevent.__greenlet_primitives.SwitchOutGreenletWithLoop.switch
File "src/gevent/__greenlet_primitives.pxd", line 35, in gevent.__greenlet_primitives._greenlet_switch
socket.timeout: timed out

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "/Users/ulo/Envs/raiden/lib/python3.7/site-packages/requests/adapters.py", line 449, in send
timeout=timeout
File "/Users/ulo/Envs/raiden/lib/python3.7/site-packages/urllib3/connectionpool.py", line 641, in urlopen
_stacktrace=sys.exc_info()[2])
File "/Users/ulo/Envs/raiden/lib/python3.7/site-packages/urllib3/util/retry.py", line 368, in increment
raise six.reraise(type(error), error, _stacktrace)
File "/Users/ulo/Envs/raiden/lib/python3.7/site-packages/urllib3/packages/six.py", line 686, in reraise
raise value
File "/Users/ulo/Envs/raiden/lib/python3.7/site-packages/urllib3/connectionpool.py", line 603, in urlopen
chunked=chunked)
File "/Users/ulo/Envs/raiden/lib/python3.7/site-packages/urllib3/connectionpool.py", line 389, in _make_request
self._raise_timeout(err=e, url=url, timeout_value=read_timeout)
File "/Users/ulo/Envs/raiden/lib/python3.7/site-packages/urllib3/connectionpool.py", line 307, in _raise_timeout
raise ReadTimeoutError(self, url, "Read timed out. (read timeout=%s)" % timeout_value)
urllib3.exceptions.ReadTimeoutError: HTTPConnectionPool(host='geth.mainnet.ethnodes.brainbot.com', port=8545): Read timed out. (read timeout=10)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "/Users/ulo/Dropbox/devel/brainbot/raiden/raiden/ui/cli.py", line 610, in run
run_services(kwargs)
File "/Users/ulo/Dropbox/devel/brainbot/raiden/raiden/ui/runners.py", line 21, in run_services
app = run_app(**options)
File "/Users/ulo/Dropbox/devel/brainbot/raiden/raiden/ui/app.py", line 438, in run_app
raiden_app.start()
File "/Users/ulo/Dropbox/devel/brainbot/raiden/raiden/app.py", line 91, in start
self.raiden.start()
File "/Users/ulo/Dropbox/devel/brainbot/raiden/raiden/raiden_service.py", line 364, in start
self._synchronize_with_blockchain()
File "/Users/ulo/Dropbox/devel/brainbot/raiden/raiden/raiden_service.py", line 670, in _synchronize_with_blockchain
self._poll_until_target(latest_confirmed_block_number)
File "/Users/ulo/Dropbox/devel/brainbot/raiden/raiden/raiden_service.py", line 993, in _poll_until_target
poll_result = self.blockchain_events.fetch_logs_in_batch(target_block_number)
File "/Users/ulo/Dropbox/devel/brainbot/raiden/raiden/blockchain/events.py", line 488, in fetch_logs_in_batch
decoded_result = self._query_and_track(from_block, to_block)
File "/Users/ulo/Dropbox/devel/brainbot/raiden/raiden/blockchain/events.py", line 556, in _query_and_track
RPCEndpoint("eth_getLogs"), [filter_params]
File "/Users/ulo/Envs/raiden/lib/python3.7/site-packages/web3/manager.py", line 149, in request_blocking
response = self._make_request(method, params)
File "/Users/ulo/Envs/raiden/lib/python3.7/site-packages/web3/manager.py", line 128, in _make_request
return request_func(method, params)
File "/Users/ulo/Envs/raiden/lib/python3.7/site-packages/web3/middleware/cache.py", line 134, in middleware
return make_request(method, params)
File "cytoolz/functoolz.pyx", line 250, in cytoolz.functoolz.curry.__call__
File "/Users/ulo/Envs/raiden/lib/python3.7/site-packages/web3/middleware/formatting.py", line 73, in apply_formatters
response = make_request(method, formatted_params)
File "/Users/ulo/Envs/raiden/lib/python3.7/site-packages/web3/middleware/gas_price_strategy.py", line 34, in middleware
return make_request(method, params)
File "cytoolz/functoolz.pyx", line 250, in cytoolz.functoolz.curry.__call__
File "/Users/ulo/Envs/raiden/lib/python3.7/site-packages/web3/middleware/formatting.py", line 73, in apply_formatters
response = make_request(method, formatted_params)
File "/Users/ulo/Envs/raiden/lib/python3.7/site-packages/web3/middleware/attrdict.py", line 33, in middleware
response = make_request(method, params)
File "cytoolz/functoolz.pyx", line 250, in cytoolz.functoolz.curry.__call__
File "/Users/ulo/Envs/raiden/lib/python3.7/site-packages/web3/middleware/formatting.py", line 73, in apply_formatters
response = make_request(method, formatted_params)
File "/Users/ulo/Envs/raiden/lib/python3.7/site-packages/web3/middleware/normalize_errors.py", line 25, in middleware
result = make_request(method, params)
File "cytoolz/functoolz.pyx", line 250, in cytoolz.functoolz.curry.__call__
File "/Users/ulo/Envs/raiden/lib/python3.7/site-packages/web3/middleware/formatting.py", line 75, in apply_formatters
response = make_request(method, params)
File "cytoolz/functoolz.pyx", line 250, in cytoolz.functoolz.curry.__call__
File "/Users/ulo/Envs/raiden/lib/python3.7/site-packages/web3/middleware/formatting.py", line 73, in apply_formatters
response = make_request(method, formatted_params)
File "cytoolz/functoolz.pyx", line 250, in cytoolz.functoolz.curry.__call__
File "/Users/ulo/Envs/raiden/lib/python3.7/site-packages/web3/middleware/formatting.py", line 75, in apply_formatters
response = make_request(method, params)
File "/Users/ulo/Envs/raiden/lib/python3.7/site-packages/web3/middleware/exception_retry_request.py", line 104, in middleware
return make_request(method, params)
File "/Users/ulo/Envs/raiden/lib/python3.7/site-packages/web3/providers/rpc.py", line 83, in make_request
**self.get_request_kwargs()
File "/Users/ulo/Envs/raiden/lib/python3.7/site-packages/web3/_utils/request.py", line 34, in make_post_request
response = session.post(endpoint_uri, data=data, *args, **kwargs)  # type: ignore
File "/Users/ulo/Envs/raiden/lib/python3.7/site-packages/requests/sessions.py", line 581, in post
return self.request('POST', url, data=data, json=json, **kwargs)
File "/Users/ulo/Envs/raiden/lib/python3.7/site-packages/requests/sessions.py", line 533, in request
resp = self.send(prep, **send_kwargs)
File "/Users/ulo/Envs/raiden/lib/python3.7/site-packages/requests/sessions.py", line 646, in send
r = adapter.send(request, **kwargs)
File "/Users/ulo/Envs/raiden/lib/python3.7/site-packages/requests/adapters.py", line 529, in send
raise ReadTimeout(e, request=request)
requests.exceptions.ReadTimeout: HTTPConnectionPool(host='geth.mainnet.ethnodes.brainbot.com', port=8545): Read timed out. (read timeout=10)

# Fixed code
def _poll_until_target(self, target_block_number: BlockNumber) -> None:
    """Poll blockchain events up to `target_block_number`.

    Multiple queries may be necessary on restarts, because the node may
    have been offline for an extend period of time. During normal
    operation, this must not happen, because in this case the node may have
    missed important events, like a channel close, while the transport
    layer is running, this can lead to loss of funds.

    It is very important for `confirmed_target_block_number` to be an
    confirmed block, otherwise reorgs may cause havoc. This is problematic
    since some operations are irreversible, namely sending a balance proof.
    Once a node accepts a deposit, these tokens can be used to do mediated
    transfers, and if a reorg removes the deposit tokens could be lost.

    This function takes care of fetching blocks in batches and confirming
    their result. This is important to keep memory usage low and to speed
    up restarts. Memory usage can get a hit if the node is asleep for a
    long period of time and on the first run, since all the missing
    confirmed blocks have to be fetched before the node is in a working
    state. Restarts get a hit if the node is closed while it was
    synchronizing, without regularly saving that work, if the node is
    killed while synchronizing, it only gets gradually slower.

    Returns:
        int: number of polling queries required to synchronized with
        `target_block_number`.
    """
    msg = (
        f"The blockchain event handler has to be instantiated before the "
        f"alarm task is started. node:{self!r}"
    )
    assert self.blockchain_events, msg

    sync_start = datetime.now()
    while self.blockchain_events.last_fetched_block < target_block_number:
        poll_result = self.blockchain_events.fetch_logs_in_batch(target_block_number)
        if poll_result is None:
            # No blocks could be fetched (due to timeout), retry
            continue

        pendingtokenregistration: Dict[
            TokenNetworkAddress, Tuple[TokenNetworkRegistryAddress, TokenAddress]
        ] = dict()

        state_changes: List[StateChange] = list()
        for event in poll_result.events:
            state_changes.extend(
                blockchainevent_to_statechange(
                    self,
                    event,
                    poll_result.polled_block_number,
                    pendingtokenregistration,
                )
            )

        # On restarts the node has to pick up all events generated since the
        # last run. To do this the node will set the filters' from_block to
        # the value of the latest block number known to have *all* events
        # processed.
        #
        # To guarantee the above the node must either:
        #
        # - Dispatch the state changes individually, leaving the Block
        # state change last, so that it knows all the events for the
        # given block have been processed. On restarts this can result in
        # the same event being processed twice.
        # - Dispatch all the smart contract events together with the Block
        # state change in a single transaction, either all or nothing will
        # be applied, and on a restart the node picks up from where it
        # left.
        #
        # The approach used below is to dispatch the Block and the
        # blockchain events in a single transaction. This is the preferred
        # approach because it guarantees that no events will be missed and
        # it fixes race conditions on the value of the block number value,
        # that can lead to crashes.
        #
        # Example: The user creates a new channel with an initial deposit
        # of X tokens. This is done with two operations, the first is to
        # open the new channel, the second is to deposit the requested
        # tokens in it. Once the node fetches the event for the new channel,
        # it will immediately request the deposit, which leaves a window for
        # a race condition. If the Block state change was not yet
        # processed, the block hash used as the triggering block for the
        # deposit will be off-by-one, and it will point to the block
        # immediately before the channel existed. This breaks a proxy
        # precondition which crashes the client.
        block_state_change = Block(
            block_number=poll_result.polled_block_number,
            gas_limit=poll_result.polled_block_gas_limit,
            block_hash=poll_result.polled_block_hash,
        )
        state_changes.append(block_state_change)

        # It's important to /not/ block here, because this function can
        # be called from the alarm task greenlet, which should not
        # starve. This was a problem when the node decided to send a new
        # transaction, since the proxies block until the transaction is
        # mined and confirmed (e.g. the settle window is over and the
        # node sends the settle transaction).
        self.handle_and_track_state_changes(state_changes)

        self._log_sync_progress(poll_result.polled_block_number, target_block_number)

    sync_end = datetime.now()
    log.debug(
        "Synchronized to a new confirmed block",
        event_filters_qty=len(self.blockchain_events._address_to_filters),
        sync_elapsed=sync_end - sync_start,
    )