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