Beispiel #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
Beispiel #2
0
    def close_cooperatively(self, closing_sig: bytes):
        """
        Attempts to close the channel immediately by providing a hash of the channel's balance
        proof signed by the receiver. This signature must correspond to the balance proof stored in
        the passed channel state.
        """
        if self.state == Channel.State.closed:
            log.error('Channel must not be closed already to be closed cooperatively.')
            return None
        log.info('Attempting to cooperatively close channel to {} created at block #{}.'.format(
            self.receiver, self.block
        ))
        current_block = self.core.web3.eth.blockNumber
        receiver_recovered = verify_closing_sig(
            self.sender,
            self.block,
            self.balance,
            closing_sig,
            self.core.channel_manager.address
        )
        if not is_same_address(receiver_recovered, self.receiver):
            log.error('Invalid closing signature.')
            return None

        tx = create_signed_contract_transaction(
            self.core.private_key,
            self.core.channel_manager,
            'cooperativeClose',
            [
                self.receiver,
                self.block,
                self.balance,
                self.balance_sig,
                closing_sig
            ]
        )
        self.core.web3.eth.sendRawTransaction(tx)

        log.debug('Waiting for settle confirmation event...')
        event = get_event_blocking(
            self.core.channel_manager,
            'ChannelSettled',
            from_block=current_block + 1,
            argument_filters={
                '_sender_address': self.sender,
                '_receiver_address': self.receiver,
                '_open_block_number': self.block
            }
        )

        if event:
            log.debug('Successfully closed channel in block {}.'.format(event['blockNumber']))
            self.state = Channel.State.closed
            return event
        else:
            log.error('No event received.')
            return None
Beispiel #3
0
    def close_cooperatively(self, closing_sig: bytes):
        """
        Attempts to close the channel immediately by providing a hash of the channel's balance
        proof signed by the receiver. This signature must correspond to the balance proof stored in
        the passed channel state.
        """
        if self.state == Channel.State.closed:
            log.error('Channel must not be closed already to be closed cooperatively.')
            return None
        log.info('Attempting to cooperatively close channel to {} created at block #{}.'.format(
            self.receiver, self.block
        ))
        current_block = self.core.web3.eth.blockNumber
        receiver_recovered = verify_closing_sig(
            self.sender,
            self.block,
            self.balance,
            closing_sig,
            self.core.channel_manager.address
        )
        if not is_same_address(receiver_recovered, self.receiver):
            log.error('Invalid closing signature.')
            return None

        tx = create_signed_contract_transaction(
            self.core.private_key,
            self.core.channel_manager,
            'cooperativeClose',
            [
                self.receiver,
                self.block,
                self.balance,
                self.balance_sig,
                closing_sig
            ]
        )
        self.core.web3.eth.sendRawTransaction(tx)

        log.debug('Waiting for settle confirmation event...')
        event = get_event_blocking(
            self.core.channel_manager,
            'ChannelSettled',
            from_block=current_block + 1,
            argument_filters={
                '_sender_address': self.sender,
                '_receiver_address': self.receiver,
                '_open_block_number': self.block
            }
        )

        if event:
            log.debug('Successfully closed channel in block {}.'.format(event['blockNumber']))
            self.state = Channel.State.closed
            return event
        else:
            log.error('No event received.')
            return None
Beispiel #4
0
    def settle(self):
        """
        Attempts to settle a channel that has passed its settlement period. If a channel cannot be
        settled yet, the call is ignored with a warning. Blocks until a confirmation event is
        received or timeout.
        """
        if self.state != Channel.State.settling:
            log.error('Channel must be in the settlement period to settle.')
            return None
        log.info('Attempting to settle channel to {} created at block #{}.'.format(
            self.receiver, self.block
        ))

        _, _, settle_block, _, _ = self.core.channel_manager.call().getChannelInfo(
            self.sender, self.receiver, self.block
        )

        current_block = self.core.web3.eth.blockNumber
        wait_remaining = settle_block - current_block
        if wait_remaining > 0:
            log.warning('{} more blocks until this channel can be settled. Aborting.'.format(
                wait_remaining
            ))
            return None

        tx = create_signed_contract_transaction(
            self.core.private_key,
            self.core.channel_manager,
            'settle',
            [
                self.receiver,
                self.block
            ]
        )
        self.core.web3.eth.sendRawTransaction(tx)

        log.debug('Waiting for settle confirmation event...')
        event = get_event_blocking(
            self.core.channel_manager,
            'ChannelSettled',
            from_block=current_block + 1,
            argument_filters={
                '_sender_address': self.sender,
                '_receiver_address': self.receiver,
                '_open_block_number': self.block
            }
        )

        if event:
            log.debug('Successfully settled channel in block {}.'.format(event['blockNumber']))
            self.state = Channel.State.closed
            self.on_settle(self)
            return event
        else:
            log.error('No event received.')
            return None
Beispiel #5
0
    def settle(self):
        """
        Attempts to settle a channel that has passed its settlement period. If a channel cannot be
        settled yet, the call is ignored with a warning. Blocks until a confirmation event is
        received or timeout.
        """
        if self.state != Channel.State.settling:
            log.error('Channel must be in the settlement period to settle.')
            return None
        log.info('Attempting to settle channel to {} created at block #{}.'.format(
            self.receiver, self.block
        ))

        _, _, settle_block, _, _ = self.core.channel_manager.call().getChannelInfo(
            self.sender, self.receiver, self.block
        )

        current_block = self.core.web3.eth.blockNumber
        wait_remaining = settle_block - current_block
        if wait_remaining > 0:
            log.warning('{} more blocks until this channel can be settled. Aborting.'.format(
                wait_remaining
            ))
            return None

        tx = create_signed_contract_transaction(
            self.core.private_key,
            self.core.channel_manager,
            'settle',
            [
                self.receiver,
                self.block
            ]
        )
        self.core.web3.eth.sendRawTransaction(tx)

        log.debug('Waiting for settle confirmation event...')
        event = get_event_blocking(
            self.core.channel_manager,
            'ChannelSettled',
            from_block=current_block + 1,
            argument_filters={
                '_sender_address': self.sender,
                '_receiver_address': self.receiver,
                '_open_block_number': self.block
            }
        )

        if event:
            log.debug('Successfully settled channel in block {}.'.format(event['blockNumber']))
            self.state = Channel.State.closed
            self.on_settle(self)
            return event
        else:
            log.error('No event received.')
            return None
Beispiel #6
0
    def topup(self, deposit):
        """
        Attempts to increase the deposit in an existing channel. Block until confirmation.
        """
        if self.state != Channel.State.open:
            log.error('Channel must be open to be topped up.')
            return

        _balance = self.core.web3.eth.getBalance(self.core.address)
        if _balance < deposit:
            log.error(
                'Insufficient tokens available for the specified topup ({}/{})'
                .format(_balance, deposit)
            )
            return None

        log.info('Topping up channel to {} created at block #{} by {} tokens.'.format(
            self.receiver, self.block, deposit
        ))
        current_block = self.core.web3.eth.blockNumber

        data = (decode_hex(self.sender) +
                decode_hex(self.receiver) +
                self.block.to_bytes(4, byteorder='big'))
        tx = create_signed_contract_transaction(
            self.core.private_key,
            self.core.channel_manager,
            'topUp',
            [
                self.receiver,
                self.block
            ],
            deposit
        )
        self.core.web3.eth.sendRawTransaction(tx)

        log.debug('Waiting for topup confirmation event...')
        event = get_event_blocking(
            self.core.channel_manager,
            'ChannelToppedUp',
            from_block=current_block + 1,
            argument_filters={
                '_sender_address': self.sender,
                '_receiver_address': self.receiver,
                '_open_block_number': self.block
            }
        )

        if event:
            log.debug('Successfully topped up channel in block {}.'.format(event['blockNumber']))
            self.deposit += deposit
            return event
        else:
            log.error('No event received.')
            return None
Beispiel #7
0
    def topup(self, deposit):
        """
        Attempts to increase the deposit in an existing channel. Block until confirmation.
        """
        if self.state != Channel.State.open:
            log.error('Channel must be open to be topped up.')
            return

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

        log.info('Topping up channel to {} created at block #{} by {} tokens.'.format(
            self.receiver, self.block, deposit
        ))
        current_block = self.core.web3.eth.blockNumber

        data = (decode_hex(self.sender) +
                decode_hex(self.receiver) +
                self.block.to_bytes(4, byteorder='big'))
        tx = create_signed_contract_transaction(
            self.core.private_key,
            self.core.token,
            'transfer',
            [
                self.core.channel_manager.address,
                deposit,
                data
            ]
        )
        self.core.web3.eth.sendRawTransaction(tx)

        log.debug('Waiting for topup confirmation event...')
        event = get_event_blocking(
            self.core.channel_manager,
            'ChannelToppedUp',
            from_block=current_block + 1,
            argument_filters={
                '_sender_address': self.sender,
                '_receiver_address': self.receiver,
                '_open_block_number': self.block
            }
        )

        if event:
            log.debug('Successfully topped up channel in block {}.'.format(event['blockNumber']))
            self.deposit += deposit
            return event
        else:
            log.error('No event received.')
            return None
Beispiel #8
0
    def close(self, balance=None):
        """
        Attempts to request close on a channel. An explicit balance can be given to override the
        locally stored balance signature. Blocks until a confirmation event is received or timeout.
        """
        if self.state != Channel.State.open:
            log.error('Channel must be open to request a close.')
            return
        log.info('Requesting close of channel to {} created at block #{}.'.format(
            self.receiver, self.block
        ))
        current_block = self.core.web3.eth.blockNumber

        if balance is not None:
            self.update_balance(balance)

        tx = create_signed_contract_transaction(
            self.core.private_key,
            self.core.channel_manager,
            'uncooperativeClose',
            [
                self.receiver,
                self.block,
                self.balance
            ]
        )
        self.core.web3.eth.sendRawTransaction(tx)

        log.debug('Waiting for close confirmation event...')
        event = get_event_blocking(
            self.core.channel_manager,
            'ChannelCloseRequested',
            from_block=current_block + 1,
            argument_filters={
                '_sender_address': self.sender,
                '_receiver_address': self.receiver,
                '_open_block_number': self.block
            }
        )

        if event:
            log.debug('Successfully sent channel close request in block {}.'.format(
                event['blockNumber']
            ))
            self.state = Channel.State.settling
            return event
        else:
            log.error('No event received.')
            return None
Beispiel #9
0
    def close(self, balance=None):
        """
        Attempts to request close on a channel. An explicit balance can be given to override the
        locally stored balance signature. Blocks until a confirmation event is received or timeout.
        """
        if self.state != Channel.State.open:
            log.error('Channel must be open to request a close.')
            return
        log.info('Requesting close of channel to {} created at block #{}.'.format(
            self.receiver, self.block
        ))
        current_block = self.core.web3.eth.blockNumber

        if balance is not None:
            self.update_balance(balance)

        tx = create_signed_contract_transaction(
            self.core.private_key,
            self.core.channel_manager,
            'uncooperativeClose',
            [
                self.receiver,
                self.block,
                self.balance
            ]
        )
        self.core.web3.eth.sendRawTransaction(tx)

        log.debug('Waiting for close confirmation event...')
        event = get_event_blocking(
            self.core.channel_manager,
            'ChannelCloseRequested',
            from_block=current_block + 1,
            argument_filters={
                '_sender_address': self.sender,
                '_receiver_address': self.receiver,
                '_open_block_number': self.block
            }
        )

        if event:
            log.debug('Successfully sent channel close request in block {}.'.format(
                event['blockNumber']
            ))
            self.state = Channel.State.settling
            return event
        else:
            log.error('No event received.')
            return None
Beispiel #10
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

        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
Beispiel #11
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