def settle(self): if log.isEnabledFor(logging.INFO): log.info( 'settle called', node=pex(self.node_address), ) transaction_hash = estimate_and_transact( self.proxy, 'settle', ) self.client.poll(unhexlify(transaction_hash), timeout=self.poll_timeout) receipt_or_none = check_transaction_threw(self.client, transaction_hash) if receipt_or_none: log.info( 'settle failed', node=pex(self.node_address), contract=pex(self.address), ) self._check_exists() raise TransactionThrew('Settle', receipt_or_none) if log.isEnabledFor(logging.INFO): log.info( 'settle sucessfull', node=pex(self.node_address), contract=pex(self.address), )
def add_token(self, token_address): if not isaddress(token_address): raise ValueError('token_address must be a valid address') transaction_hash = estimate_and_transact( self.proxy, 'addToken', self.startgas, self.gasprice, token_address, ) self.client.poll(unhexlify(transaction_hash), timeout=self.poll_timeout) receipt_or_none = check_transaction_threw(self.client, transaction_hash) if receipt_or_none: raise TransactionThrew('AddToken', receipt_or_none) manager_address = self.manager_address_by_token(token_address) if manager_address is None: log.error('Transaction failed and check_transaction_threw didnt detect it') raise RuntimeError('channelManagerByToken failed') if log.isEnabledFor(logging.INFO): log.info( 'add_token called', token_address=pex(token_address), registry_address=pex(self.address), manager_address=pex(manager_address), ) return manager_address
def add_token(self, token_address): if not isaddress(token_address): raise ValueError('token_address must be a valid address') transaction_hash = estimate_and_transact( self.proxy.addToken, self.startgas, self.gasprice, token_address, ) self.client.poll(transaction_hash.decode('hex'), timeout=self.poll_timeout) receipt_or_none = check_transaction_threw(self.client, transaction_hash) if receipt_or_none: raise TransactionThrew('AddToken', receipt_or_none) manager_address = self.manager_address_by_token(token_address) if manager_address is None: log.error( 'Transaction failed and check_transaction_threw didnt detect it' ) raise RuntimeError('channelManagerByToken failed') if log.isEnabledFor(logging.INFO): log.info( 'add_token called', token_address=pex(token_address), registry_address=pex(self.address), manager_address=pex(manager_address), ) return manager_address
def new_netting_channel(self, other_peer, settle_timeout): if not isaddress(other_peer): raise ValueError('The other_peer must be a valid address') invalid_timeout = (settle_timeout < NETTINGCHANNEL_SETTLE_TIMEOUT_MIN or settle_timeout > NETTINGCHANNEL_SETTLE_TIMEOUT_MAX) if invalid_timeout: raise ValueError('settle_timeout must be in range [{}, {}]'.format( NETTINGCHANNEL_SETTLE_TIMEOUT_MIN, NETTINGCHANNEL_SETTLE_TIMEOUT_MAX)) local_address = privatekey_to_address(self.client.privkey) if local_address == other_peer: raise SamePeerAddress( 'The other peer must not have the same address as the client.') transaction_hash = estimate_and_transact( self.proxy, 'newChannel', self.startgas, self.gasprice, other_peer, settle_timeout, ) self.client.poll(unhexlify(transaction_hash), timeout=self.poll_timeout) if check_transaction_threw(self.client, transaction_hash): raise DuplicatedChannelError('Duplicated channel') netting_channel_results_encoded = self.proxy.call( 'getChannelWith', other_peer, startgas=self.startgas, ) # address is at index 0 netting_channel_address_encoded = netting_channel_results_encoded if not netting_channel_address_encoded: log.error('netting_channel_address failed', peer1=pex(local_address), peer2=pex(other_peer)) raise RuntimeError('netting_channel_address failed') netting_channel_address_bin = address_decoder( netting_channel_address_encoded) if log.isEnabledFor(logging.INFO): log.info( 'new_netting_channel called', peer1=pex(local_address), peer2=pex(other_peer), netting_channel=pex(netting_channel_address_bin), ) return netting_channel_address_bin
def close(self, nonce, transferred_amount, locksroot, extra_hash, signature): """ Close the channel using the provided balance proof. Raises: AddressWithoutCode: If the channel was settled prior to the call. """ if log.isEnabledFor(logging.INFO): log.info( 'close called', node=pex(self.node_address), contract=pex(self.address), nonce=nonce, transferred_amount=transferred_amount, locksroot=encode_hex(locksroot), extra_hash=encode_hex(extra_hash), signature=encode_hex(signature), ) transaction_hash = estimate_and_transact( self.proxy, 'close', nonce, transferred_amount, locksroot, extra_hash, signature, ) self.client.poll(unhexlify(transaction_hash), timeout=self.poll_timeout) receipt_or_none = check_transaction_threw(self.client, transaction_hash) if receipt_or_none: log.critical( 'close failed', node=pex(self.node_address), contract=pex(self.address), nonce=nonce, transferred_amount=transferred_amount, locksroot=encode_hex(locksroot), extra_hash=encode_hex(extra_hash), signature=encode_hex(signature), ) self._check_exists() raise TransactionThrew('Close', receipt_or_none) if log.isEnabledFor(logging.INFO): log.info( 'close sucessfull', node=pex(self.node_address), contract=pex(self.address), nonce=nonce, transferred_amount=transferred_amount, locksroot=encode_hex(locksroot), extra_hash=encode_hex(extra_hash), signature=encode_hex(signature), )
def deposit(self, amount): """ Deposit amount token in the channel. Raises: AddressWithoutCode: If the channel was settled prior to the call. RuntimeError: If the netting channel token address is empty. """ if not isinstance(amount, (int, long)): raise ValueError('amount needs to be an integral number.') token_address = self.token_address() token = Token( self.client, token_address, self.startgas, self.gasprice, self.poll_timeout, ) current_balance = token.balance_of(self.node_address) if current_balance < amount: raise ValueError( 'deposit [{}] cant be larger than the available balance [{}].'. format( amount, current_balance, )) if log.isEnabledFor(logging.INFO): log.info('deposit called', contract=pex(self.address), amount=amount) transaction_hash = estimate_and_transact( self.proxy, 'deposit', self.startgas, self.gasprice, amount, ) self.client.poll( unhexlify(transaction_hash), timeout=self.poll_timeout, ) receipt_or_none = check_transaction_threw(self.client, transaction_hash) if receipt_or_none: log.critical('deposit failed', contract=pex(self.address)) self._check_exists() raise TransactionThrew('Deposit', receipt_or_none) if log.isEnabledFor(logging.INFO): log.info('deposit sucessfull', contract=pex(self.address), amount=amount)
def update_transfer(self, nonce, transferred_amount, locksroot, extra_hash, signature): if signature: if log.isEnabledFor(logging.INFO): log.info( 'updateTransfer called', node=pex(self.node_address), contract=pex(self.address), nonce=nonce, transferred_amount=transferred_amount, locksroot=encode_hex(locksroot), extra_hash=encode_hex(extra_hash), signature=encode_hex(signature), ) transaction_hash = estimate_and_transact( self.proxy, 'updateTransfer', nonce, transferred_amount, locksroot, extra_hash, signature, ) self.client.poll( unhexlify(transaction_hash), timeout=self.poll_timeout, ) receipt_or_none = check_transaction_threw(self.client, transaction_hash) if receipt_or_none: log.critical( 'updateTransfer failed', node=pex(self.node_address), contract=pex(self.address), nonce=nonce, transferred_amount=transferred_amount, locksroot=encode_hex(locksroot), extra_hash=encode_hex(extra_hash), signature=encode_hex(signature), ) self._check_exists() raise TransactionThrew('Update Transfer', receipt_or_none) if log.isEnabledFor(logging.INFO): log.info( 'updateTransfer sucessfull', node=pex(self.node_address), contract=pex(self.address), nonce=nonce, transferred_amount=transferred_amount, locksroot=encode_hex(locksroot), extra_hash=encode_hex(extra_hash), signature=encode_hex(signature), )
def approve(self, contract_address, allowance): """ Aprove `contract_address` to transfer up to `deposit` amount of token. """ # TODO: check that `contract_address` is a netting channel and that # `self.address` is one of the participants (maybe add this logic into # `NettingChannel` and keep this straight forward) transaction_hash = estimate_and_transact( self.proxy, 'approve', self.startgas, self.gasprice, contract_address, allowance, ) self.client.poll(unhexlify(transaction_hash), timeout=self.poll_timeout) receipt_or_none = check_transaction_threw(self.client, transaction_hash) if receipt_or_none: user_balance = self.balance_of(self.client.sender) # If the balance is zero, either the smart contract doesnt have a # balanceOf function or the actual balance is zero if user_balance == 0: msg = ( "Approve failed. \n" "Your account balance is 0 (zero), either the smart " "contract is not a valid ERC20 token or you don't have funds " "to use for openning a channel. " ) raise TransactionThrew(msg, receipt_or_none) # The approve call failed, check the user has enough balance # (assuming the token smart contract may check for the maximum # allowance, which is not necessarily the case) elif user_balance < allowance: msg = ( 'Approve failed. \n' 'Your account balance is {}, nevertheless the call to ' 'approve failed. Please make sure the corresponding smart ' 'contract is a valid ERC20 token.' ).format(user_balance) raise TransactionThrew(msg, receipt_or_none) # If the user has enough balance, warn the user the smart contract # may not have the approve function. else: msg = ( 'Approve failed. \n' 'Your account balance is {}, the request allowance is {}. ' 'The smart contract may be rejecting your request for the ' 'lack of balance.' ).format(user_balance, allowance) raise TransactionThrew(msg, receipt_or_none)
def add_token(self, token_address): if not isaddress(token_address): raise ValueError('token_address must be a valid address') log.info( 'add_token called', node=pex(self.node_address), token_address=pex(token_address), registry_address=pex(self.address), ) transaction_hash = estimate_and_transact( self.proxy, 'addToken', self.address, token_address, ) self.client.poll(unhexlify(transaction_hash), timeout=self.poll_timeout) receipt_or_none = check_transaction_threw(self.client, transaction_hash) if receipt_or_none: log.info( 'add_token failed', node=pex(self.node_address), token_address=pex(token_address), registry_address=pex(self.address), ) raise TransactionThrew('AddToken', receipt_or_none) manager_address = self.manager_address_by_token(token_address) if manager_address is None: log.info( 'add_token failed and check_transaction_threw didnt detect it', node=pex(self.node_address), token_address=pex(token_address), registry_address=pex(self.address), ) raise RuntimeError('channelManagerByToken failed') log.info( 'add_token sucessful', node=pex(self.node_address), token_address=pex(token_address), registry_address=pex(self.address), manager_address=pex(manager_address), ) return manager_address
def transfer(self, to_address, amount): transaction_hash = estimate_and_transact( self.proxy.transfer, # pylint: disable=no-member self.startgas, self.gasprice, to_address, amount, ) self.client.poll(unhexlify(transaction_hash)) receipt_or_none = check_transaction_threw(self.client, transaction_hash) if receipt_or_none: raise TransactionThrew('Transfer', receipt_or_none)
def transfer(self, to_address, amount): transaction_hash = estimate_and_transact( self.proxy, 'transfer', to_address, amount, ) self.client.poll(unhexlify(transaction_hash)) receipt_or_none = check_transaction_threw(self.client, transaction_hash) if receipt_or_none: raise TransactionThrew('Transfer', receipt_or_none)
def transfer(self, to_address, amount): transaction_hash = estimate_and_transact( self.proxy, 'transfer', self.startgas, self.gasprice, to_address, amount, ) self.client.poll(unhexlify(transaction_hash)) receipt_or_none = check_transaction_threw(self.client, transaction_hash) if receipt_or_none: raise TransactionThrew('Transfer', receipt_or_none)
def withdraw(self, unlock_proofs): # force a list to get the length (could be a generator) unlock_proofs = list(unlock_proofs) if log.isEnabledFor(logging.INFO): log.info( 'withdraw called', node=pex(self.node_address), contract=pex(self.address), ) failed = False for merkle_proof, locked_encoded, secret in unlock_proofs: if isinstance(locked_encoded, messages.Lock): raise ValueError('unlock must be called with a lock encoded `.as_bytes`') merkleproof_encoded = ''.join(merkle_proof) transaction_hash = estimate_and_transact( self.proxy, 'withdraw', locked_encoded, merkleproof_encoded, secret, ) self.client.poll(unhexlify(transaction_hash), timeout=self.poll_timeout) receipt_or_none = check_transaction_threw(self.client, transaction_hash) lock = messages.Lock.from_bytes(locked_encoded) if receipt_or_none: lock = messages.Lock.from_bytes(locked_encoded) log.critical( 'withdraw failed', node=pex(self.node_address), contract=pex(self.address), lock=lock, ) self._check_exists() failed = True elif log.isEnabledFor(logging.INFO): log.info( 'withdraw sucessfull', node=pex(self.node_address), contract=pex(self.address), lock=lock, ) if failed: raise TransactionThrew('Withdraw', receipt_or_none)
def approve(self, contract_address, allowance): """ Aprove `contract_address` to transfer up to `deposit` amount of token. """ # TODO: check that `contract_address` is a netting channel and that # `self.address` is one of the participants (maybe add this logic into # `NettingChannel` and keep this straight forward) transaction_hash = estimate_and_transact( self.proxy.approve, self.startgas, self.gasprice, contract_address, allowance, ) self.client.poll(transaction_hash.decode('hex'), timeout=self.poll_timeout) receipt_or_none = check_transaction_threw(self.client, transaction_hash) if receipt_or_none: raise TransactionThrew('Approve', receipt_or_none)
def withdraw(self, unlock_proof): if log.isEnabledFor(logging.INFO): log.info( 'withdraw called', node=pex(self.node_address), contract=pex(self.address), ) if isinstance(unlock_proof.lock_encoded, messages.Lock): raise ValueError( 'unlock must be called with a lock encoded `.as_bytes`') merkleproof_encoded = ''.join(unlock_proof.merkle_proof) transaction_hash = estimate_and_transact( self.proxy, 'withdraw', unlock_proof.lock_encoded, merkleproof_encoded, unlock_proof.secret, ) self.client.poll(unhexlify(transaction_hash), timeout=self.poll_timeout) receipt_or_none = check_transaction_threw(self.client, transaction_hash) if receipt_or_none: log.critical( 'withdraw failed', node=pex(self.node_address), contract=pex(self.address), lock=unlock_proof, ) self._check_exists() raise TransactionThrew('Withdraw', receipt_or_none) elif log.isEnabledFor(logging.INFO): log.info( 'withdraw sucessfull', node=pex(self.node_address), contract=pex(self.address), lock=unlock_proof, )
def settle(self): """ Settle the channel. Raises: ChannelBusyError: If the channel is busy with another operation """ if log.isEnabledFor(logging.INFO): log.info( 'settle called', node=pex(self.node_address), ) if not self.channel_operations_lock.acquire(0): raise ChannelBusyError( f'Channel with address {self.address} is ' f'busy with another ongoing operation' ) self.channel_operations_lock.release() with self.channel_operations_lock: transaction_hash = estimate_and_transact( self.proxy, 'settle', ) self.client.poll(unhexlify(transaction_hash), timeout=self.poll_timeout) receipt_or_none = check_transaction_threw(self.client, transaction_hash) if receipt_or_none: log.info( 'settle failed', node=pex(self.node_address), contract=pex(self.address), ) self._check_exists() raise TransactionThrew('Settle', receipt_or_none) if log.isEnabledFor(logging.INFO): log.info( 'settle successful', node=pex(self.node_address), contract=pex(self.address), )
def settle(self): if log.isEnabledFor(logging.INFO): log.info('settle called') transaction_hash = estimate_and_transact( self.proxy.settle, self.startgas, self.gasprice, ) self.client.poll(transaction_hash.decode('hex'), timeout=self.poll_timeout) receipt_or_none = check_transaction_threw(self.client, transaction_hash) if receipt_or_none: log.info('settle failed', contract=pex(self.address)) self._check_exists() raise TransactionThrew('Settle', receipt_or_none) if log.isEnabledFor(logging.INFO): log.info('settle sucessfull', contract=pex(self.address))
def _new_netting_channel(self, other_peer, settle_timeout): if self.channel_exists(other_peer): raise DuplicatedChannelError('Channel with given partner address already exists') transaction_hash = estimate_and_transact( self.proxy, 'newChannel', other_peer, settle_timeout, ) if not transaction_hash: raise RuntimeError('open channel transaction failed') self.client.poll(unhexlify(transaction_hash), timeout=self.poll_timeout) if check_transaction_threw(self.client, transaction_hash): raise DuplicatedChannelError('Duplicated channel') return transaction_hash
def new_netting_channel(self, other_peer: Address, settle_timeout: int) -> Address: """ Creates and deploys a new netting channel contract. Args: other_peer: The peer to open the channel with. settle_timeout: The settle timout to use for this channel. Returns: The address of the new netting channel. """ if not isaddress(other_peer): raise ValueError('The other_peer must be a valid address') invalid_timeout = (settle_timeout < NETTINGCHANNEL_SETTLE_TIMEOUT_MIN or settle_timeout > NETTINGCHANNEL_SETTLE_TIMEOUT_MAX) if invalid_timeout: raise InvalidSettleTimeout( 'settle_timeout must be in range [{}, {}]'.format( NETTINGCHANNEL_SETTLE_TIMEOUT_MIN, NETTINGCHANNEL_SETTLE_TIMEOUT_MAX)) local_address = privatekey_to_address(self.client.privkey) if local_address == other_peer: raise SamePeerAddress( 'The other peer must not have the same address as the client.') transaction_hash = estimate_and_transact( self.proxy, 'newChannel', other_peer, settle_timeout, ) self.client.poll(unhexlify(transaction_hash), timeout=self.poll_timeout) if check_transaction_threw(self.client, transaction_hash): raise DuplicatedChannelError('Duplicated channel') netting_channel_results_encoded = self.proxy.call( 'getChannelWith', other_peer, ) # address is at index 0 netting_channel_address_encoded = netting_channel_results_encoded if not netting_channel_address_encoded: log.error('netting_channel_address failed', peer1=pex(local_address), peer2=pex(other_peer)) raise RuntimeError('netting_channel_address failed') netting_channel_address_bin = address_decoder( netting_channel_address_encoded) if log.isEnabledFor(logging.INFO): log.info( 'new_netting_channel called', peer1=pex(local_address), peer2=pex(other_peer), netting_channel=pex(netting_channel_address_bin), ) return netting_channel_address_bin
def new_netting_channel(self, peer1, peer2, settle_timeout): if not isaddress(peer1): raise ValueError('The peer1 must be a valid address') if not isaddress(peer2): raise ValueError('The peer2 must be a valid address') invalid_timeout = (settle_timeout < NETTINGCHANNEL_SETTLE_TIMEOUT_MIN or settle_timeout > NETTINGCHANNEL_SETTLE_TIMEOUT_MAX) if invalid_timeout: raise ValueError('settle_timeout must be in range [{}, {}]'.format( NETTINGCHANNEL_SETTLE_TIMEOUT_MIN, NETTINGCHANNEL_SETTLE_TIMEOUT_MAX)) if peer1 == peer2: raise SamePeerAddress('Peer1 and peer2 must not be equal') if privatekey_to_address(self.client.privkey) == peer1: other = peer2 else: other = peer1 transaction_hash = estimate_and_transact( self.proxy.newChannel, self.startgas, self.gasprice, other, settle_timeout, ) self.client.poll(unhexlify(transaction_hash), timeout=self.poll_timeout) if check_transaction_threw(self.client, transaction_hash): raise DuplicatedChannelError('Duplicated channel') netting_channel_results_encoded = self.proxy.getChannelWith.call( other, startgas=self.startgas, ) # address is at index 0 netting_channel_address_encoded = netting_channel_results_encoded if not netting_channel_address_encoded: log.error('netting_channel_address failed', peer1=pex(peer1), peer2=pex(peer2)) raise RuntimeError('netting_channel_address failed') netting_channel_address_bin = address_decoder( netting_channel_address_encoded) if log.isEnabledFor(logging.INFO): log.info( 'new_netting_channel called', peer1=pex(peer1), peer2=pex(peer2), netting_channel=pex(netting_channel_address_bin), ) return netting_channel_address_bin
def close(self, nonce, transferred_amount, locksroot, extra_hash, signature): """ Close the channel using the provided balance proof. Raises: AddressWithoutCode: If the channel was settled prior to the call. ChannelBusyError: If the channel is busy with another operation. """ log.info( 'close called', node=pex(self.node_address), contract=pex(self.address), nonce=nonce, transferred_amount=transferred_amount, locksroot=encode_hex(locksroot), extra_hash=encode_hex(extra_hash), signature=encode_hex(signature), ) if not self.channel_operations_lock.acquire(blocking=False): raise ChannelBusyError( f'Channel with address {self.address} is ' f'busy with another ongoing operation.' ) with releasing(self.channel_operations_lock): transaction_hash = estimate_and_transact( self.proxy, 'close', nonce, transferred_amount, locksroot, extra_hash, signature, ) self.client.poll(unhexlify(transaction_hash), timeout=self.poll_timeout) receipt_or_none = check_transaction_threw(self.client, transaction_hash) if receipt_or_none: log.critical( 'close failed', node=pex(self.node_address), contract=pex(self.address), nonce=nonce, transferred_amount=transferred_amount, locksroot=encode_hex(locksroot), extra_hash=encode_hex(extra_hash), signature=encode_hex(signature), ) self._check_exists() raise TransactionThrew('Close', receipt_or_none) log.info( 'close successful', node=pex(self.node_address), contract=pex(self.address), nonce=nonce, transferred_amount=transferred_amount, locksroot=encode_hex(locksroot), extra_hash=encode_hex(extra_hash), signature=encode_hex(signature), )
def deposit(self, amount): """ Deposit amount token in the channel. Raises: AddressWithoutCode: If the channel was settled prior to the call. ChannelBusyError: If the channel is busy with another operation RuntimeError: If the netting channel token address is empty. """ if not isinstance(amount, int): raise ValueError('amount needs to be an integral number.') token_address = self.token_address() token = Token( self.client, token_address, self.poll_timeout, ) current_balance = token.balance_of(self.node_address) if current_balance < amount: raise ValueError('deposit [{}] cant be larger than the available balance [{}].'.format( amount, current_balance, )) log.info( 'deposit called', node=pex(self.node_address), contract=pex(self.address), amount=amount, ) if not self.channel_operations_lock.acquire(blocking=False): raise ChannelBusyError( f'Channel with address {self.address} is ' f'busy with another ongoing operation.' ) with releasing(self.channel_operations_lock): transaction_hash = estimate_and_transact( self.proxy, 'deposit', amount, ) self.client.poll( unhexlify(transaction_hash), timeout=self.poll_timeout, ) receipt_or_none = check_transaction_threw(self.client, transaction_hash) if receipt_or_none: log.critical( 'deposit failed', node=pex(self.node_address), contract=pex(self.address), ) self._check_exists() raise TransactionThrew('Deposit', receipt_or_none) log.info( 'deposit successful', node=pex(self.node_address), contract=pex(self.address), amount=amount, )