Exemple #1
0
    def __init__(
        self,
        web3: Web3,
        contract_manager: ContractManager,
        registry_address: Address,
        sync_start_block: int = 0,
        required_confirmations: int = 8,
        poll_interval: int = 10,
    ):
        """ Creates a new pathfinding service

        Args:
            contract_manager: A contract manager
            token_network_listener: A blockchain listener object
            token_network_registry_listener: A blockchain listener object for the network registry
            chain_id: The id of the chain the PFS runs on
        """
        super().__init__()
        self.web3 = web3
        self.contract_manager = contract_manager
        self.registry_address = registry_address
        self.sync_start_block = sync_start_block
        self.required_confirmations = required_confirmations
        self.poll_interval = poll_interval
        self.chain_id = int(web3.net.version)

        self.is_running = gevent.event.Event()
        self.token_networks: Dict[Address, TokenNetwork] = {}
        self.token_network_listeners: List[BlockchainListener] = []

        self.is_running = gevent.event.Event()

        log.info('Starting TokenNetworkRegistry Listener (required confirmations: {})...'.format(
            self.required_confirmations,
        ))
        self.token_network_registry_listener = BlockchainListener(
            web3=web3,
            contract_manager=self.contract_manager,
            contract_name=CONTRACT_TOKEN_NETWORK_REGISTRY,
            contract_address=self.registry_address,
            required_confirmations=self.required_confirmations,
            poll_interval=self.poll_interval,
            sync_start_block=self.sync_start_block,
        )
        log.info(
            f'Listening to token network registry @ {registry_address} '
            f'from block {sync_start_block}',
        )
        self._setup_token_networks()
Exemple #2
0
def blockchain_listener(web3, contracts_manager, token_network):
    blockchain_listener = BlockchainListener(
        web3=web3,
        contract_manager=contracts_manager,
        contract_name=CONTRACT_TOKEN_NETWORK,
        contract_address=token_network.address,
        poll_interval=0,
    )
    return blockchain_listener
Exemple #3
0
    def create_token_network_for_address(
        self,
        token_network_address: Address,
        token_address: Address,
        block_number: int = 0,
    ):
        token_network = TokenNetwork(token_network_address, token_address)
        self.token_networks[token_network_address] = token_network

        log.debug('Creating token network model', token_network_address=token_network_address)
        token_network_listener = BlockchainListener(
            web3=self.web3,
            contract_manager=self.contract_manager,
            contract_address=token_network_address,
            contract_name=CONTRACT_TOKEN_NETWORK,
            required_confirmations=self.required_confirmations,
            poll_interval=self.poll_interval,
            sync_start_block=block_number,
        )

        # subscribe to event notifications from blockchain listener
        token_network_listener.add_confirmed_listener(
            create_channel_event_topics(),
            self.handle_channel_event,
        )
        token_network_listener.start()
        self.token_network_listeners.append(token_network_listener)
Exemple #4
0
    def __init__(
            self,
            web3: Web3,
            contract_manager: ContractManager,
            registry_address: Address,
            private_key: str,
            db_filename: str,
            user_deposit_contract_address: Address,
            sync_start_block: int = 0,
            required_confirmations: int = 8,
            poll_interval: int = 10,
            service_fee: int = 0,
    ):
        """ Creates a new pathfinding service

        Args:
            contract_manager: A contract manager
            token_network_listener: A blockchain listener object
            token_network_registry_listener: A blockchain listener object for the network registry
            chain_id: The id of the chain the PFS runs on
        """
        super().__init__()

        self.web3 = web3
        self.contract_manager = contract_manager
        self.registry_address = registry_address
        self.sync_start_block = sync_start_block
        self.required_confirmations = required_confirmations
        self.poll_interval = poll_interval
        self.chain_id = int(web3.net.version)
        self.private_key = private_key
        self.address = private_key_to_address(private_key)
        self.service_fee = service_fee

        self.is_running = gevent.event.Event()
        self.token_networks: Dict[Address, TokenNetwork] = {}
        self.token_network_listeners: List[BlockchainListener] = []
        self.database = PFSDatabase(
            filename=db_filename,
            pfs_address=self.address,
        )
        self.user_deposit_contract = web3.eth.contract(
            abi=self.contract_manager.get_contract_abi(
                CONTRACT_USER_DEPOSIT,
            ),
            address=user_deposit_contract_address,
        )

        log.info(
            'Starting TokenNetworkRegistry Listener',
            required_confirmations=self.required_confirmations,
        )
        self.token_network_registry_listener = BlockchainListener(
            web3=web3,
            contract_manager=self.contract_manager,
            contract_name=CONTRACT_TOKEN_NETWORK_REGISTRY,
            contract_address=self.registry_address,
            required_confirmations=self.required_confirmations,
            poll_interval=self.poll_interval,
            sync_start_block=self.sync_start_block,
        )
        log.info(
            'Listening to token network registry',
            registry_address=registry_address,
            start_block=sync_start_block,
        )
        self._setup_token_networks()

        try:
            self.matrix_listener = MatrixListener(
                private_key=private_key,
                chain_id=self.chain_id,
                callback=self.handle_message,
                service_room_suffix=PATH_FINDING_BROADCASTING_ROOM,
            )
        except ConnectionError as e:
            log.critical(
                'Could not connect to broadcasting system.',
                exc=e,
            )
            sys.exit(1)
Exemple #5
0
class PathfindingService(gevent.Greenlet):
    def __init__(
            self,
            web3: Web3,
            contract_manager: ContractManager,
            registry_address: Address,
            private_key: str,
            db_filename: str,
            user_deposit_contract_address: Address,
            sync_start_block: int = 0,
            required_confirmations: int = 8,
            poll_interval: int = 10,
            service_fee: int = 0,
    ):
        """ Creates a new pathfinding service

        Args:
            contract_manager: A contract manager
            token_network_listener: A blockchain listener object
            token_network_registry_listener: A blockchain listener object for the network registry
            chain_id: The id of the chain the PFS runs on
        """
        super().__init__()

        self.web3 = web3
        self.contract_manager = contract_manager
        self.registry_address = registry_address
        self.sync_start_block = sync_start_block
        self.required_confirmations = required_confirmations
        self.poll_interval = poll_interval
        self.chain_id = int(web3.net.version)
        self.private_key = private_key
        self.address = private_key_to_address(private_key)
        self.service_fee = service_fee

        self.is_running = gevent.event.Event()
        self.token_networks: Dict[Address, TokenNetwork] = {}
        self.token_network_listeners: List[BlockchainListener] = []
        self.database = PFSDatabase(
            filename=db_filename,
            pfs_address=self.address,
        )
        self.user_deposit_contract = web3.eth.contract(
            abi=self.contract_manager.get_contract_abi(
                CONTRACT_USER_DEPOSIT,
            ),
            address=user_deposit_contract_address,
        )

        log.info(
            'Starting TokenNetworkRegistry Listener',
            required_confirmations=self.required_confirmations,
        )
        self.token_network_registry_listener = BlockchainListener(
            web3=web3,
            contract_manager=self.contract_manager,
            contract_name=CONTRACT_TOKEN_NETWORK_REGISTRY,
            contract_address=self.registry_address,
            required_confirmations=self.required_confirmations,
            poll_interval=self.poll_interval,
            sync_start_block=self.sync_start_block,
        )
        log.info(
            'Listening to token network registry',
            registry_address=registry_address,
            start_block=sync_start_block,
        )
        self._setup_token_networks()

        try:
            self.matrix_listener = MatrixListener(
                private_key=private_key,
                chain_id=self.chain_id,
                callback=self.handle_message,
                service_room_suffix=PATH_FINDING_BROADCASTING_ROOM,
            )
        except ConnectionError as e:
            log.critical(
                'Could not connect to broadcasting system.',
                exc=e,
            )
            sys.exit(1)

    def _setup_token_networks(self):
        self.token_network_registry_listener.add_confirmed_listener(
            create_registry_event_topics(self.contract_manager),
            self.handle_token_network_created,
        )

    def _run(self):
        register_error_handler(error_handler)
        self.matrix_listener.start()
        self.token_network_registry_listener.start()
        self.is_running.wait()

    def stop(self):
        self.token_network_registry_listener.stop()
        for task in self.token_network_listeners:
            task.stop()
        self.matrix_listener.stop()
        self.is_running.set()
        self.matrix_listener.join()

    def follows_token_network(self, token_network_address: Address) -> bool:
        """ Checks if a token network is followed by the pathfinding service. """
        assert is_checksum_address(token_network_address)

        return token_network_address in self.token_networks.keys()

    def _get_token_network(self, token_network_address: Address) -> Optional[TokenNetwork]:
        """ Returns the `TokenNetwork` for the given address or `None` for unknown networks. """

        assert is_checksum_address(token_network_address)
        if not self.follows_token_network(token_network_address):
            return None
        else:
            return self.token_networks[token_network_address]

    def handle_channel_event(self, event: Dict):
        event_name = event['event']

        if event_name == ChannelEvent.OPENED:
            self.handle_channel_opened(event)
        elif event_name == ChannelEvent.DEPOSIT:
            self.handle_channel_new_deposit(event)
        elif event_name == ChannelEvent.CLOSED:
            self.handle_channel_closed(event)
        else:
            log.debug('Unhandled event', evt=event)

    def handle_channel_opened(self, event: Dict):
        token_network = self._get_token_network(event['address'])

        if token_network is None:
            return

        channel_identifier = event['args']['channel_identifier']
        participant1 = event['args']['participant1']
        participant2 = event['args']['participant2']
        settle_timeout = event['args']['settle_timeout']

        log.info(
            'Received ChannelOpened event',
            token_network_address=token_network.address,
            channel_identifier=channel_identifier,
            participant1=participant1,
            participant2=participant2,
            settle_timeout=settle_timeout,
        )

        token_network.handle_channel_opened_event(
            channel_identifier,
            participant1,
            participant2,
            settle_timeout,
        )

    def handle_channel_new_deposit(self, event: Dict):
        token_network = self._get_token_network(event['address'])

        if token_network is None:
            return

        channel_identifier = event['args']['channel_identifier']
        participant_address = event['args']['participant']
        total_deposit = event['args']['total_deposit']

        log.info(
            'Received ChannelNewDeposit event',
            token_network_address=token_network.address,
            channel_identifier=channel_identifier,
            participant=participant_address,
            total_deposit=total_deposit,
        )

        token_network.handle_channel_new_deposit_event(
            channel_identifier,
            participant_address,
            total_deposit,
        )

    def handle_channel_closed(self, event: Dict):
        token_network = self._get_token_network(event['address'])

        if token_network is None:
            return

        channel_identifier = event['args']['channel_identifier']

        log.info(
            'Received ChannelClosed event',
            token_network_address=token_network.address,
            channel_identifier=channel_identifier,
        )

        token_network.handle_channel_closed_event(channel_identifier)

    def handle_token_network_created(self, event):
        token_network_address = event['args']['token_network_address']
        token_address = event['args']['token_address']
        event_block_number = event['blockNumber']

        assert is_checksum_address(token_network_address)
        assert is_checksum_address(token_address)

        if not self.follows_token_network(token_network_address):
            log.info(
                'Found new token network',
                token_address=token_address,
                token_network_address=token_network_address,
            )
            self.create_token_network_for_address(
                token_network_address,
                token_address,
                event_block_number,
            )

    def create_token_network_for_address(
        self,
        token_network_address: Address,
        token_address: Address,
        block_number: int = 0,
    ):
        token_network = TokenNetwork(token_network_address, token_address)
        self.token_networks[token_network_address] = token_network

        log.debug('Creating token network model', token_network_address=token_network_address)
        token_network_listener = BlockchainListener(
            web3=self.web3,
            contract_manager=self.contract_manager,
            contract_address=token_network_address,
            contract_name=CONTRACT_TOKEN_NETWORK,
            required_confirmations=self.required_confirmations,
            poll_interval=self.poll_interval,
            sync_start_block=block_number,
        )

        # subscribe to event notifications from blockchain listener
        token_network_listener.add_confirmed_listener(
            create_channel_event_topics(),
            self.handle_channel_event,
        )
        token_network_listener.start()
        self.token_network_listeners.append(token_network_listener)

    def handle_message(self, message: SignedMessage):
        if isinstance(message, UpdatePFS):
            try:
                self.on_pfs_update(message)
            except InvalidCapacityUpdate as x:
                log.info(
                    str(x),
                    chain_id=message.canonical_identifier.chain_identifier,
                    token_network_address=message.canonical_identifier.token_network_address,
                    channel_identifier=message.canonical_identifier.channel_identifier,
                    updating_capacity=message.updating_capacity,
                    other_capacity=message.updating_capacity,
                )
        else:
            log.info('Ignoring unknown message type')

    def on_pfs_update(self, message: UpdatePFS):
        token_network_address = to_checksum_address(
            message.canonical_identifier.token_network_address,
        )
        log.info(
            'Received Capacity Update',
            token_network_address=token_network_address,
            channel_identifier=message.canonical_identifier.channel_identifier,
        )

        assert is_checksum_address(message.updating_participant)
        assert is_checksum_address(message.other_participant)

        # check if chain_id matches
        if message.canonical_identifier.chain_identifier != self.chain_id:
            raise InvalidCapacityUpdate('Received Capacity Update with unknown chain identifier')

        # check if token network exists
        token_network = self._get_token_network(token_network_address)
        if token_network is None:
            raise InvalidCapacityUpdate('Received Capacity Update with unknown token network')

        # check if channel exists
        channel_identifier = message.canonical_identifier.channel_identifier
        if channel_identifier not in token_network.channel_id_to_addresses:
            raise InvalidCapacityUpdate(
                'Received Capacity Update with unknown channel identifier in token network',
            )

        # TODO: check signature of message

        # check values < max int 256
        if message.updating_capacity > UINT256_MAX:
            raise InvalidCapacityUpdate(
                'Received Capacity Update with impossible updating_capacity',
            )
        if message.other_capacity > UINT256_MAX:
            raise InvalidCapacityUpdate(
                'Received Capacity Update with impossible other_capacity',
            )

        # check if participants fit to channel id
        participants = token_network.channel_id_to_addresses[channel_identifier]
        if message.updating_participant not in participants:
            raise InvalidCapacityUpdate(
                'Sender of Capacity Update does not match the internal channel',
            )
        if message.other_participant not in participants:
            raise InvalidCapacityUpdate(
                'Other Participant of Capacity Update does not match the internal channel',
            )

        # check if nonce is higher than current nonce
        view_to_partner, view_from_partner = token_network.get_channel_views_for_partner(
            channel_identifier=channel_identifier,
            updating_participant=message.updating_participant,
            other_participant=message.other_participant,
        )

        valid_nonces = (
            message.updating_nonce <= view_to_partner.update_nonce and
            message.other_nonce <= view_from_partner.update_nonce
        )
        if valid_nonces:
            raise InvalidCapacityUpdate('Capacity Update already received')

        token_network.handle_channel_balance_update_message(
            channel_identifier=message.canonical_identifier.channel_identifier,
            updating_participant=message.updating_participant,
            other_participant=message.other_participant,
            updating_nonce=message.updating_nonce,
            other_nonce=message.other_nonce,
            updating_capacity=message.updating_capacity,
            other_capacity=message.other_capacity,
            reveal_timeout=message.reveal_timeout,
        )
def test_blockchain_listener_nonexistant_contract(
    web3: Web3,
    wait_for_blocks,
    generate_raiden_clients,
    blockchain_listener: BlockchainListener,
    ethereum_tester,
):
    blockchain_listener.required_confirmations = 4
    blockchain_listener.contract_address = '0xaAaAaAaaAaAaAaaAaAAAAAAAAaaaAaAaAaaAaaAa'
    blockchain_listener.start()
    blockchain_listener.wait_sync()

    unconfirmed_channel_open_events: List[Dict] = []
    confirmed_channel_open_events: List[Dict] = []

    blockchain_listener.add_unconfirmed_listener(
        create_channel_event_topics(),
        unconfirmed_channel_open_events.append,
    )
    blockchain_listener.add_confirmed_listener(
        create_channel_event_topics(),
        confirmed_channel_open_events.append,
    )

    # create unconfirmed channel
    c1, c2 = generate_raiden_clients(2)
    c1.open_channel(c2.address)

    # no unconfirmed event should be available
    wait_for_blocks(0)
    assert len(unconfirmed_channel_open_events) == 0

    # no confirmed event should be available after 4 more blocks
    wait_for_blocks(4)
    assert len(confirmed_channel_open_events) == 0

    blockchain_listener.stop()
def test_blockchain_listener(
    web3: Web3,
    wait_for_blocks,
    generate_raiden_clients,
    blockchain_listener: BlockchainListener,
    ethereum_tester,
):
    blockchain_listener.required_confirmations = 4
    blockchain_listener.start()
    blockchain_listener.wait_sync()

    unconfirmed_channel_open_events: List[Dict] = []
    confirmed_channel_open_events: List[Dict] = []

    blockchain_listener.add_unconfirmed_listener(
        create_channel_event_topics(),
        unconfirmed_channel_open_events.append,
    )
    blockchain_listener.add_confirmed_listener(
        create_channel_event_topics(),
        confirmed_channel_open_events.append,
    )

    # create unconfirmed channel
    c1, c2 = generate_raiden_clients(2)
    c1.open_channel(c2.address)

    # the unconfirmed event should be available directly
    wait_for_blocks(0)
    assert len(unconfirmed_channel_open_events) == 1
    assert unconfirmed_channel_open_events[0]['args'][
        'participant1'] == c1.address
    assert unconfirmed_channel_open_events[0]['args'][
        'participant2'] == c2.address
    # settle_timeout acc. to mock client = 15
    assert unconfirmed_channel_open_events[0]['args']['settle_timeout'] == 15

    # the confirmed event should be available after 4 more blocks as set above
    assert len(confirmed_channel_open_events) == 0
    wait_for_blocks(4)
    assert len(confirmed_channel_open_events) == 1
    assert confirmed_channel_open_events[0]['args'][
        'participant1'] == c1.address
    assert confirmed_channel_open_events[0]['args'][
        'participant2'] == c2.address
    # settle_timeout acc. to mock client = 15
    assert confirmed_channel_open_events[0]['args']['settle_timeout'] == 15

    blockchain_listener.stop()
def test_reorg(
    web3: Web3,
    wait_for_blocks,
    generate_raiden_clients,
    blockchain_listener: BlockchainListener,
    ethereum_tester,
):
    blockchain_listener.required_confirmations = 5
    blockchain_listener.start()
    blockchain_listener.wait_sync()

    unconfirmed_channel_open_events: List[Dict] = []
    blockchain_listener.add_unconfirmed_listener(
        create_channel_event_topics(),
        unconfirmed_channel_open_events.append,
    )

    c1, c2 = generate_raiden_clients(2)
    snapshot_id = web3.testing.snapshot()

    # create unconfirmed channel
    c1.open_channel(c2.address)
    wait_for_blocks(0)
    assert len(unconfirmed_channel_open_events) == 1
    assert unconfirmed_channel_open_events[0]['args'][
        'participant1'] == c1.address
    assert unconfirmed_channel_open_events[0]['args'][
        'participant2'] == c2.address

    # remove unconfirmed channel opening with reorg
    web3.testing.revert(snapshot_id)

    # run the BlockchainListener again, it should have a lower head_number now
    old_head_number = blockchain_listener.unconfirmed_head_number
    wait_for_blocks(0)
    new_head_number = blockchain_listener.unconfirmed_head_number

    assert old_head_number > new_head_number

    # test that a chain reorg of one block is handled
    # this created a channel first, then reverts and creates a different channel
    unconfirmed_channel_open_events.clear()
    c1.open_channel(c2.address)
    wait_for_blocks(0)
    assert len(unconfirmed_channel_open_events) == 1
    assert unconfirmed_channel_open_events[0]['args'][
        'participant1'] == c1.address
    assert unconfirmed_channel_open_events[0]['args'][
        'participant2'] == c2.address
    web3.testing.revert(snapshot_id)
    c2.open_channel(c1.address)
    wait_for_blocks(0)
    assert len(unconfirmed_channel_open_events) == 2
    assert unconfirmed_channel_open_events[1]['args'][
        'participant1'] == c2.address
    assert unconfirmed_channel_open_events[1]['args'][
        'participant2'] == c1.address

    web3.testing.revert(snapshot_id)

    # test a big chain reorg (> required_confirmations)
    confirmed_channel_open_events: List[Dict] = []
    blockchain_listener.add_confirmed_listener(
        create_channel_event_topics(),
        confirmed_channel_open_events.append,
    )
    c1.open_channel(c2.address)

    # create a new event and wait till it's confirmed
    wait_for_blocks(5)

    assert len(confirmed_channel_open_events) == 1

    # revert the chain, this should kill the process
    web3.testing.revert(snapshot_id)

    with pytest.raises(SystemExit):
        wait_for_blocks(0)

    blockchain_listener.stop()
class PathfindingService(gevent.Greenlet):
    def __init__(
        self,
        web3: Web3,
        contract_manager: ContractManager,
        registry_address: Address,
        sync_start_block: int = 0,
        required_confirmations: int = 8,
        poll_interval: int = 10,
    ) -> None:
        """ Creates a new pathfinding service

        Args:
            contract_manager: A contract manager
            token_network_listener: A blockchain listener object
            token_network_registry_listener: A blockchain listener object for the network registry
            chain_id: The id of the chain the PFS runs on
        """
        super().__init__()
        self.web3 = web3
        self.contract_manager = contract_manager
        self.registry_address = registry_address
        self.sync_start_block = sync_start_block
        self.required_confirmations = required_confirmations
        self.poll_interval = poll_interval
        self.chain_id = int(web3.net.version)

        self.is_running = gevent.event.Event()
        self.token_networks: Dict[Address, TokenNetwork] = {}
        self.token_network_listeners: List[BlockchainListener] = []

        self.is_running = gevent.event.Event()

        log.info(
            'Starting TokenNetworkRegistry Listener (required confirmations: {})...'
            .format(self.required_confirmations, ))
        self.token_network_registry_listener = BlockchainListener(
            web3=web3,
            contract_manager=self.contract_manager,
            contract_name=CONTRACT_TOKEN_NETWORK_REGISTRY,
            contract_address=self.registry_address,
            required_confirmations=self.required_confirmations,
            poll_interval=self.poll_interval,
            sync_start_block=self.sync_start_block,
        )
        log.info(
            f'Listening to token network registry @ {registry_address} '
            f'from block {sync_start_block}', )
        self._setup_token_networks()

    def _setup_token_networks(self):
        self.token_network_registry_listener.add_confirmed_listener(
            create_registry_event_topics(self.contract_manager),
            self.handle_token_network_created,
        )

    def _run(self):
        register_error_handler(error_handler)

        self.token_network_registry_listener.start()

        self.is_running.wait()

    def stop(self):
        self.token_network_registry_listener.stop()
        for task in self.token_network_listeners:
            task.stop()
        self.is_running.set()

    def follows_token_network(self, token_network_address: Address) -> bool:
        """ Checks if a token network is followed by the pathfinding service. """
        assert is_checksum_address(token_network_address)

        return token_network_address in self.token_networks.keys()

    def _get_token_network(
            self, token_network_address: Address) -> Optional[TokenNetwork]:
        """ Returns the `TokenNetwork` for the given address or `None` for unknown networks. """

        assert is_checksum_address(token_network_address)

        if not self.follows_token_network(token_network_address):
            return None
        else:
            return self.token_networks[token_network_address]

    def _check_chain_id(self, received_chain_id: int):
        if not received_chain_id == self.chain_id:
            raise ValueError('Chain id does not match')

    def handle_channel_event(self, event: Dict):
        event_name = event['event']

        if event_name == ChannelEvent.OPENED:
            self.handle_channel_opened(event)
        elif event_name == ChannelEvent.DEPOSIT:
            self.handle_channel_new_deposit(event)
        elif event_name == ChannelEvent.CLOSED:
            self.handle_channel_closed(event)
        else:
            log.info('Unhandled event: %s', event_name)

    def handle_channel_opened(self, event: Dict):
        token_network = self._get_token_network(event['address'])

        if token_network is None:
            return

        log.debug('Received ChannelOpened event for token network {}'.format(
            token_network.address, ))

        channel_identifier = event['args']['channel_identifier']
        participant1 = event['args']['participant1']
        participant2 = event['args']['participant2']

        token_network.handle_channel_opened_event(
            channel_identifier,
            participant1,
            participant2,
        )

    def handle_channel_new_deposit(self, event: Dict):
        token_network = self._get_token_network(event['address'])

        if token_network is None:
            return

        log.debug(
            'Received ChannelNewDeposit event for token network {}'.format(
                token_network.address, ))

        channel_identifier = event['args']['channel_identifier']
        participant_address = event['args']['participant']
        total_deposit = event['args']['total_deposit']

        token_network.handle_channel_new_deposit_event(
            channel_identifier,
            participant_address,
            total_deposit,
        )

    def handle_channel_closed(self, event: Dict):
        token_network = self._get_token_network(event['address'])

        if token_network is None:
            return

        log.debug('Received ChannelClosed event for token network {}'.format(
            token_network.address, ))

        channel_identifier = event['args']['channel_identifier']

        token_network.handle_channel_closed_event(channel_identifier)

    def handle_token_network_created(self, event):
        token_network_address = event['args']['token_network_address']
        token_address = event['args']['token_address']
        event_block_number = event['blockNumber']

        assert is_checksum_address(token_network_address)
        assert is_checksum_address(token_address)

        if not self.follows_token_network(token_network_address):
            log.info(
                f'Found token network for token {token_address} @ {token_network_address}'
            )
            self.create_token_network_for_address(
                token_network_address,
                token_address,
                event_block_number,
            )

    def create_token_network_for_address(
        self,
        token_network_address: Address,
        token_address: Address,
        block_number: int = 0,
    ):
        token_network = TokenNetwork(token_network_address, token_address)
        self.token_networks[token_network_address] = token_network

        log.info('Creating token network for %s', token_network_address)
        token_network_listener = BlockchainListener(
            web3=self.web3,
            contract_manager=self.contract_manager,
            contract_address=token_network_address,
            contract_name=CONTRACT_TOKEN_NETWORK,
            required_confirmations=self.required_confirmations,
            poll_interval=self.poll_interval,
            sync_start_block=block_number,
        )

        # subscribe to event notifications from blockchain listener
        token_network_listener.add_confirmed_listener(
            create_channel_event_topics(),
            self.handle_channel_event,
        )
        token_network_listener.start()
        self.token_network_listeners.append(token_network_listener)