Esempio n. 1
0
    def open_channel(self, receiver_address: str, deposit: int):
        """
        Attempts to open a new channel to the receiver with the given deposit. Blocks until the
        creation transaction is found in a pending block or timeout is reached. The new channel
        state is returned.
        """
        assert isinstance(receiver_address, str)
        assert isinstance(deposit, int)
        assert deposit > 0

        balance = self.context.web3.eth.getBalance(self.context.address)
        #  token_balance = self.context.token.call().balanceOf(self.context.address)
        if balance < deposit:
            log.error(
                'Insufficient balance available for the specified deposit ({}/{})'
                .format(balance, deposit))
            return None

        current_block = self.context.web3.eth.blockNumber
        log.info(
            'Creating channel to {} with an initial deposit of {} @{}'.format(
                receiver_address, deposit, current_block))

        tx = create_signed_contract_transaction(self.context.private_key,
                                                self.context.channel_manager,
                                                'createChannel',
                                                [receiver_address], deposit)
        ret = self.context.web3.eth.sendRawTransaction(tx)
        log.warning(str(ret))

        log.debug('Waiting for channel creation event on the blockchain...')
        filters = {
            '_sender_address': self.context.address,
            '_receiver_address': receiver_address
        }
        event = get_event_blocking(self.context.channel_manager,
                                   'ChannelCreated',
                                   from_block=current_block + 1,
                                   to_block='latest',
                                   argument_filters=filters)

        if event:
            log.debug('Event received. Channel created in block {}.'.format(
                event['blockNumber']))
            assert is_same_address(event['args']['_sender_address'],
                                   self.context.address)
            assert is_same_address(event['args']['_receiver_address'],
                                   receiver_address)
            channel = Channel(self.context,
                              self.context.address,
                              receiver_address,
                              event['blockNumber'],
                              event['args']['_deposit'],
                              on_settle=lambda c: self.channels.remove(c))
            self.channels.append(channel)
        else:
            log.error('Error: No event received.')
            channel = None

        return channel
Esempio n. 2
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)))
Esempio n. 3
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)))
Esempio n. 4
0
    def open_channel(self, receiver_address: str,
                     deposit: int) -> Optional[Channel]:
        """Open a channel with a receiver and deposit

        Attempts to open a new channel to the receiver with the given deposit. Blocks until the
        creation transaction is found in a pending block or timeout is reached.

        Args:
            receiver_address: the partner with whom the channel should be opened
            deposit:  the initial deposit for the channel (Should be > 0)

        Returns:
            The opened channel, if successful. Otherwise `None`

        """

        assert isinstance(receiver_address, str)
        assert isinstance(deposit, int)
        assert deposit > 0

        token_balance = self.context.token.call().balanceOf(
            self.context.address)
        if token_balance < deposit:
            log.error(
                'Insufficient tokens available for the specified deposit ({}/{})'
                .format(token_balance, deposit))
            return None

        current_block = self.context.web3.eth.blockNumber
        log.info(
            'Creating channel to {} with an initial deposit of {} @{}'.format(
                receiver_address, deposit, current_block))

        data = decode_hex(self.context.address) + decode_hex(receiver_address)
        tx = create_signed_contract_transaction(
            self.context.private_key, self.context.token, 'transfer',
            [self.context.channel_manager.address, deposit, data])
        self.context.web3.eth.sendRawTransaction(tx)

        log.debug('Waiting for channel creation event on the blockchain...')
        filters = {
            '_sender_address': self.context.address,
            '_receiver_address': receiver_address
        }
        event = get_event_blocking(self.context.channel_manager,
                                   'ChannelCreated',
                                   from_block=current_block + 1,
                                   to_block='latest',
                                   argument_filters=filters)

        if event:
            log.debug('Event received. Channel created in block {}.'.format(
                event['blockNumber']))
            assert is_same_address(event['args']['_sender_address'],
                                   self.context.address)
            assert is_same_address(event['args']['_receiver_address'],
                                   receiver_address)
            channel = Channel(self.context,
                              self.context.address,
                              receiver_address,
                              event['blockNumber'],
                              event['args']['_deposit'],
                              on_settle=lambda c: self.channels.remove(c))
            self.channels.append(channel)
        else:
            log.error('Error: No event received.')
            channel = None

        return channel
Esempio n. 5
0
    def sync_channels(self):
        filter_kwargs = {'fromBlock': 0, 'toBlock': 'latest'}
        filters = {'_sender_address': self.context.address}
        create = self.context.channel_manager.events.ChannelCreated.createFilter(
            argument_filters=filters, **filter_kwargs).get_all_entries()

        topup = self.context.channel_manager.events.ChannelToppedUp.createFilter(
            argument_filters=filters, **filter_kwargs).get_all_entries()

        close = self.context.channel_manager.events.ChannelCloseRequested.createFilter(
            argument_filters=filters, **filter_kwargs).get_all_entries()

        settle = self.context.channel_manager.events.ChannelSettled.createFilter(
            argument_filters=filters, **filter_kwargs).get_all_entries()

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