def notify_rcode_to_next_peer(cls, htlc_trade, rcode): """ :param next_channel: :return: """ if not htlc_trade: LOG.error('void input parameter -- htlc_trade') return if not htlc_trade.channel: LOG.info('Htlc trade with HashR<{}> has been finished'.format( htlc_trade.hashcode)) return peer = None try: # notify the previous node the R-code LOG.debug('Payment get channel {}/{}'.format( htlc_trade.channel, htlc_trade.hashcode)) channel = Channel(htlc_trade.channel) nonce = channel.latest_nonce(htlc_trade.channel) LOG.info("Next peer: {}".format(peer)) from wallet.transaction.htlc import RResponse RResponse.create(htlc_trade.channel, 'TNC', nonce, channel.partner_uri, channel.founder_uri, htlc_trade.hashcode, rcode, None) except Exception as error: LOG.error('Failed to notify RCode<{}> to peer<{}>'.format( rcode, peer))
def confirm_payment(cls, channel_name, hashcode, hlock_to_rsmc=False): """""" if not hlock_to_rsmc: return # find the htlc trade history try: htlc_lock = cls.batch_query_trade( channel_name, filters={ 'type': EnumTradeType.TRADE_TYPE_HTLC.name, 'hashcode': hashcode })[0] cls.update_trade(channel_name, htlc_lock.nonce, state=EnumTradeState.confirmed.name) return except Exception as error: LOG.error( 'Payment for channel<{}> with HashR<{}> Not found from the DB'. format(channel_name, hashcode)) LOG.error('confirm payament Exception: {}'.format(error)) return
def latest_valid_trade(cls, channel_name): """ :param channel_name: :return: """ try: filters = { '$or': [{ 'state': EnumTradeState.confirmed_onchain.name }, { 'state': EnumTradeState.confirmed.name }, { 'state': EnumTradeState.confirming.name }] } valid_trade = APITransaction(channel_name).sort(key='nonce', filters=filters)[0] except Exception as error: LOG.exception( 'No valid transaction records were found for channel<{}>.'. format(channel_name)) return None, None else: return valid_trade, valid_trade.nonce
def quick_close(cls, channel_name, wallet=None, cli=False, trigger=None): """ :param channel_name: :param wallet: :param cli: :param trigger: :return: """ try: channel = cls.query_channel(channel_name)[0] # get peer address peer = channel.src_addr if wallet.url == peer: peer = channel.dest_addr # start trigger to close channel quickly trigger(wallet, peer, channel_name, 'TNC') except Exception as error: if cli: console_log.error( 'Failed to close channel: {}'.format(channel_name)) LOG.error('Failed to close channel<{}>, Exception: {}'.format( channel_name, error))
def latest_trade(channel_name): try: trade = APITransaction(channel_name).sort(key='nonce')[0] except Exception as error: LOG.error( 'No transaction records were found for channel<{}>. Exception: {}' .format(channel_name, error)) return None else: return trade
def transfer( cls, channel_name, wallet, receiver, asset_type, count, hashcode=None, cli=False, router=None, next_jump=None, comments=None, trigger=None, ): """ :param sender: :param receiver: :param asset_type: :param count: :param hash_random: :param wallet: :return: """ try: if router: # HTLC transaction trigger(wallet, channel_name, asset_type, wallet.url, receiver, count, hashcode, router, next_jump, comments=comments) else: # RSMC transaction trigger(channel_name, asset_type, wallet.url, receiver, count, hashcode=None, comments=comments) except Exception as error: LOG.error('Failed to transfer {} {} to receiver<{}>. Exception: {}' \ .format(count, asset_type, receiver, error)) return
def sync_channel_info_to_gateway(channel_name, type, asset_type='TNC'): LOG.info("Debug sync_channel_info_to_gateway channel {} type {}".format( channel_name, type)) ch = Channel(channel_name) if not ch: return None balance = ch.balance nb = {} for item, value in balance.items(): if ch.founder_uri.__contains__(item): nb[ch.founder_uri] = value else: nb[ch.partner_uri] = value return sync_channel(type, ch.channel_name, ch.founder_uri, ch.partner_uri, nb, asset_type)
def latest_confirmed_trade(cls, channel_name): try: filters = { '$or': [{ 'state': EnumTradeState.confirmed_onchain.name }, { 'state': EnumTradeState.confirmed.name }] } trade = APITransaction(channel_name).sort(key='nonce', filters=filters)[0] except Exception as error: LOG.error( 'No transaction records were found for channel<{}>. Exception: {}' .format(channel_name, error)) return None else: return trade
def get_channel(cls, address1, address2, state=None): channels = [] filter_list = [{ 'src_addr': address1, 'dest_addr': address2 }, { 'src_addr': address2, 'dest_addr': address1 }] for filter_item in filter_list: if state: filter_item.update({'state': state.name}) try: channels.extend( APIChannel.batch_query_channel(filters=filter_item)) except Exception as error: LOG.debug( 'Batch query channels from DB error: {}'.format(error)) return channels
def force_release_htlc(cls, uri='', channel_name='', hashcode='', rcode='', sign_key='', gwei_coef=1, trigger=None, is_debug=False, is_pubnishment=False, htcl_to_rsmc=False): """ :param uri: :param channel_name: :param hashcode: :param rcode: :param sign_key: :param gwei_coef: :param trigger: :param is_debug: :param htcl_to_rsmc: :return: """ # some internal function here # ToDo: not a good way, could be optimized later def common_kwargs_for_trigger(invoker, channel_id, lock_hash, lock_secret, invoker_key, gwei_coef=1): # makeup the parameters for callback return { 'invoker': invoker, 'channel_id': channel_id, 'lock_hash': lock_hash, 'lock_secret': lock_secret, 'invoker_key': invoker_key, 'gwei_coef': gwei_coef } def unlock_htlc_kwargs(trade, address, peer_address, is_debug): """""" trigger_kwargs = { 'lock_period': trade.delay_block, 'lock_amount': trade.payment } # is debug by test engineer if not is_debug: trigger_kwargs.update({'lock_secret': htlc_trade.rcode}) # what role is played by this wallet if EnumTradeRole.TRADE_ROLE_FOUNDER.name == trade.role: trigger_kwargs.update({ 'founder': address, 'founder_signature': trade.delay_commitment, 'partner': peer_address, 'partner_signature': trade.peer_delay_commitment }) else: trigger_kwargs.update({ 'founder': peer_address, 'founder_signature': trade.peer_delay_commitment, 'partner': address, 'partner_signature': trade.delay_commitment }) return trigger_kwargs def punishment_htlc_kwargs(trade, address, peer_address): """""" trigger_kwargs = { 'nonce': trade.nonce, 'lock_secret': htlc_trade.rcode } # what role is played by this wallet if EnumTradeRole.TRADE_ROLE_FOUNDER.name == trade.role: trigger_kwargs.update({ 'founder': address, 'partner': peer_address, 'founder_balance': trade.balance, 'partner_balance': trade.peer_balance, 'founder_signature': trade.commitment, 'partner_signature': trade.peer_commitment }) else: trigger_kwargs.update({ 'founder': peer_address, 'partner': address, 'founder_balance': trade.peer_balance, 'partner_balance': trade.balance, 'founder_signature': trade.peer_commitment, 'partner_signature': trade.commitment }) return trigger_kwargs # start to unlock the payment of htlc-locked part try: # get the htlc record by hashcode htlc_trade = cls.batch_query_trade( channel_name, filters={ 'type': EnumTradeType.TRADE_TYPE_HTLC.name, 'hashcode': hashcode })[0] # if the state of this trasaction is confirming, no need to update transaction rsmc_trade = None if is_pubnishment: rsmc_trade = cls.batch_query_trade( channel_name, filters={ 'type': EnumTradeType.TRADE_TYPE_RSMC.name, 'hashcode': hashcode }) rsmc_trade = rsmc_trade[0] if rsmc_trade else None # no action Rsmc trade with this HashR has already existed and the state of trade is confirmed if rsmc_trade and rsmc_trade.state in [ EnumTradeState.confirmed.name ]: # to trigger the htlc punishment and close the channel pass else: # Below actions are needed: # step 1 : validate the R-code # step 2 : update the R-code to htlc trade LOG.info( 'No need to punish hlock transaction with HashR<{}>'. format(hashcode)) # to record this rcode if rcode is correct one if Payment.verify_hr(hashcode, rcode): cls.update_trade(channel_name, htlc_trade.nonce, rcode=rcode) # here, we need to notify the rcode to next wallet if htcl_to_rsmc: cls.notify_rcode_to_next_peer(htlc_trade, rcode) return {'result': 'success'} except Exception as error: LOG.error('No Htlc trade was found or rcode is error. channel<{}>, HashR<{}>. Exception: {}' \ .format(channel_name, hashcode, error)) else: channel = cls(channel_name) peer_uri = channel.peer_uri(uri) self_address, _, _ = uri_parser(uri) peer_address, _, _ = uri_parser(peer_uri) LOG.debug('Unlock Htlc payment: channel<{}>, HashR<{}>'.format( channel_name, hashcode)) # makeup the parameters for callback function trigger_kwargs = common_kwargs_for_trigger(self_address, channel_name, hashcode, rcode, sign_key, gwei_coef) # is withdraw update topic of the contract if is_pubnishment: trigger_kwargs.update( punishment_htlc_kwargs(rsmc_trade, self_address, peer_address)) else: trigger_kwargs.update( unlock_htlc_kwargs(htlc_trade, self_address, peer_address, is_debug)) # start trigger the callback and return the result. LOG.debug('Force to unlock htlc with aruments: {}'.format( trigger_kwargs)) return trigger(**trigger_kwargs) return None
def force_release_rsmc(cls, uri=None, channel_name=None, nonce=None, sign_key=None, gwei_coef=1, trigger=None, is_debug=False): """ :param uri: :param channel_name: :param nonce: :param sign_key: :param gwei_coef: :param trigger: :return: """ if not (uri and channel_name and sign_key and trigger): LOG.warn( 'uri<{}>, channel_name<{}>, trigger<{}> and sign_key should not be none' .format(uri, channel_name, trigger)) return None try: # get records by nonce from the trade history if is_debug and nonce is not None: trade = cls.query_trade(channel_name, int(nonce)) else: trade = cls.latest_confirmed_trade(channel_name) latest_nonce = int(trade.nonce) if nonce is not None and (latest_nonce == int(nonce) or int(nonce) > latest_nonce + 1): LOG.debug( 'No need update transaction. nonce<{}>, latest_nonce<{}>' .format(nonce, latest_nonce)) # update the channel to settling state Channel.update_channel( channel_name, state=EnumChannelState.SETTLING.name) return None nonce = latest_nonce except Exception as error: LOG.error('No trade record could be forced to release. channel<{}>, nonce<{}>. Exception: {}' \ .format(channel_name, nonce, error)) else: channel = cls(channel_name) peer_uri = channel.peer_uri(uri) self_address, _, _ = uri_parser(uri) peer_address, _, _ = uri_parser(peer_uri) LOG.debug('Force to close channel<{}> with nonce<{}>'.format( channel_name, nonce)) LOG.debug('Trade nonce: {}'.format(trade.nonce)) # get hashcode and rcode hashcode = None rcode = None if trade.nonce not in [0, 1]: hashcode = trade.hashcode rcode = trade.rcode trade_role = trade.role if EnumTradeRole.TRADE_ROLE_FOUNDER.name == trade_role: result = trigger(self_address, channel_name, nonce, self_address, trade.balance, peer_address, trade.peer_balance, hashcode, rcode, trade.commitment, trade.peer_commitment, sign_key, gwei_coef) else: result = trigger(self_address, channel_name, nonce, peer_address, trade.peer_balance, self_address, trade.balance, hashcode, rcode, trade.peer_commitment, trade.commitment, sign_key, gwei_coef) return result return None
def create(cls, wallet, founder, partner, asset_type, deposit, partner_deposit=None, comments=None, trigger=None, cli=True): """ Provide one method to be called by the wallet prompt console. :param wallet: :param partner: :param asset_type: :param deposit: :param partner_deposit: :param comments: :param cli: :return: """ if not (wallet and partner and asset_type and deposit): LOG.error('Invalid parameters:wallet<{}>, founder<{}>, partner<{}>, asset_type<{}>, deposit<{}>' \ .format(wallet, founder, partner, asset_type, deposit)) # here we could use some hooks to register event to handle output console ???? console_log.error( 'Illegal mandatory parameters. Please check in your command.') return False # use deposit as default value for both partners if partner's deposit is not set: if not partner_deposit: partner_deposit = deposit # judge whether the channel exist or not if Channel.get_channel(founder, partner, EnumChannelState.OPENED): console_log.warning('OPENED channel already exists.') return False else: # creating channel is ongoing # channel = Channel.get_channel(founder, partner, EnumChannelState.OPENING) # if : # console.warning('Channel {} is on the way. Please try later if failed') # return pass channel_name = cls.new_channel_name(founder, partner) if cli: deposit = int(deposit) partner_deposit = int(partner_deposit) if 0 >= deposit or 0 >= partner_deposit: LOG.error('Could not register channel because of illegal deposit<{}:{}>.'\ .format(deposit, partner_deposit)) return False try: trigger(wallet, channel_name, asset_type, founder, deposit, partner, partner_deposit, comments) except Exception as error: LOG.info('Create channel<{}> failed. Exception: {}'.format( channel_name, error)) return False return True
def add_channel(**kwargs): if kwargs.get('channel') is None: LOG.error('MUST specified the key parameter \'channel\'') return False kwargs.update({'alive_block': 0}) return APIChannel.add_channel(**kwargs)