def _register_secret_batch(self, secrets): gas_limit = self.proxy.estimate_gas('registerSecretBatch', secrets) gas_limit = safe_gas_limit( gas_limit, len(secrets) * GAS_REQUIRED_PER_SECRET_IN_BATCH) transaction_hash = self.proxy.transact('registerSecretBatch', gas_limit, secrets) self.client.poll(transaction_hash) receipt_or_none = check_transaction_threw(self.client, transaction_hash) if receipt_or_none: raise TransactionThrew('registerSecretBatch', receipt_or_none) return transaction_hash
def register_endpoint(self, node_address, endpoint): if node_address != self.client.sender: raise ValueError("node_address doesnt match this node's address") transaction_hash = self.proxy.transact( 'registerEndpoint', endpoint, ) self.client.poll(transaction_hash) receipt_or_none = check_transaction_threw(self.client, transaction_hash) if receipt_or_none: raise TransactionThrew('Register Endpoint', receipt_or_none)
def unlock( self, channel_identifier: typing.ChannelID, partner: typing.Address, merkle_tree_leaves: typing.MerkleTreeLeaves, ): log_details = { 'token_network': pex(self.address), 'node': pex(self.node_address), 'partner': pex(partner), 'merkle_tree_leaves': merkle_tree_leaves, } if merkle_tree_leaves is None or not merkle_tree_leaves: log.info('skipping unlock, tree is empty', **log_details) return log.info('unlock called', **log_details) leaves_packed = b''.join(lock.encoded for lock in merkle_tree_leaves) transaction_hash = self.proxy.transact( 'unlock', self.node_address, partner, leaves_packed, ) self.client.poll(transaction_hash) receipt_or_none = check_transaction_threw(self.client, transaction_hash) if receipt_or_none: channel_settled = self.channel_is_settled(self.node_address, partner) if channel_settled is False: log.critical( 'unlock failed. Channel is not in a settled state', **log_details) raise ChannelIncorrectStateError( 'Channel is not in a settled state. An unlock cannot be made', ) log.critical('unlock failed', **log_details) raise TransactionThrew('Unlock', receipt_or_none) log.info('unlock successful', **log_details)
def withdraw( self, channel_identifier: typing.ChannelID, partner: typing.Address, total_withdraw: int, partner_signature: typing.Address, signature: typing.Signature, ): log_details = { 'token_network': pex(self.address), 'node': pex(self.node_address), 'partner': pex(partner), 'total_withdraw': total_withdraw, 'partner_signature': encode_hex(partner_signature), 'signature': encode_hex(signature), } log.info('withdraw called', **log_details) self._check_for_outdated_channel( self.node_address, partner, channel_identifier, ) with self.channel_operations_lock[partner]: transaction_hash = self.proxy.transact( 'setTotalWithdraw', self.node_address, partner, total_withdraw, partner_signature, signature, ) self.client.poll(transaction_hash) receipt_or_none = check_transaction_threw(self.client, transaction_hash) if receipt_or_none: log.critical('withdraw failed', **log_details) channel_opened = self.channel_is_opened( self.node_address, partner) if channel_opened is False: raise ChannelIncorrectStateError( 'Channel is not in an opened state. A withdraw cannot be made', ) raise TransactionThrew('Withdraw', receipt_or_none) log.info('withdraw successful', **log_details)
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', 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, self.startgas, self.gasprice, locked_encoded, merkleproof_encoded, secret, ) self.client.poll(transaction_hash.decode('hex'), 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', contract=pex(self.address), lock=lock, ) self._check_exists() failed = True if log.isEnabledFor(logging.INFO): log.info( 'withdraw sucessfull', contract=pex(self.address), lock=lock, ) if failed: raise TransactionThrew('Withdraw', receipt_or_none)
def add_token(self, token_address: typing.TokenAddress): if not isaddress(token_address): raise InvalidAddress('Expected binary address format for token') log.info( 'add_token called', node=pex(self.node_address), token_address=pex(token_address), registry_address=pex(self.address), ) transaction_hash = self.proxy.transact('createERC20TokenNetwork', 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('createERC20TokenNetwork', receipt_or_none) token_network_address = self.token_to_tokennetwork(token_address) if token_network_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('token_to_token_networks failed') log.info( 'add_token sucessful', node=pex(self.node_address), token_address=pex(token_address), registry_address=pex(self.address), token_network_address=pex(token_network_address), ) return token_network_address
def set_total_deposit(self, total_deposit): """ Set the total deposit of 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(total_deposit, int): raise ValueError('total_deposit needs to be an integral number.') log.info( 'set_total_deposit called', node=pex(self.node_address), contract=pex(self.address), amount=total_deposit, ) 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 = self.proxy.transact( 'setTotalDeposit', total_deposit, ) self.client.poll(unhexlify(transaction_hash)) receipt_or_none = check_transaction_threw(self.client, transaction_hash) if receipt_or_none: log.critical( 'set_total_deposit failed', node=pex(self.node_address), contract=pex(self.address), ) self._check_exists() raise TransactionThrew('Deposit', receipt_or_none) log.info( 'set_total_deposit successful', node=pex(self.node_address), contract=pex(self.address), amount=total_deposit, )
def add_token(self, token_address: typing.TokenAddress): if not is_binary_address(token_address): raise InvalidAddress('Expected binary address format for token') log_details = { 'node': pex(self.node_address), 'token_address': pex(token_address), 'registry_address': pex(self.address), } log.debug('createERC20TokenNetwork called', **log_details) transaction_hash = self.proxy.transact( 'createERC20TokenNetwork', safe_gas_limit(GAS_REQUIRED_FOR_CREATE_ERC20_TOKEN_NETWORK), token_address, ) self.client.poll(transaction_hash) receipt_or_none = check_transaction_threw(self.client, transaction_hash) if receipt_or_none: if self.get_token_network(token_address): msg = 'Token already registered' log.info(f'createERC20TokenNetwork failed, {msg}', **log_details) raise RaidenRecoverableError(msg) log.critical(f'createERC20TokenNetwork failed', **log_details) raise TransactionThrew('createERC20TokenNetwork', receipt_or_none) token_network_address = self.get_token_network(token_address) if token_network_address is None: log.critical( 'createERC20TokenNetwork failed and check_transaction_threw didnt detect it', **log_details, ) raise RuntimeError('token_to_token_networks failed') log.info( 'createERC20TokenNetwork successful', token_network_address=pex(token_network_address), **log_details, ) return token_network_address
def update_transfer( self, partner: typing.Address, nonce: typing.Nonce, balance_hash: typing.BalanceHash, additional_hash: typing.AdditionalHash, closing_signature: typing.Signature, non_closing_signature: typing.Signature, ): log_details = { 'token_network': pex(self.address), 'node': pex(self.node_address), 'partner': pex(partner), 'nonce': nonce, 'balance_hash': encode_hex(balance_hash), 'additional_hash': encode_hex(additional_hash), 'closing_signature': encode_hex(closing_signature), 'non_closing_signature': encode_hex(non_closing_signature), } log.info('updateNonClosingBalanceProof called', **log_details) transaction_hash = self.proxy.transact( 'updateNonClosingBalanceProof', partner, self.node_address, balance_hash, nonce, additional_hash, closing_signature, non_closing_signature, ) self.client.poll(unhexlify(transaction_hash)) receipt_or_none = check_transaction_threw(self.client, transaction_hash) if receipt_or_none: log.critical('updateNonClosingBalanceProof failed', **log_details) channel_closed = self.channel_is_closed(self.node_address, partner) if channel_closed is False: raise ChannelIncorrectStateError( 'Channel is not in a closed state') raise TransactionThrew('Update NonClosing balance proof', receipt_or_none) log.info('updateNonClosingBalanceProof successful', **log_details)
def register_endpoint(self, node_address, endpoint): if node_address != self.client.sender: raise ValueError("node_address doesnt match this node's address") transaction_hash = self.proxy.registerEndpoint.transact( endpoint, gasprice=self.gasprice, startgas=DISCOVERY_REGISTRATION_GAS) 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('Register Endpoint', 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): 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 unlock(self, partner: typing.Address, merkle_tree_leaves: typing.MerkleTreeLeaves): log.info( 'unlock called', token_network=pex(self.address), node=pex(self.node_address), partner=pex(partner), ) # TODO see if we need to do any checks for the unlock_proof transaction_hash = self.proxy.transact( 'unlock', self.node_address, partner, merkle_tree_leaves, ) 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( 'unlock failed', token_network=pex(self.address), node=pex(self.node_address), partner=pex(partner), ) channel_settled = self.channel_is_settled(partner) if channel_settled is False: raise ChannelIncorrectStateError( 'Channel is not in a settled state. An unlock cannot be made', ) raise TransactionThrew('Unlock', receipt_or_none) log.info( 'unlock successful', token_network=pex(self.address), node=pex(self.node_address), partner=pex(partner), )
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 register_secret(self, secret: typing.Secret): secrethash = sha3(secret) if self.check_registered(secrethash): log.info( 'secret already registered', node=pex(self.node_address), contract=pex(self.address), secrethash=encode_hex(secrethash), ) return log.info( 'registerSecret called', node=pex(self.node_address), contract=pex(self.address), ) transaction_hash = self.proxy.transact( 'registerSecret', 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( 'registerSecret failed', node=pex(self.node_address), contract=pex(self.address), secret=secret, ) raise TransactionThrew('registerSecret', receipt_or_none) log.info( 'registerSecret successful', node=pex(self.node_address), contract=pex(self.address), secret=secret, )
def unlock(self, unlock_proof): log.info( 'unlock 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 = b''.join(unlock_proof.merkle_proof) transaction_hash = self.proxy.transact( 'unlock', 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( 'unlock failed', node=pex(self.node_address), contract=pex(self.address), lock=unlock_proof, ) self._check_exists() raise TransactionThrew('unlock', receipt_or_none) log.info( 'unlock successful', 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 """ log.info( 'settle called', node=pex(self.node_address), ) 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 = self.proxy.transact( '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) log.info( 'settle successful', node=pex(self.node_address), contract=pex(self.address), )
def transfer(self, to_address: Address, amount: TokenAmount): # Note that given_block_identifier is not used here as there # are no preconditions to check before sending the transaction log_details = { "node": pex(self.node_address), "contract": pex(self.address), "to_address": pex(to_address), "amount": amount, } log.debug("transfer called", **log_details) startgas = GAS_LIMIT_FOR_TOKEN_CONTRACT_CALL transaction_hash = self.proxy.transact( "transfer", safe_gas_limit(startgas), to_checksum_address(to_address), amount ) self.client.poll(transaction_hash) receipt_or_none = check_transaction_threw(self.client, transaction_hash) if receipt_or_none: log.critical("transfer failed", **log_details) raise TransactionThrew("Transfer", receipt_or_none) # TODO: check Transfer event (issue: #2598) log.info("transfer successful", **log_details)
def register_endpoint(self, node_address, endpoint): if node_address != self.client.sender: raise ValueError("node_address doesnt match this node's address") log_details = { 'node': pex(self.node_address), 'node_address': pex(node_address), 'endpoint': endpoint, } log.debug('registerEndpoint called', **log_details) transaction_hash = self.proxy.transact( 'registerEndpoint', endpoint, ) self.client.poll(transaction_hash) receipt_or_none = check_transaction_threw(self.client, transaction_hash) if receipt_or_none: log.critical('registerEndpoint failed', **log_details) raise TransactionThrew('Register Endpoint', receipt_or_none) log.debug('registerEndpoint successful', **log_details)
def transfer(self, to_address, amount): log_details = { 'node': pex(self.node_address), 'contract': pex(self.address), 'to_address': pex(to_address), 'amount': amount, } log.debug('transfer called', **log_details) transaction_hash = self.proxy.transact( 'transfer', to_checksum_address(to_address), amount, ) self.client.poll(transaction_hash) receipt_or_none = check_transaction_threw(self.client, transaction_hash) if receipt_or_none: log.critical('transfer failed', **log_details) raise TransactionThrew('Transfer', receipt_or_none) # TODO: check Transfer event (issue: #2598) log.info('transfer successful', **log_details)
def register_endpoint(self, node_address, endpoint): if node_address != self.client.address: raise ValueError("node_address doesnt match this node's address") log_details = { "node": pex(self.node_address), "node_address": pex(node_address), "endpoint": endpoint, } log.debug("registerEndpoint called", **log_details) transaction_hash = self.proxy.transact( "registerEndpoint", safe_gas_limit(GAS_REQUIRED_FOR_ENDPOINT_REGISTER), endpoint) self.client.poll(transaction_hash) receipt_or_none = check_transaction_threw(self.client, transaction_hash) if receipt_or_none: log.critical("registerEndpoint failed", **log_details) raise TransactionThrew("Register Endpoint", receipt_or_none) log.debug("registerEndpoint successful", **log_details)
def update_transfer( self, nonce, transferred_amount, locked_amount, locksroot, extra_hash, signature, ): if signature: log.info( 'updateTransfer called', node=pex(self.node_address), contract=pex(self.address), nonce=nonce, transferred_amount=transferred_amount, locked_amount=locked_amount, locksroot=encode_hex(locksroot), extra_hash=encode_hex(extra_hash), signature=encode_hex(signature), ) transaction_hash = self.proxy.transact( 'updateTransfer', nonce, transferred_amount, locked_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, locked_amount=locked_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) log.info( 'updateTransfer successful', node=pex(self.node_address), contract=pex(self.address), nonce=nonce, transferred_amount=transferred_amount, locked_amount=locked_amount, locksroot=encode_hex(locksroot), extra_hash=encode_hex(extra_hash), signature=encode_hex(signature), )
def close(self, nonce, transferred_amount, locked_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, locked_amount=locked_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 = self.proxy.transact( 'close', nonce, transferred_amount, locked_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, locked_amount=locked_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, locked_amount=locked_amount, locksroot=encode_hex(locksroot), extra_hash=encode_hex(extra_hash), signature=encode_hex(signature), )
def settle( self, channel_identifier: typing.ChannelID, transferred_amount: int, locked_amount: int, locksroot: typing.Locksroot, partner: typing.Address, partner_transferred_amount: int, partner_locked_amount: int, partner_locksroot: typing.Locksroot, ): """ Settle the channel. Raises: ChannelBusyError: If the channel is busy with another operation """ log_details = { 'token_network': pex(self.address), 'node': pex(self.node_address), 'partner': pex(partner), 'transferred_amount': transferred_amount, 'locked_amount': locked_amount, 'locksroot': encode_hex(locksroot), 'partner_transferred_amount': partner_transferred_amount, 'partner_locked_amount': partner_locked_amount, 'partner_locksroot': encode_hex(partner_locksroot), } log.debug('settle called', **log_details) self._check_for_outdated_channel( self.node_address, partner, channel_identifier, ) with self.channel_operations_lock[partner]: our_maximum = transferred_amount + locked_amount partner_maximum = partner_transferred_amount + partner_locked_amount # The second participant transferred + locked amount must be higher our_bp_is_larger = our_maximum > partner_maximum if our_bp_is_larger: transaction_hash = self.proxy.transact( 'settleChannel', channel_identifier, partner, partner_transferred_amount, partner_locked_amount, partner_locksroot, self.node_address, transferred_amount, locked_amount, locksroot, ) else: transaction_hash = self.proxy.transact( 'settleChannel', channel_identifier, self.node_address, transferred_amount, locked_amount, locksroot, partner, partner_transferred_amount, partner_locked_amount, partner_locksroot, ) self.client.poll(transaction_hash) receipt_or_none = check_transaction_threw(self.client, transaction_hash) if receipt_or_none: log.critical('settle failed', **log_details) self._check_channel_state_for_settle( self.node_address, partner, channel_identifier, ) raise TransactionThrew('Settle', receipt_or_none) log.info('settle successful', **log_details)
def withdraw( self, channel_identifier: typing.ChannelID, partner: typing.Address, total_withdraw: int, partner_signature: typing.Address, signature: typing.Signature, ): log_details = { 'token_network': pex(self.address), 'node': pex(self.node_address), 'partner': pex(partner), 'total_withdraw': total_withdraw, 'partner_signature': encode_hex(partner_signature), 'signature': encode_hex(signature), } log.debug('setTotalWithdraw called', **log_details) self._check_for_outdated_channel( self.node_address, partner, channel_identifier, ) current_withdraw = self.detail_participant( channel_identifier, self.node_address, partner, ).withdrawn amount_to_withdraw = total_withdraw - current_withdraw if total_withdraw < current_withdraw: msg = ( f'Current withdraw ({current_withdraw}) is already larger ' f'than the requested total withdraw amount ({total_withdraw})', ) log.critical(f'setTotalWithdraw failed, {msg}', **log_details) raise WithdrawMismatch(msg) if amount_to_withdraw <= 0: msg = f'withdraw {amount_to_withdraw} must be greater than 0.' log.critical(f'setTotalWithdraw failed, {msg}', **log_details) raise ValueError(msg) with self.channel_operations_lock[partner]: transaction_hash = self.proxy.transact( 'setTotalWithdraw', channel_identifier, self.node_address, total_withdraw, partner_signature, signature, ) self.client.poll(transaction_hash) receipt_or_none = check_transaction_threw(self.client, transaction_hash) if receipt_or_none: log.critical('setTotalWithdraw failed', **log_details) self._check_channel_state_for_withdraw( self.node_address, partner, channel_identifier, total_withdraw, ) raise TransactionThrew('Withdraw', receipt_or_none) log.info('setTotalWithdraw successful', **log_details)
def update_transfer( self, channel_identifier: typing.ChannelID, partner: typing.Address, balance_hash: typing.BalanceHash, nonce: typing.Nonce, additional_hash: typing.AdditionalHash, closing_signature: typing.Signature, non_closing_signature: typing.Signature, ): log_details = { 'token_network': pex(self.address), 'node': pex(self.node_address), 'partner': pex(partner), 'nonce': nonce, 'balance_hash': encode_hex(balance_hash), 'additional_hash': encode_hex(additional_hash), 'closing_signature': encode_hex(closing_signature), 'non_closing_signature': encode_hex(non_closing_signature), } log.debug('updateNonClosingBalanceProof called', **log_details) self._check_for_outdated_channel( self.node_address, partner, channel_identifier, ) transaction_hash = self.proxy.transact( 'updateNonClosingBalanceProof', channel_identifier, partner, self.node_address, balance_hash, nonce, additional_hash, closing_signature, non_closing_signature, ) self.client.poll(transaction_hash) receipt_or_none = check_transaction_threw(self.client, transaction_hash) if receipt_or_none: channel_closed = self.channel_is_closed( participant1=self.node_address, participant2=partner, channel_identifier=channel_identifier, ) if channel_closed is False: msg = 'Channel is not in a closed state' log.critical(f'updateNonClosingBalanceProof failed, {msg}', **log_details) raise RaidenUnrecoverableError(msg) msg = 'Update NonClosing balance proof' log.critical(f'updateNonClosingBalanceProof failed, {msg}', **log_details) raise TransactionThrew(msg, receipt_or_none) log.info('updateNonClosingBalanceProof successful', **log_details)
def close( self, channel_identifier: typing.ChannelID, partner: typing.Address, balance_hash: typing.BalanceHash, nonce: typing.Nonce, additional_hash: typing.AdditionalHash, signature: typing.Signature, ): """ Close the channel using the provided balance proof. Raises: ChannelBusyError: If the channel is busy with another operation. RaidenRecoverableError: If the channel is already closed. RaidenUnrecoverableError: If the channel does not exist or is settled. """ log_details = { 'token_network': pex(self.address), 'node': pex(self.node_address), 'partner': pex(partner), 'nonce': nonce, 'balance_hash': encode_hex(balance_hash), 'additional_hash': encode_hex(additional_hash), 'signature': encode_hex(signature), } log.debug('closeChannel called', **log_details) self._check_for_outdated_channel( self.node_address, partner, channel_identifier, ) self._check_channel_state_for_close( self.node_address, partner, channel_identifier, ) with self.channel_operations_lock[partner]: transaction_hash = self.proxy.transact( 'closeChannel', channel_identifier, partner, balance_hash, nonce, additional_hash, signature, ) self.client.poll(transaction_hash) receipt_or_none = check_transaction_threw(self.client, transaction_hash) if receipt_or_none: log.critical('closeChannel failed', **log_details) self._check_channel_state_for_close( self.node_address, partner, channel_identifier, ) raise TransactionThrew('Close', receipt_or_none) log.info('closeChannel successful', **log_details)
def set_total_deposit( self, channel_identifier: typing.ChannelID, total_deposit: typing.TokenAmount, partner: typing.Address, ): """ Set total token deposit in the channel to total_deposit. Raises: ChannelBusyError: If the channel is busy with another operation RuntimeError: If the token address is empty. """ if not isinstance(total_deposit, int): raise ValueError('total_deposit needs to be an integral number.') self._check_for_outdated_channel( self.node_address, partner, channel_identifier, ) token_address = self.token_address() token = Token(self.client, token_address) with self.channel_operations_lock[partner], self.deposit_lock: # setTotalDeposit requires a monotonically increasing value. This # is used to handle concurrent actions: # # - The deposits will be done in order, i.e. the monotonic # property is preserved by the caller # - The race of two deposits will be resolved with the larger # deposit winning # - Retries wont have effect # # This check is serialized with the channel_operations_lock to avoid # sending invalid transactions on-chain (decreasing total deposit). # current_deposit = self.detail_participant( channel_identifier, self.node_address, partner, ).deposit amount_to_deposit = total_deposit - current_deposit log_details = { 'token_network': pex(self.address), 'node': pex(self.node_address), 'partner': pex(partner), 'total_deposit': total_deposit, 'amount_to_deposit': amount_to_deposit, 'id': id(self), } log.debug('setTotalDeposit called', **log_details) # These two scenarions can happen if two calls to deposit happen # and then we get here on the second call if total_deposit < current_deposit: msg = ( f'Current deposit ({current_deposit}) is already larger ' f'than the requested total deposit amount ({total_deposit})' ) log.info(f'setTotalDeposit failed, {msg}', **log_details) raise DepositMismatch(msg) if amount_to_deposit <= 0: msg = (f'total_deposit - current_deposit = ' f'{amount_to_deposit} must be greater than 0.', ) log.info(f'setTotalDeposit failed, {msg}', **log_details) raise DepositMismatch(msg) # A node may be setting up multiple channels for the same token # concurrently. Because each deposit changes the user balance this # check must be serialized with the operation locks. # # This check is merely informational, used to avoid sending # transactions which are known to fail. # # It is serialized with the deposit_lock to avoid sending invalid # transactions on-chain (account without balance). The lock # channel_operations_lock is not sufficient, as it allows two # concurrent deposits for different channels. # current_balance = token.balance_of(self.node_address) if current_balance < amount_to_deposit: msg = ( f'total_deposit - current_deposit = {amount_to_deposit} can not ' f'be larger than the available balance {current_balance}, ' f'for token at address {pex(token_address)}') log.info(f'setTotalDeposit failed, {msg}', **log_details) raise DepositMismatch(msg) # If there are channels being set up concurrenlty either the # allowance must be accumulated *or* the calls to `approve` and # `setTotalDeposit` must be serialized. This is necessary otherwise # the deposit will fail. # # Calls to approve and setTotalDeposit are serialized with the # deposit_lock to avoid transaction failure, because with two # concurrent deposits, we may have the transactions executed in the # following order # # - approve # - approve # - setTotalDeposit # - setTotalDeposit # # in which case the second `approve` will overwrite the first, # and the first `setTotalDeposit` will consume the allowance, # making the second deposit fail. token.approve(self.address, amount_to_deposit) transaction_hash = self.proxy.transact( 'setTotalDeposit', channel_identifier, self.node_address, total_deposit, partner, ) self.client.poll(transaction_hash) receipt_or_none = check_transaction_threw(self.client, transaction_hash) if receipt_or_none: if token.allowance(self.node_address, self.address) < amount_to_deposit: log_msg = ( 'setTotalDeposit failed. The allowance is insufficient, ' 'check concurrent deposits for the same token network ' 'but different proxies.') elif token.balance_of(self.node_address) < amount_to_deposit: log_msg = 'setTotalDeposit failed. The address doesnt have funds' else: log_msg = 'setTotalDeposit failed' log.critical(log_msg, **log_details) self._check_channel_state_for_deposit( self.node_address, partner, channel_identifier, total_deposit, ) raise TransactionThrew('Deposit', receipt_or_none) log.info('setTotalDeposit successful', **log_details)
def settle( self, transferred_amount: int, locked_amount: int, locksroot: typing.Locksroot, partner: typing.Address, partner_transferred_amount: int, partner_locked_amount: int, partner_locksroot: typing.Locksroot, ): """ Settle the channel. Raises: ChannelBusyError: If the channel is busy with another operation """ log.info( 'settle called', token_network=pex(self.address), node=pex(self.node_address), partner=pex(partner), transferred_amount=transferred_amount, locked_amount=locked_amount, locksroot=encode_hex(locksroot), partner_transferred_amount=partner_transferred_amount, partner_locked_amount=partner_locked_amount, partner_locksroot=encode_hex(partner_locksroot), ) self._check_channel_lock(partner) with releasing(self.channel_operations_lock[partner]): transaction_hash = self.proxy.transact( 'settleChannel', self.node_address, transferred_amount, locked_amount, locksroot, partner, partner_transferred_amount, partner_locked_amount, partner_locksroot, ) 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', token_network=pex(self.address), node=pex(self.node_address), partner=pex(partner), transferred_amount=transferred_amount, locked_amount=locked_amount, locksroot=encode_hex(locksroot), partner_transferred_amount=partner_transferred_amount, partner_locked_amount=partner_locked_amount, partner_locksroot=encode_hex(partner_locksroot), ) channel_closed = self.channel_is_closed(partner) if channel_closed is False: raise ChannelIncorrectStateError( 'Channel is not in a closed state. It cannot be settled', ) raise TransactionThrew('Settle', receipt_or_none) log.info( 'settle successful', token_network=pex(self.address), node=pex(self.node_address), partner=pex(partner), transferred_amount=transferred_amount, locked_amount=locked_amount, locksroot=encode_hex(locksroot), partner_transferred_amount=partner_transferred_amount, partner_locked_amount=partner_locked_amount, partner_locksroot=encode_hex(partner_locksroot), )