def test_cooperative(
        channel_manager: ChannelManager,
        confirmed_open_channel: Channel,
        receiver_address: str,
        web3: Web3,
        token_contract: Contract,
        wait_for_blocks,
        sender_address: str
):
    blockchain = channel_manager.blockchain
    channel_id = (confirmed_open_channel.sender, confirmed_open_channel.block)

    sig1 = encode_hex(confirmed_open_channel.create_transfer(5))
    channel_manager.register_payment(sender_address, confirmed_open_channel.block, 5, sig1)

    receiver_sig = channel_manager.sign_close(sender_address, confirmed_open_channel.block, 5)
    channel_rec = channel_manager.channels[channel_id]
    assert channel_rec.is_closed is True
    block_before = web3.eth.blockNumber
    confirmed_open_channel.close_cooperatively(receiver_sig)
    wait_for_blocks(blockchain.n_confirmations)
    gevent.sleep(blockchain.poll_interval)
    logs = get_logs(token_contract, 'Transfer', from_block=block_before - 1)
    assert len([l for l in logs
                if is_same_address(l['args']['_to'], receiver_address) and
                l['args']['_value'] == 5]) == 1
    assert len([l for l in logs
                if is_same_address(l['args']['_to'], sender_address) and
                l['args']['_value'] == 5]) == 1
    wait_for_blocks(blockchain.n_confirmations)
    gevent.sleep(blockchain.poll_interval)
    assert channel_id not in channel_manager.channels
Example #2
0
def test_settlement(
        channel_manager: ChannelManager,
        confirmed_open_channel: Channel,
        receiver_address: str,
        wait_for_blocks,
        web3: Web3,
        token_contract: Contract,
        sender_address: str
):
    blockchain = channel_manager.blockchain
    channel_id = (confirmed_open_channel.sender, confirmed_open_channel.block)

    sig = encode_hex(confirmed_open_channel.create_transfer(2))
    channel_manager.register_payment(sender_address, confirmed_open_channel.block, 2, sig)

    confirmed_open_channel.close()
    wait_for_blocks(blockchain.n_confirmations)
    gevent.sleep(blockchain.poll_interval)
    block_before = web3.eth.blockNumber
    channel_rec = channel_manager.channels[channel_id]
    wait_for_blocks(channel_rec.settle_timeout - block_before)
    confirmed_open_channel.settle()

    logs = get_logs(token_contract, 'Transfer', from_block=block_before - 1)
    assert len([l for l in logs
                if is_same_address(l['args']['_to'], receiver_address) and
                l['args']['_value'] == 2]) == 1
    assert len([l for l in logs
                if is_same_address(l['args']['_to'], sender_address) and
                l['args']['_value'] == 8]) == 1

    wait_for_blocks(blockchain.n_confirmations)
    gevent.sleep(blockchain.poll_interval)
    assert channel_id not in channel_manager.channels
Example #3
0
def test_cooperative(
        channel_manager: ChannelManager,
        confirmed_open_channel: Channel,
        receiver_address: str,
        web3: Web3,
        token_contract: Contract,
        wait_for_blocks,
        sender_address: str
):
    blockchain = channel_manager.blockchain
    channel_id = (confirmed_open_channel.sender, confirmed_open_channel.block)

    sig1 = encode_hex(confirmed_open_channel.create_transfer(5))
    channel_manager.register_payment(sender_address, confirmed_open_channel.block, 5, sig1)

    receiver_sig = channel_manager.sign_close(sender_address, confirmed_open_channel.block, 5)
    channel_rec = channel_manager.channels[channel_id]
    assert channel_rec.is_closed is True
    block_before = web3.eth.blockNumber
    confirmed_open_channel.close_cooperatively(receiver_sig)
    wait_for_blocks(blockchain.n_confirmations)
    gevent.sleep(blockchain.poll_interval)
    logs = get_logs(token_contract, 'Transfer', from_block=block_before - 1)
    assert len([l for l in logs
                if is_same_address(l['args']['_to'], receiver_address) and
                l['args']['_value'] == 5]) == 1
    assert len([l for l in logs
                if is_same_address(l['args']['_to'], sender_address) and
                l['args']['_value'] == 5]) == 1
    wait_for_blocks(blockchain.n_confirmations)
    gevent.sleep(blockchain.poll_interval)
    assert channel_id not in channel_manager.channels
def test_challenge(
        channel_manager: ChannelManager,
        confirmed_open_channel: Channel,
        receiver_address: str,
        sender_address: str,
        wait_for_blocks,
        web3: Web3,
        client: Client
):
    blockchain = channel_manager.blockchain
    channel_id = (confirmed_open_channel.sender, confirmed_open_channel.block)
    sig = encode_hex(confirmed_open_channel.create_transfer(5))
    channel_manager.register_payment(sender_address, confirmed_open_channel.block, 5, sig)
    # hack channel to decrease balance
    confirmed_open_channel.update_balance(0)
    sig = confirmed_open_channel.create_transfer(3)
    block_before = web3.eth.blockNumber
    confirmed_open_channel.close()
    # should challenge and immediately settle
    for waited_blocks in count():
        logs = get_logs(client.context.token, 'Transfer', from_block=block_before - 1)
        if logs:
            break
        wait_for_blocks(1)
        assert waited_blocks < 10

    assert len([l for l in logs
                if is_same_address(l['args']['_to'], receiver_address) and
                l['args']['_value'] == 5]) == 1
    assert len([l for l in logs
                if is_same_address(l['args']['_to'], sender_address) and
                l['args']['_value'] == 5]) == 1
    wait_for_blocks(blockchain.n_confirmations)
    gevent.sleep(blockchain.poll_interval)
    assert channel_id not in channel_manager.channels

    # update channel state so that it will not be closed twice
    client.sync_channels()
    new_state = None
    for channel in client.channels:
        if all(channel.sender == confirmed_open_channel.sender,
               channel.receiver == confirmed_open_channel.receiver,
               channel.block == confirmed_open_channel.block):
            new_state = channel.state
    if new_state is None:
        confirmed_open_channel.state = confirmed_open_channel.State.closed
    else:
        confirmed_open_channel.state = new_state
Example #5
0
def test_challenge(
        channel_manager: ChannelManager,
        confirmed_open_channel: Channel,
        receiver_address: str,
        sender_address: str,
        wait_for_blocks,
        web3: Web3,
        client: Client
):
    blockchain = channel_manager.blockchain
    channel_id = (confirmed_open_channel.sender, confirmed_open_channel.block)
    sig = encode_hex(confirmed_open_channel.create_transfer(5))
    channel_manager.register_payment(sender_address, confirmed_open_channel.block, 5, sig)
    # hack channel to decrease balance
    confirmed_open_channel.update_balance(0)
    sig = confirmed_open_channel.create_transfer(3)
    block_before = web3.eth.blockNumber
    confirmed_open_channel.close()
    # should challenge and immediately settle
    for waited_blocks in count():
        logs = get_logs(client.context.token, 'Transfer', from_block=block_before - 1)
        if logs:
            break
        wait_for_blocks(1)
        assert waited_blocks < 10

    assert len([l for l in logs
                if is_same_address(l['args']['_to'], receiver_address) and
                l['args']['_value'] == 5]) == 1
    assert len([l for l in logs
                if is_same_address(l['args']['_to'], sender_address) and
                l['args']['_value'] == 5]) == 1
    wait_for_blocks(blockchain.n_confirmations)
    gevent.sleep(blockchain.poll_interval)
    assert channel_id not in channel_manager.channels

    # update channel state so that it will not be closed twice
    client.sync_channels()
    new_state = None
    for channel in client.channels:
        if all(channel.sender == confirmed_open_channel.sender,
               channel.receiver == confirmed_open_channel.receiver,
               channel.block == confirmed_open_channel.block):
            new_state = channel.state
    if new_state is None:
        confirmed_open_channel.state = confirmed_open_channel.State.closed
    else:
        confirmed_open_channel.state = new_state
def test_settlement(
        channel_manager: ChannelManager,
        confirmed_open_channel: Channel,
        receiver_address: str,
        wait_for_blocks,
        web3: Web3,
        token_contract: Contract,
        sender_address: str,
        use_tester: bool
):
    if not use_tester:
        pytest.skip('This test takes several hours on real blockchains.')

    blockchain = channel_manager.blockchain
    channel_id = (confirmed_open_channel.sender, confirmed_open_channel.block)

    sig = encode_hex(confirmed_open_channel.create_transfer(2))
    channel_manager.register_payment(sender_address, confirmed_open_channel.block, 2, sig)

    confirmed_open_channel.close()
    wait_for_blocks(blockchain.n_confirmations)
    gevent.sleep(blockchain.poll_interval)
    block_before = web3.eth.blockNumber
    channel_rec = channel_manager.channels[channel_id]
    wait_for_blocks(channel_rec.settle_timeout - block_before)
    confirmed_open_channel.settle()

    logs = get_logs(token_contract, 'Transfer', from_block=block_before - 1)
    assert len([l for l in logs
                if is_same_address(l['args']['_to'], receiver_address) and
                l['args']['_value'] == 2]) == 1
    assert len([l for l in logs
                if is_same_address(l['args']['_to'], sender_address) and
                l['args']['_value'] == 8]) == 1

    wait_for_blocks(blockchain.n_confirmations)
    gevent.sleep(blockchain.poll_interval)
    assert channel_id not in channel_manager.channels
Example #7
0
    def _update(self):
        current_block = self.web3.eth.blockNumber
        # reset unconfirmed channels in case of reorg
        if self.wait_sync_event.is_set():  # but not on first sync
            if current_block < self.cm.state.unconfirmed_head_number:
                self.log.info('chain reorganization detected. '
                              'Resyncing unconfirmed events (unconfirmed_head=%d) [@%d]' %
                              (self.cm.state.unconfirmed_head_number, self.web3.eth.blockNumber))
                self.cm.reset_unconfirmed()
            try:
                # raises if hash doesn't exist (i.e. block has been replaced)
                self.web3.eth.getBlock(self.cm.state.unconfirmed_head_hash)
            except ValueError:
                self.log.info('chain reorganization detected. '
                              'Resyncing unconfirmed events (unconfirmed_head=%d) [@%d]. '
                              '(getBlock() raised ValueError)' %
                              (self.cm.state.unconfirmed_head_number, current_block))
                self.cm.reset_unconfirmed()

            # in case of reorg longer than confirmation number fail
            try:
                self.web3.eth.getBlock(self.cm.state.confirmed_head_hash)
            except ValueError:
                self.log.critical('events considered confirmed have been reorganized')
                assert False  # unreachable as long as confirmation level is set high enough

        if self.cm.state.confirmed_head_number is None:
            self.cm.state.update_sync_state(confirmed_head_number=self.sync_start_block)
        if self.cm.state.unconfirmed_head_number is None:
            self.cm.state.update_sync_state(unconfirmed_head_number=self.sync_start_block)
        new_unconfirmed_head_number = self.cm.state.unconfirmed_head_number + self.sync_chunk_size
        new_unconfirmed_head_number = min(new_unconfirmed_head_number, current_block)
        new_confirmed_head_number = max(new_unconfirmed_head_number - self.n_confirmations, 0)

        # return if blocks have already been processed
        if (self.cm.state.confirmed_head_number >= new_confirmed_head_number and
                self.cm.state.unconfirmed_head_number >= new_unconfirmed_head_number):
            return

        # filter for events after block_number
        filters_confirmed = {
            'from_block': self.cm.state.confirmed_head_number + 1,
            'to_block': new_confirmed_head_number,
            'argument_filters': {
                '_receiver_address': self.cm.state.receiver
            }
        }
        filters_unconfirmed = {
            'from_block': self.cm.state.unconfirmed_head_number + 1,
            'to_block': new_unconfirmed_head_number,
            'argument_filters': {
                '_receiver_address': self.cm.state.receiver
            }
        }
        self.log.debug(
            'filtering for events u:%s-%s c:%s-%s @%d',
            filters_unconfirmed['from_block'],
            filters_unconfirmed['to_block'],
            filters_confirmed['from_block'],
            filters_confirmed['to_block'],
            current_block
        )

        # unconfirmed channel created
        logs = get_logs(
            self.channel_manager_contract,
            'ChannelCreated',
            **filters_unconfirmed
        )
        for log in logs:
            assert is_same_address(log['args']['_receiver_address'], self.cm.state.receiver)
            sender = log['args']['_sender_address']
            sender = to_checksum_address(sender)
            deposit = log['args']['_deposit']
            open_block_number = log['blockNumber']
            self.log.debug(
                'received unconfirmed ChannelCreated event (sender %s, block number %s)',
                sender,
                open_block_number
            )
            self.cm.unconfirmed_event_channel_opened(sender, open_block_number, deposit)

        # channel created
        logs = get_logs(
            self.channel_manager_contract,
            'ChannelCreated',
            **filters_confirmed
        )
        for log in logs:
            assert is_same_address(log['args']['_receiver_address'], self.cm.state.receiver)
            sender = log['args']['_sender_address']
            sender = to_checksum_address(sender)
            deposit = log['args']['_deposit']
            open_block_number = log['blockNumber']
            self.log.debug('received ChannelOpened event (sender %s, block number %s)',
                           sender, open_block_number)
            self.cm.event_channel_opened(sender, open_block_number, deposit)

        # unconfirmed channel top ups
        logs = get_logs(
            self.channel_manager_contract,
            'ChannelToppedUp',
            **filters_unconfirmed
        )
        for log in logs:
            assert is_same_address(log['args']['_receiver_address'], self.cm.state.receiver)
            txhash = log['transactionHash']
            sender = log['args']['_sender_address']
            sender = to_checksum_address(sender)
            open_block_number = log['args']['_open_block_number']
            added_deposit = log['args']['_added_deposit']
            self.log.debug(
                'received top up event (sender %s, block number %s, deposit %s)',
                sender,
                open_block_number,
                added_deposit
            )
            self.cm.unconfirmed_event_channel_topup(
                sender,
                open_block_number,
                txhash,
                added_deposit
            )

        # confirmed channel top ups
        logs = get_logs(
            self.channel_manager_contract,
            'ChannelToppedUp',
            **filters_confirmed
        )
        for log in logs:
            assert is_same_address(log['args']['_receiver_address'], self.cm.state.receiver)
            txhash = log['transactionHash']
            sender = log['args']['_sender_address']
            sender = to_checksum_address(sender)
            open_block_number = log['args']['_open_block_number']
            added_deposit = log['args']['_added_deposit']
            self.log.debug(
                'received top up event (sender %s, block number %s, added deposit %s)',
                sender,
                open_block_number,
                added_deposit
            )
            self.cm.event_channel_topup(sender, open_block_number, txhash, added_deposit)

        # channel settled event
        logs = get_logs(
            self.channel_manager_contract,
            'ChannelSettled',
            **filters_confirmed
        )
        for log in logs:
            assert is_same_address(log['args']['_receiver_address'], self.cm.state.receiver)
            sender = log['args']['_sender_address']
            sender = to_checksum_address(sender)
            open_block_number = log['args']['_open_block_number']
            self.log.debug('received ChannelSettled event (sender %s, block number %s)',
                           sender, open_block_number)
            self.cm.event_channel_settled(sender, open_block_number)

        # channel close requested
        logs = get_logs(
            self.channel_manager_contract,
            'ChannelCloseRequested',
            **filters_confirmed
        )
        for log in logs:
            assert is_same_address(log['args']['_receiver_address'], self.cm.state.receiver)
            sender = log['args']['_sender_address']
            sender = to_checksum_address(sender)
            open_block_number = log['args']['_open_block_number']
            if (sender, open_block_number) not in self.cm.channels:
                continue
            balance = log['args']['_balance']
            try:
                timeout = self.channel_manager_contract.call().getChannelInfo(
                    sender,
                    self.cm.state.receiver,
                    open_block_number
                )[2]
            except BadFunctionCallOutput:
                self.log.warning(
                    'received ChannelCloseRequested event for a channel that doesn\'t '
                    'exist or has been closed already (sender=%s open_block_number=%d)'
                    % (sender, open_block_number))
                self.cm.force_close_channel(sender, open_block_number)
                continue
            self.log.debug('received ChannelCloseRequested event (sender %s, block number %s)',
                           sender, open_block_number)
            try:
                self.cm.event_channel_close_requested(sender, open_block_number, balance, timeout)
            except InsufficientBalance:
                self.log.fatal('Insufficient ETH balance of the receiver. '
                               "Can't close the channel. "
                               'Will retry once the balance is sufficient')
                self.insufficient_balance = True

        # update head hash and number
        try:
            new_unconfirmed_head_hash = self.web3.eth.getBlock(new_unconfirmed_head_number).hash
        except AttributeError:
            self.log.info('chain reorganization detected. '
                            'Resyncing unconfirmed events (unconfirmed_head=%d) [@%d]. ' %
                            new_unconfirmed_head_number)
            self.cm.reset_unconfirmed()
            new_unconfirmed_head_number = self.cm.state.unconfirmed_head_number
        try:
            new_unconfirmed_head_hash = self.web3.eth.getBlock(new_unconfirmed_head_number).hash
            new_confirmed_head_hash = self.web3.eth.getBlock(new_confirmed_head_number).hash
        except AttributeError:
            self.log.critical("RPC endpoint didn't return proper info for an existing block "
                              "(%d,%d)" % (new_unconfirmed_head_number, new_confirmed_head_number))
            self.log.critical("It is possible that the blockchain isn't fully synced. "
                              "This often happens when Parity is run with --fast or --warp sync.")
            self.log.critical("Can't continue - check status of the ethereum node.")
            sys.exit(1)
        self.cm.set_head(
            new_unconfirmed_head_number,
            new_unconfirmed_head_hash,
            new_confirmed_head_number,
            new_confirmed_head_hash
        )
        if not self.wait_sync_event.is_set() and new_unconfirmed_head_number == current_block:
            self.log.info('Channel info (recever: {}) sync finished, continue listen ...'.format(self.cm.receiver))
            self.wait_sync_event.set()
Example #8
0
    def sync_channels(self):
        """
        Merges locally available channel information, including their current balance signatures,
        with channel information available on the blockchain to make up for local data loss.
        Naturally, balance signatures cannot be recovered from the blockchain.
        """
        filters = {'_sender_address': self.context.address}
        create = get_logs(self.context.channel_manager,
                          'ChannelCreated',
                          argument_filters=filters)
        topup = get_logs(self.context.channel_manager,
                         'ChannelToppedUp',
                         argument_filters=filters)
        close = get_logs(self.context.channel_manager,
                         'ChannelCloseRequested',
                         argument_filters=filters)
        settle = get_logs(self.context.channel_manager,
                          'ChannelSettled',
                          argument_filters=filters)

        channel_key_to_channel = {}

        def get_channel(event) -> Channel:
            sender = to_checksum_address(event['args']['_sender_address'])
            receiver = to_checksum_address(event['args']['_receiver_address'])
            block = event['args'].get('_open_block_number',
                                      event['blockNumber'])
            assert is_same_address(sender, self.context.address)
            return channel_key_to_channel.get((sender, receiver, block), None)

        for c in self.channels:
            channel_key_to_channel[(c.sender, c.receiver, c.block)] = c

        for e in create:
            c = get_channel(e)
            if c:
                c.deposit = e['args']['_deposit']
            else:
                c = Channel(
                    self.context,
                    to_checksum_address(e['args']['_sender_address']),
                    to_checksum_address(e['args']['_receiver_address']),
                    e['blockNumber'],
                    e['args']['_deposit'],
                    on_settle=lambda channel: self.channels.remove(channel))
                assert is_same_address(c.sender, self.context.address)
                channel_key_to_channel[(c.sender, c.receiver, c.block)] = c

        for e in topup:
            c = get_channel(e)
            c.deposit += e['args']['_added_deposit']

        for e in close:
            # Requested closed, not actual closed.
            c = get_channel(e)

            c.update_balance(e['args']['_balance'])
            c.state = Channel.State.settling

        for e in settle:
            c = get_channel(e)
            c.state = Channel.State.closed

        # Forget closed channels.
        self.channels = [
            c for c in channel_key_to_channel.values()
            if c.state != Channel.State.closed
        ]

        log.debug('Synced a total of {} channels.'.format(len(self.channels)))
Example #9
0
    def sync_channels(self):
        """
        Merges locally available channel information, including their current balance signatures,
        with channel information available on the blockchain to make up for local data loss.
        Naturally, balance signatures cannot be recovered from the blockchain.
        """
        filters = {'_sender_address': self.context.address}
        create = get_logs(
            self.context.channel_manager,
            'ChannelCreated',
            argument_filters=filters
        )
        topup = get_logs(
            self.context.channel_manager,
            'ChannelToppedUp',
            argument_filters=filters
        )
        close = get_logs(
            self.context.channel_manager,
            'ChannelCloseRequested',
            argument_filters=filters
        )
        settle = get_logs(
            self.context.channel_manager,
            'ChannelSettled',
            argument_filters=filters
        )

        channel_key_to_channel = {}

        def get_channel(event) -> Channel:
            sender = to_checksum_address(event['args']['_sender_address'])
            receiver = to_checksum_address(event['args']['_receiver_address'])
            block = event['args'].get('_open_block_number', event['blockNumber'])
            assert is_same_address(sender, self.context.address)
            return channel_key_to_channel.get((sender, receiver, block), None)

        for c in self.channels:
            channel_key_to_channel[(c.sender, c.receiver, c.block)] = c

        for e in create:
            c = get_channel(e)
            if c:
                c.deposit = e['args']['_deposit']
            else:
                c = Channel(
                    self.context,
                    to_checksum_address(e['args']['_sender_address']),
                    to_checksum_address(e['args']['_receiver_address']),
                    e['blockNumber'],
                    e['args']['_deposit'],
                    on_settle=lambda channel: self.channels.remove(channel)
                )
                assert is_same_address(c.sender, self.context.address)
                channel_key_to_channel[(c.sender, c.receiver, c.block)] = c

        for e in topup:
            c = get_channel(e)
            c.deposit += e['args']['_added_deposit']

        for e in close:
            # Requested closed, not actual closed.
            c = get_channel(e)

            c.update_balance(e['args']['_balance'])
            c.state = Channel.State.settling

        for e in settle:
            c = get_channel(e)
            c.state = Channel.State.closed

        # Forget closed channels.
        self.channels = [
            c for c in channel_key_to_channel.values() if c.state != Channel.State.closed
        ]

        log.debug('Synced a total of {} channels.'.format(len(self.channels)))
Example #10
0
    def _update(self):
        #print(bcolors.BOLD + 'monitor: block number %d' % (self.web3.eth.blockNumber) + bcolors.ENDC)
        current_block = self.web3.eth.blockNumber
        # reset unconfirmed channels in case of reorg
        if self.wait_sync_event.is_set():  # but not on first sync
            if current_block < self.cm.state.unconfirmed_head_number:
                self.log.info(
                    'chain reorganization detected. '
                    'Resyncing unconfirmed events (unconfirmed_head=%d) [@%d]'
                    % (self.cm.state.unconfirmed_head_number,
                       self.web3.eth.blockNumber))
                self.cm.reset_unconfirmed()
            try:
                # raises if hash doesn't exist (i.e. block has been replaced)
                self.web3.eth.getBlock(self.cm.state.unconfirmed_head_hash)
            except ValueError:
                self.log.info(
                    'chain reorganization detected. '
                    'Resyncing unconfirmed events (unconfirmed_head=%d) [@%d]. '
                    '(getBlock() raised ValueError)' %
                    (self.cm.state.unconfirmed_head_number, current_block))
                self.cm.reset_unconfirmed()

            # in case of reorg longer than confirmation number fail
            try:
                self.web3.eth.getBlock(self.cm.state.confirmed_head_hash)
            except ValueError:
                self.log.critical(
                    'events considered confirmed have been reorganized')
                assert False  # unreachable as long as confirmation level is set high enough

        if self.cm.state.confirmed_head_number is None:
            self.cm.state.update_sync_state(
                confirmed_head_number=self.sync_start_block)
        if self.cm.state.unconfirmed_head_number is None:
            self.cm.state.update_sync_state(
                unconfirmed_head_number=self.sync_start_block)

        new_unconfirmed_head_number = self.cm.state.unconfirmed_head_number + self.sync_chunk_size
        new_unconfirmed_head_number = min(new_unconfirmed_head_number,
                                          current_block)
        new_confirmed_head_number = max(
            new_unconfirmed_head_number - self.n_confirmations, 0)

        # return if blocks have already been processed
        if (self.cm.state.confirmed_head_number >= new_confirmed_head_number
                and self.cm.state.unconfirmed_head_number >=
                new_unconfirmed_head_number):
            return

        # Look for events in the state guardian contract with the customer
        # before the channel manager. If customer does something in between
        # monitor probably needs to handle close rather than responding to
        # customer channel.

        #for job in self.cm.jobs:
        for customer, sender, open_block_number in self.cm.jobs:

            # filter for events after block_number
            filters_confirmed = {
                'from_block': self.cm.state.confirmed_head_number + 1,
                'to_block': new_confirmed_head_number,
                'argument_filters': {
                    '_receiver_address': customer
                }
            }
            filters_unconfirmed = {
                'from_block': self.cm.state.unconfirmed_head_number + 1,
                'to_block': new_unconfirmed_head_number,
                'argument_filters': {
                    '_receiver_address': customer
                }
            }
            self.log.debug('filtering for channel events u:%s-%s c:%s-%s @%d',
                           filters_unconfirmed['from_block'],
                           filters_unconfirmed['to_block'],
                           filters_confirmed['from_block'],
                           filters_confirmed['to_block'], current_block)

            # channel close requested
            logs = get_logs(self.channel_manager_contract,
                            'ChannelCloseRequested', **filters_confirmed)
            for log in logs:
                self.log.debug(
                    'detected a channel close request in channel manager')
                #assert is_same_address(log['args']['_receiver_address'], self.cm.state.receiver)
                sender = log['args']['_sender_address']
                receiver = log['args']['_receiver_address']
                sender = to_checksum_address(sender)
                receiver = to_checksum_address(receiver)
                open_block_number = log['args']['_open_block_number']
                balance = log['args']['_balance']
                self.log.debug('sucessfully parsed the event information')
                self.log.debug('address of channel manager %s',
                               self.channel_manager_contract.address)
                try:
                    self.log.debug('params to get info: %s, %s, %d', sender,
                                   self.cm.state.receiver, open_block_number)
                    mtimeout, timeout = self.channel_manager_contract.call(
                    ).getChannelInfo(sender, self.cm.state.receiver,
                                     open_block_number)[2:4]
                except BadFunctionCallOutput:
                    self.log.debug(
                        'BadFunctionCallOutput error caught when trying to get channel info'
                    )
                    continue
                try:
                    self.cm.event_channel_close_requested(
                        sender, open_block_number, balance, timeout)
                except InsufficientBalance:
                    self.log.fatal('Insufficient ETH balance of the receiver. '
                                   "Can't close the channel. "
                                   'Will retry once the balance is sufficient')
                    self.insufficient_balance = True
                    # TODO: recover

            # channel settled event
            logs = get_logs(self.channel_manager_contract, 'ChannelSettled',
                            **filters_confirmed)
            for log in logs:
                #assert is_same_address(log['args']['_receiver_address'], self.cm.state.receiver)
                sender = log['args']['_sender_address']
                sender = to_checksum_address(sender)
                open_block_number = log['args']['_open_block_number']
                self.log.debug(
                    'received ChannelSettled event (sender %s, block number %s)',
                    sender, open_block_number)
                self.cm.event_channel_settled(sender, open_block_number)

            logs = get_logs(self.channel_monitor_contract, 'Dispute',
                            **filters_confirmed)

            for log in logs:
                customer = to_checksum_address(
                    log['args']['_customer_address'])
                sender = to_checksum_address(log['args']['_sender_address'])
                open_block_number = log['args']['_open_block_number']

                self.log.info(
                    'Customer triggered a dispute (customer %s, sender %s, open_block_number %d)\n',
                    customer, sender, open_block_number)

                self.cm.event_customer_dispute(customer, sender,
                                               open_block_number)

            logs = get_logs(self.channel_monitor_contract, 'Resolve',
                            **filters_unconfirmed)

            for log in logs:
                self.log.debug('\n\n saw resolve event happen \n\n')

            logs = get_logs(self.channel_monitor_contract, 'Withdraw',
                            **filters_unconfirmed)

            for log in logs:
                self.log.debug(
                    'detected successful WITHDRAW in monitor contract')

        # update head hash and number
        try:
            new_unconfirmed_head_hash = self.web3.eth.getBlock(
                new_unconfirmed_head_number).hash
            new_confirmed_head_hash = self.web3.eth.getBlock(
                new_confirmed_head_number).hash
        except AttributeError:
            self.log.critical(
                "RPC endpoint didn't return proper info for an existing block "
                "(%d,%d)" %
                (new_unconfirmed_head_number, new_confirmed_head_number))
            self.log.critical(
                "It is possible that the blockchain isn't fully synced. "
                "This often happens when Parity is run with --fast or --warp sync."
            )
            self.log.critical(
                "Can't continue - check status of the ethereum node.")
            sys.exit(1)
        self.cm.set_head(new_unconfirmed_head_number,
                         new_unconfirmed_head_hash, new_confirmed_head_number,
                         new_confirmed_head_hash)
        if not self.wait_sync_event.is_set(
        ) and new_unconfirmed_head_number == current_block:
            self.wait_sync_event.set()
Example #11
0
    def _update(self):
        #print(bcolors.BOLD + 'manager: block number %d' % (self.web3.eth.blockNumber) + bcolors.ENDC)
        current_block = self.web3.eth.blockNumber
        # reset unconfirmed channels in case of reorg
        if self.wait_sync_event.is_set():  # but not on first sync
            if current_block < self.cm.state.unconfirmed_head_number:
                self.log.info(
                    'chain reorganization detected. '
                    'Resyncing unconfirmed events (unconfirmed_head=%d) [@%d]'
                    % (self.cm.state.unconfirmed_head_number,
                       self.web3.eth.blockNumber))
                self.cm.reset_unconfirmed()
            try:
                # raises if hash doesn't exist (i.e. block has been replaced)
                self.web3.eth.getBlock(self.cm.state.unconfirmed_head_hash)
            except ValueError:
                self.log.info(
                    'chain reorganization detected. '
                    'Resyncing unconfirmed events (unconfirmed_head=%d) [@%d]. '
                    '(getBlock() raised ValueError)' %
                    (self.cm.state.unconfirmed_head_number, current_block))
                self.cm.reset_unconfirmed()

            # in case of reorg longer than confirmation number fail
            try:
                self.web3.eth.getBlock(self.cm.state.confirmed_head_hash)
            except ValueError:
                self.log.critical(
                    'events considered confirmed have been reorganized')
                assert False  # unreachable as long as confirmation level is set high enough

        if self.cm.state.confirmed_head_number is None:
            self.cm.state.update_sync_state(
                confirmed_head_number=self.sync_start_block)
        if self.cm.state.unconfirmed_head_number is None:
            self.cm.state.update_sync_state(
                unconfirmed_head_number=self.sync_start_block)
        new_unconfirmed_head_number = self.cm.state.unconfirmed_head_number + self.sync_chunk_size
        new_unconfirmed_head_number = min(new_unconfirmed_head_number,
                                          current_block)
        new_confirmed_head_number = max(
            new_unconfirmed_head_number - self.n_confirmations, 0)

        # return if blocks have already been processed
        if (self.cm.state.confirmed_head_number >= new_confirmed_head_number
                and self.cm.state.unconfirmed_head_number >=
                new_unconfirmed_head_number):
            return

        # filter for events after block_number
        filters_confirmed = {
            'from_block': self.cm.state.confirmed_head_number + 1,
            'to_block': new_confirmed_head_number,
            'argument_filters': {
                '_receiver_address': self.cm.state.receiver
            }
        }
        filters_unconfirmed = {
            'from_block': self.cm.state.unconfirmed_head_number + 1,
            'to_block': new_unconfirmed_head_number,
            'argument_filters': {
                '_receiver_address': self.cm.state.receiver
            }
        }
        self.log.debug('filtering for events u:%s-%s c:%s-%s @%d',
                       filters_unconfirmed['from_block'],
                       filters_unconfirmed['to_block'],
                       filters_confirmed['from_block'],
                       filters_confirmed['to_block'], current_block)

        # unconfirmed channel created
        logs = get_logs(self.channel_manager_contract, 'ChannelCreated',
                        **filters_unconfirmed)
        for log in logs:
            assert is_same_address(log['args']['_receiver_address'],
                                   self.cm.state.receiver)
            sender = log['args']['_sender_address']
            sender = to_checksum_address(sender)
            deposit = log['args']['_deposit']
            open_block_number = log['blockNumber']
            self.log.debug(
                'received unconfirmed ChannelCreated event (sender %s, block number %s)',
                sender, open_block_number)
            self.cm.unconfirmed_event_channel_opened(sender, open_block_number,
                                                     deposit)

        # channel created
        logs = get_logs(self.channel_manager_contract, 'ChannelCreated',
                        **filters_confirmed)
        for log in logs:
            assert is_same_address(log['args']['_receiver_address'],
                                   self.cm.state.receiver)
            sender = log['args']['_sender_address']
            sender = to_checksum_address(sender)
            deposit = log['args']['_deposit']
            open_block_number = log['blockNumber']
            self.log.debug(
                'received ChannelOpened event (sender %s, block number %s)',
                sender, open_block_number)
            self.cm.event_channel_opened(sender, open_block_number, deposit)

        # unconfirmed channel top ups
        logs = get_logs(self.channel_manager_contract, 'ChannelToppedUp',
                        **filters_unconfirmed)
        for log in logs:
            assert is_same_address(log['args']['_receiver_address'],
                                   self.cm.state.receiver)
            txhash = log['transactionHash']
            sender = log['args']['_sender_address']
            sender = to_checksum_address(sender)
            open_block_number = log['args']['_open_block_number']
            added_deposit = log['args']['_added_deposit']
            self.log.debug(
                'received top up event (sender %s, block number %s, deposit %s)',
                sender, open_block_number, added_deposit)
            self.cm.unconfirmed_event_channel_topup(sender, open_block_number,
                                                    txhash, added_deposit)

        # confirmed channel top ups
        logs = get_logs(self.channel_manager_contract, 'ChannelToppedUp',
                        **filters_confirmed)
        for log in logs:
            assert is_same_address(log['args']['_receiver_address'],
                                   self.cm.state.receiver)
            txhash = log['transactionHash']
            sender = log['args']['_sender_address']
            sender = to_checksum_address(sender)
            open_block_number = log['args']['_open_block_number']
            added_deposit = log['args']['_added_deposit']
            self.log.debug(
                'received top up event (sender %s, block number %s, added deposit %s)',
                sender, open_block_number, added_deposit)
            self.cm.event_channel_topup(sender, open_block_number, txhash,
                                        added_deposit)

        # channel settled event
        logs = get_logs(self.channel_manager_contract, 'ChannelSettled',
                        **filters_confirmed)
        for log in logs:
            assert is_same_address(log['args']['_receiver_address'],
                                   self.cm.state.receiver)
            sender = log['args']['_sender_address']
            sender = to_checksum_address(sender)
            open_block_number = log['args']['_open_block_number']
            self.log.debug(
                'received ChannelSettled event (sender %s, block number %s)',
                sender, open_block_number)
            self.cm.event_channel_settled(sender, open_block_number)

        # channel close requested
        logs = get_logs(self.channel_manager_contract, 'ChannelCloseRequested',
                        **filters_confirmed)
        for log in logs:
            assert is_same_address(log['args']['_receiver_address'],
                                   self.cm.state.receiver)
            sender = log['args']['_sender_address']
            sender = to_checksum_address(sender)
            open_block_number = log['args']['_open_block_number']
            if (sender, open_block_number) not in self.cm.channels:
                continue
            balance = log['args']['_balance']
            try:
                #self.log.info('params to get info: %s, %s, %d', sender, self.cm.state.receiver, open_block_number)
                mtimeout, timeout = self.channel_manager_contract.call(
                ).getChannelInfo(sender, self.cm.state.receiver,
                                 open_block_number)[2:4]

                print(bcolors.OKGREEN + 'Channel close request:' +
                      '\n\tsender %s' % sender +
                      '\n\tmonitor timeout %d' % mtimeout +
                      '\n\tsettle timeout %d' % timeout + bcolors.ENDC)

            except BadFunctionCallOutput:
                self.log.warning(
                    'received ChannelCloseRequested event for a channel that doesn\'t '
                    'exist or has been closed already (sender=%s open_block_number=%d)'
                    % (sender, open_block_number))
                self.cm.force_close_channel(sender, open_block_number)
                continue
            self.log.debug(
                'received ChannelCloseRequested event (sender %s, block number %s)',
                sender, open_block_number)
            self.log.info(
                'received ChannelCloseRequested event (blocknumber %s, monitor period %s, timeout %s)',
                self.cm.state.confirmed_head_number, mtimeout, timeout)

            self.wait_to_dispute[int(mtimeout)] = (sender, open_block_number,
                                                   balance, mtimeout, timeout)

            #try:
            #    self.cm.event_channel_close_requested(sender, open_block_number, balance, mtimeout, timeout)
            #except InsufficientBalance:
            #    self.log.fatal('Insufficient ETH balance of the receiver. '
            #                   "Can't close the channel. "
            #                   'Will retry once the balance is sufficient')
            #    self.insufficient_balance = True
            # TODO: recover

        logs = get_logs(self.channel_manager_contract, 'MonitorInterference',
                        **filters_unconfirmed)

        for log in logs:
            sender = log['args']['_sender_address']
            sender = to_checksum_address(sender)
            open_block_number = log['args']['open_block_number']
            if (sender, open_block_number) not in self.cm.channels:
                self.log.debug(
                    "monitor interfered in channel that wasn't outsourced (sender %s open_block_number %s)",
                    sender, open_block_number)

            self.cm.event_monitor_interference(sender, open_block_number)

        logs = get_logs(self.channel_monitor_contract, 'CustomerDeposit',
                        **filters_unconfirmed)

        for log in logs:
            sender = to_checksum_address(log['args']['_sender_address'])
            deposit = log['args']['_sender_deposit']

            if sender == self.cm.receiver:
                self.cm.event_deposit_unconfirmed(sender, deposit)

        logs = get_logs(self.channel_monitor_contract, 'CustomerDeposit',
                        **filters_confirmed)

        for log in logs:
            sender = to_checksum_address(log['args']['_sender_address'])
            deposit = log['args']['_sender_deposit']

            if sender == self.cm.receiver:
                self.cm.event_deposit(sender, deposit)

        logs = get_logs(self.channel_monitor_contract, 'RecourseResult',
                        **filters_unconfirmed)

        for log in logs:
            #            monitor_balance = log['args']['_monitor_balance']
            #            closing_balance = log['args']['_closing_balance']
            round_number = int(log['args']['_round_number'])
            receipt_hash = log['args']['_receipt_hash']
            cheated = log['args']['_cheated']

            self.log.info(
                'RECOURSE RESULT: detected unconfirmed recourse result (round_number %d, receipt hash %s), did he cheat? %r',
                round_number, receipt_hash, cheated)
            print(
                bcolors.OKBLUE, 'Recourse result detected',
                '\n\tCheated: %r' % cheated, '\n\tnew balance: %s' %
                int(self.web3.eth.getBalance(self.cm.receiver)), bcolors.ENDC)

        logs = get_logs(self.channel_manager_contract, 'ExtractHash',
                        **filters_unconfirmed)

        for log in logs:
            message_hash = log['args']['message_hash']
            state_hash = log['args']['state_hash']
            signer = to_checksum_address(log['args']['signer'])

            self.log.info('\n\tmessage_hash %s\n\tstate_hash %s\n\tsigner %s',
                          message_hash, state_hash, signer)

        logs = get_logs(self.channel_monitor_contract, 'Dispute',
                        **filters_unconfirmed)

        for log in logs:
            customer = log['args']['_customer_address']
            open_block_number = log['args']['_open_block_number']
            sender = log['args']['_sender_address']
            self.log.info(
                'DETECTED UNCONFIMED DISPUTE from monitor contract (customer %s, sender %s, open_block_number %d)',
                customer, sender, open_block_number)

        logs = get_logs(self.channel_monitor_contract, 'Dispute',
                        **filters_confirmed)

        for log in logs:
            customer = to_checksum_address(log['args']['_customer_address'])
            open_block_number = log['args']['_open_block_number']
            sender = to_checksum_address(log['args']['_sender_address'])
            settle_timeout = int(log['data'], 16)
            self.log.info(
                'DETECTED CONFIMED DISPUTE from monitor contract (customer %s, sender %s, open_block_number %d, settle timeout %d, current block %d)',
                customer, sender, open_block_number, settle_timeout,
                self.cm.blockchain.web3.eth.blockNumber)

            self.wait_to_resolve[settle_timeout + 1] = (customer, sender,
                                                        open_block_number)
            self.resolve_timeouts[sender,
                                  open_block_number] = settle_timeout + 1

        logs = get_logs(self.channel_monitor_contract, 'Resolve',
                        **filters_confirmed)

        for log in logs:
            customer = to_checksum_address(log['args']['_sender_address'])
            self.log.info('successfully resolved channel (customer %s)',
                          customer)
            print(bcolors.OKGREEN + 'Channel successfully resolved....' +
                  bcolors.ENDC)
            print(bcolors.OKGREEN + '\n\tServer balance: %d' %
                  (self.web3.eth.getBalance(self.cm.receiver)))

        logs = get_logs(self.channel_monitor_contract, 'Evidence',
                        **filters_confirmed)

        for log in logs:
            customer = to_checksum_address(log['args']['_customer_address'])
            sender = to_checksum_address(log['args']['_sender_address'])
            pre_image = log['args']['_pre_image']
            open_block_number = int(log['data'], 16)

            if (sender, open_block_number
                ) not in self.cm.channels or customer != self.cm.receiver:
                self.log.info('received SET STATE for unknown channel')
            else:
                self.log.info(
                    'Monitor called SET STATE (sender %s, open_block_number %d, pre_image %d)',
                    sender, open_block_number, pre_image)
                self.cm.event_set_state(customer, sender, open_block_number,
                                        pre_image)

        logs = get_logs(self.channel_manager_contract, 'DebugVerify',
                        **filters_unconfirmed)

        for log in logs:
            signer = to_checksum_address(log['args']['_sender'])
            self.log.info('Signer of customer evidence: %s', signer)

        logs = get_logs(self.channel_manager_contract, 'RevealHash',
                        **filters_unconfirmed)

        for log in logs:
            evidence = encode_hex(log['args']['_evidence'])
            resolved = encode_hex(log['args']['_resolved'])
            balance = int(log['args']['_balance'])
            self.log.info(
                bcolors.BOLD +
                "Reveal (balance %s, evidence %s, resolved %s)" + bcolors.ENDC,
                balance, evidence, resolved)

        # See which close requests are ready to be responded to
        # and process them normally.
        disputed = []
        for mtimeout in self.wait_to_dispute:
            if self.cm.state.confirmed_head_number > mtimeout:
                try:
                    #print(self.cm.monitor_channels)
                    sender, open_block_number = self.wait_to_dispute[mtimeout][
                        0], self.wait_to_dispute[mtimeout][1]
                    self.log.info(
                        'Processing close request at block %s, mtimeout %s',
                        self.cm.state.confirmed_head_number, mtimeout)
                    self.cm.reveal_monitor_submission(
                        *self.wait_to_dispute[mtimeout])
                    self.cm.event_channel_close_requested(
                        *self.wait_to_dispute[mtimeout])
                except InsufficientBalance:
                    self.log.fatal('Insufficient ETH balance of the receiver. '
                                   "Can't close the channel. "
                                   'Will retry once the balance is sufficient')
                    self.insufficient_balance = True
                    # TODO: recover
                disputed.append(mtimeout)

        for dispute in disputed:
            del self.wait_to_dispute[dispute]

        resolved = []
        for stimeout in self.wait_to_resolve:
            if self.cm.blockchain.web3.eth.blockNumber > stimeout:
                self.log.info(
                    'Processing DISPUTE request at block %s, stimeout %s',
                    self.cm.blockchain.web3.eth.blockNumber, stimeout)
                customer, sender, open_block_number = self.wait_to_resolve[
                    stimeout]
                self.cm.event_dispute(customer, sender, open_block_number)
                resolved.append(stimeout)

        for resolve in resolved:
            del self.wait_to_resolve[resolve]

        # update head hash and number
        try:
            new_unconfirmed_head_hash = self.web3.eth.getBlock(
                new_unconfirmed_head_number).hash
            new_confirmed_head_hash = self.web3.eth.getBlock(
                new_confirmed_head_number).hash
        except AttributeError:
            self.log.critical(
                "RPC endpoint didn't return proper info for an existing block "
                "(%d,%d)" %
                (new_unconfirmed_head_number, new_confirmed_head_number))
            self.log.critical(
                "It is possible that the blockchain isn't fully synced. "
                "This often happens when Parity is run with --fast or --warp sync."
            )
            self.log.critical(
                "Can't continue - check status of the ethereum node.")
            sys.exit(1)
        self.cm.set_head(new_unconfirmed_head_number,
                         new_unconfirmed_head_hash, new_confirmed_head_number,
                         new_confirmed_head_hash)
        if not self.wait_sync_event.is_set(
        ) and new_unconfirmed_head_number == current_block:
            self.wait_sync_event.set()
Example #12
0
    def _update(self):
        current_block = self.web3.eth.blockNumber
        # reset unconfirmed channels in case of reorg
        if self.wait_sync_event.is_set():  # but not on first sync
            if current_block < self.cm.state.unconfirmed_head_number:
                self.log.info('chain reorganization detected. '
                              'Resyncing unconfirmed events (unconfirmed_head=%d) [@%d]' %
                              (self.cm.state.unconfirmed_head_number, self.web3.eth.blockNumber))
                self.cm.reset_unconfirmed()
            try:
                # raises if hash doesn't exist (i.e. block has been replaced)
                self.web3.eth.getBlock(self.cm.state.unconfirmed_head_hash)
            except ValueError:
                self.log.info('chain reorganization detected. '
                              'Resyncing unconfirmed events (unconfirmed_head=%d) [@%d]. '
                              '(getBlock() raised ValueError)' %
                              (self.cm.state.unconfirmed_head_number, current_block))
                self.cm.reset_unconfirmed()

            # in case of reorg longer than confirmation number fail
            try:
                self.web3.eth.getBlock(self.cm.state.confirmed_head_hash)
            except ValueError:
                self.log.critical('events considered confirmed have been reorganized')
                assert False  # unreachable as long as confirmation level is set high enough

        if self.cm.state.confirmed_head_number is None:
            self.cm.state.update_sync_state(confirmed_head_number=self.sync_start_block)
        if self.cm.state.unconfirmed_head_number is None:
            self.cm.state.update_sync_state(unconfirmed_head_number=self.sync_start_block)
        new_unconfirmed_head_number = self.cm.state.unconfirmed_head_number + self.sync_chunk_size
        new_unconfirmed_head_number = min(new_unconfirmed_head_number, current_block)
        new_confirmed_head_number = max(new_unconfirmed_head_number - self.n_confirmations, 0)

        # return if blocks have already been processed
        if (self.cm.state.confirmed_head_number >= new_confirmed_head_number and
                self.cm.state.unconfirmed_head_number >= new_unconfirmed_head_number):
            return

        # filter for events after block_number
        filters_confirmed = {
            'from_block': self.cm.state.confirmed_head_number + 1,
            'to_block': new_confirmed_head_number,
            'argument_filters': {
                '_receiver_address': self.cm.state.receiver
            }
        }
        filters_unconfirmed = {
            'from_block': self.cm.state.unconfirmed_head_number + 1,
            'to_block': new_unconfirmed_head_number,
            'argument_filters': {
                '_receiver_address': self.cm.state.receiver
            }
        }
        self.log.debug(
            'filtering for events u:%s-%s c:%s-%s @%d',
            filters_unconfirmed['from_block'],
            filters_unconfirmed['to_block'],
            filters_confirmed['from_block'],
            filters_confirmed['to_block'],
            current_block
        )

        # unconfirmed channel created
        logs = get_logs(
            self.channel_manager_contract,
            'ChannelCreated',
            **filters_unconfirmed
        )
        for log in logs:
            assert is_same_address(log['args']['_receiver_address'], self.cm.state.receiver)
            sender = log['args']['_sender_address']
            sender = to_checksum_address(sender)
            deposit = log['args']['_deposit']
            open_block_number = log['blockNumber']
            self.log.debug(
                'received unconfirmed ChannelCreated event (sender %s, block number %s)',
                sender,
                open_block_number
            )
            self.cm.unconfirmed_event_channel_opened(sender, open_block_number, deposit)

        # channel created
        logs = get_logs(
            self.channel_manager_contract,
            'ChannelCreated',
            **filters_confirmed
        )
        for log in logs:
            assert is_same_address(log['args']['_receiver_address'], self.cm.state.receiver)
            sender = log['args']['_sender_address']
            sender = to_checksum_address(sender)
            deposit = log['args']['_deposit']
            open_block_number = log['blockNumber']
            self.log.debug('received ChannelOpened event (sender %s, block number %s)',
                           sender, open_block_number)
            self.cm.event_channel_opened(sender, open_block_number, deposit)

        # unconfirmed channel top ups
        logs = get_logs(
            self.channel_manager_contract,
            'ChannelToppedUp',
            **filters_unconfirmed
        )
        for log in logs:
            assert is_same_address(log['args']['_receiver_address'], self.cm.state.receiver)
            txhash = log['transactionHash']
            sender = log['args']['_sender_address']
            sender = to_checksum_address(sender)
            open_block_number = log['args']['_open_block_number']
            added_deposit = log['args']['_added_deposit']
            self.log.debug(
                'received top up event (sender %s, block number %s, deposit %s)',
                sender,
                open_block_number,
                added_deposit
            )
            self.cm.unconfirmed_event_channel_topup(
                sender,
                open_block_number,
                txhash,
                added_deposit
            )

        # confirmed channel top ups
        logs = get_logs(
            self.channel_manager_contract,
            'ChannelToppedUp',
            **filters_confirmed
        )
        for log in logs:
            assert is_same_address(log['args']['_receiver_address'], self.cm.state.receiver)
            txhash = log['transactionHash']
            sender = log['args']['_sender_address']
            sender = to_checksum_address(sender)
            open_block_number = log['args']['_open_block_number']
            added_deposit = log['args']['_added_deposit']
            self.log.debug(
                'received top up event (sender %s, block number %s, added deposit %s)',
                sender,
                open_block_number,
                added_deposit
            )
            self.cm.event_channel_topup(sender, open_block_number, txhash, added_deposit)

        # channel settled event
        logs = get_logs(
            self.channel_manager_contract,
            'ChannelSettled',
            **filters_confirmed
        )
        for log in logs:
            assert is_same_address(log['args']['_receiver_address'], self.cm.state.receiver)
            sender = log['args']['_sender_address']
            sender = to_checksum_address(sender)
            open_block_number = log['args']['_open_block_number']
            self.log.debug('received ChannelSettled event (sender %s, block number %s)',
                           sender, open_block_number)
            self.cm.event_channel_settled(sender, open_block_number)

        # channel close requested
        logs = get_logs(
            self.channel_manager_contract,
            'ChannelCloseRequested',
            **filters_confirmed
        )
        for log in logs:
            assert is_same_address(log['args']['_receiver_address'], self.cm.state.receiver)
            sender = log['args']['_sender_address']
            sender = to_checksum_address(sender)
            open_block_number = log['args']['_open_block_number']
            if (sender, open_block_number) not in self.cm.channels:
                continue
            balance = log['args']['_balance']
            try:
                timeout = self.channel_manager_contract.call().getChannelInfo(
                    sender,
                    self.cm.state.receiver,
                    open_block_number
                )[2]
            except BadFunctionCallOutput:
                self.log.warning(
                    'received ChannelCloseRequested event for a channel that doesn\'t '
                    'exist or has been closed already (sender=%s open_block_number=%d)'
                    % (sender, open_block_number))
                self.cm.force_close_channel(sender, open_block_number)
                continue
            self.log.debug('received ChannelCloseRequested event (sender %s, block number %s)',
                           sender, open_block_number)
            try:
                self.cm.event_channel_close_requested(sender, open_block_number, balance, timeout)
            except InsufficientBalance:
                self.log.fatal('Insufficient ETH balance of the receiver. '
                               "Can't close the channel. "
                               'Will retry once the balance is sufficient')
                self.insufficient_balance = True
                # TODO: recover

        # update head hash and number
        try:
            new_unconfirmed_head_hash = self.web3.eth.getBlock(new_unconfirmed_head_number).hash
            new_confirmed_head_hash = self.web3.eth.getBlock(new_confirmed_head_number).hash
        except AttributeError:
            self.log.critical("RPC endpoint didn't return proper info for an existing block "
                              "(%d,%d)" % (new_unconfirmed_head_number, new_confirmed_head_number))
            self.log.critical("It is possible that the blockchain isn't fully synced. "
                              "This often happens when Parity is run with --fast or --warp sync.")
            self.log.critical("Can't continue - check status of the ethereum node.")
            sys.exit(1)
        self.cm.set_head(
            new_unconfirmed_head_number,
            new_unconfirmed_head_hash,
            new_confirmed_head_number,
            new_confirmed_head_hash
        )
        if not self.wait_sync_event.is_set() and new_unconfirmed_head_number == current_block:
            self.wait_sync_event.set()