def execute(self, block_height, invoker='', channel_name='', invoker_key=''): """ :param block_height: :param invoker_uri: :param channel_name: :param trade: :param invoker_key: :param gwei: :return: """ super(ChannelEndSettleEvent, self).execute(block_height) # close channel event result = self.contract_event_api.end_close_channel( invoker, channel_name, invoker_key, gwei_coef=self.gwei_coef) # set channel settling if result is not None and 'success' in result.values(): Channel.update_channel(self.channel_name, state=EnumChannelState.SETTLED.name) APIStatistics.update_statistics( self.wallet_address, state=EnumChannelState.SETTLED.name) self.next_stage()
def handle(self): super(RsmcResponsesMessage, self).handle() status = EnumResponseStatus.RESPONSE_OK try: # check the response status if not self.check_response_status(self.status): self.rollback_resource(self.channel_name, self.nonce, self.payment, self.status) return # common check self.check_channel_state(self.channel_name) self.verify() self.check_role(self.role_index) if 0 == self.role_index: need_update_balance, rsmc_sign_body = self.handle_partner_response() elif 1 == self.role_index: need_update_balance, rsmc_sign_body = self.handle_founder_response() else: raise GoTo(EnumResponseStatus.RESPONSE_FAIL, 'Invalid Role<{}> of RsmcResponse'.format(self.role_index)) # send RsmcSign to peer if rsmc_sign_body: response_message = self.create_message_header(self.receiver, self.sender, self._message_name, self.channel_name, self.asset_type, self.nego_nonce or self.nonce) response_message.update({'MessageBody': rsmc_sign_body}) response_message.update({'Status': status.name}) self.send(response_message) is_htlc_to_rsmc = self.is_hlock_to_rsmc(self.hashcode) if is_htlc_to_rsmc: # update the htlc transaction state. Channel.confirm_payment(self.channel_name, self.hashcode, is_htlc_to_rsmc) # update the channel balance if need_update_balance: APIStatistics.update_statistics(self.wallet.address, payment=self.payment, payer=(0==self.role_index)) self.update_balance_for_channel(self.channel_name, self.asset_type, self.payer_address, self.payee_address, self.payment, is_htlc_to_rsmc) except GoTo as error: LOG.exception(error) status = error.reason except Exception as error: status = EnumResponseStatus.RESPONSE_EXCEPTION_HAPPENED LOG.exception('Failed to handle RsmcSign for channel<{}> nonce<{}>, role_index<{}>.Exception: {}' \ .format(self.channel_name, self.nonce, self.role_index, error)) finally: # send error response if EnumResponseStatus.RESPONSE_OK != status: if 0 == self.role_index: self.send_error_response(self.sender, self.receiver, self.channel_name, self.asset_type, self.nonce, status, kwargs={'RoleIndex': '1'}) # need rollback some resources self.rollback_resource(self.channel_name, self.nonce, self.payment, status=self.status) return
def terminate(self, block_height, *args, asset_type='TNC'): super(ChannelQuickSettleEvent, self).terminate(block_height) # to check the total deposit of the channel total_deposit = self.contract_event_api.get_channel_total_balance(self.channel_name) if 0 >= total_deposit: Channel.update_channel(self.channel_name, state=EnumChannelState.CLOSED.name) APIStatistics.update_statistics(self.wallet_address, state=EnumChannelState.CLOSED.name) console_log.info('Channel {} state is {}'.format(self.channel_name, EnumChannelState.CLOSED.name)) self.next_stage()
def execute(self, block_height, invoker_uri='', channel_name='', invoker_key='', nonce=None): """ :param block_height: :param invoker_uri: :param channel_name: :param trade: :param invoker_key: :param gwei: :return: """ super(ChannelUpdateSettleEvent, self).execute(block_height) # close channel event # ToDo uncomment below codes in future # latest_trade = Channel.latest_confirmed_trade() # if isinstance(nonce, int) and int(nonce) == latest_trade.nonce: # or check balance: # self.next_stage() # return result = Channel.force_release_rsmc( invoker_uri, channel_name, nonce, invoker_key, gwei_coef=self.gwei_coef, trigger=self.contract_event_api.update_close_channel) # set channel settling if result is not None and 'success' in result.values(): Channel.update_channel(self.channel_name, state=EnumChannelState.SETTLED.name) APIStatistics.update_statistics( self.wallet_address, state=EnumChannelState.SETTLED.name) self.next_stage()
def notify_rcode_to_next_peer(self, next_channel): """ :param next_channel: :return: """ peer = None try: # original payer is found if not next_channel or next_channel == self.channel_name: LOG.info('HTLC Founder with HashR<{}> received the R-code<{}>'. format(self.hashcode, self.rcode)) return htlc_trade = self.get_htlc_trade_by_hashr(next_channel, self.hashcode) if htlc_trade.role == EnumTradeRole.TRADE_ROLE_PARTNER.name and next_channel != htlc_trade.channel: LOG.warning( 'Why the channel is different. next_channel<{}>, stored channel<{}>' .format(next_channel, htlc_trade.channel)) # notify the previous node the R-code LOG.debug('Payment get channel {}/{}'.format( next_channel, self.hashcode)) channel = Channel(next_channel) channel.update_trade(next_channel, htlc_trade.nonce, rcode=self.rcode) peer = channel.peer_uri(self.wallet.url) nonce = channel.latest_nonce(next_channel) LOG.info("Next peer: {}".format(peer)) self.create(next_channel, self.asset_type, nonce, self.wallet.url, peer, self.hashcode, self.rcode, self.comments) APIStatistics.update_statistics(self.wallet.address, htlc_rcode=True) except Exception as error: LOG.error('Failed to notify RCode<{}> to peer<{}>'.format( self.rcode, peer))
def terminate(self, block_height, *args, asset_type='TNC'): super(ChannelDepositEvent, self).terminate(block_height, *args) # check the deposit of the contract address total_deposit = self.contract_event_api.get_channel_total_balance(self.channel_name) if total_deposit >= self.deposit + self.partner_deposit > 0: Channel.update_channel(self.channel_name, state=EnumChannelState.OPENED.name) Channel.update_trade(self.channel_name, self.nonce, state=EnumTradeState.confirmed.name) APIStatistics.update_statistics(self.wallet_address, state=EnumChannelState.OPENED.name) sync_channel_info_to_gateway(self.channel_name, 'AddChannel', asset_type) console_log.info('Channel {} state is {}'.format(self.channel_name, EnumChannelState.OPENED.name)) # to trigger monitor event for closing channel event_monitor_close_channel(self.channel_name) event_monitor_settle(self.channel_name) # to trigger monitor event for unlocking htlc locked payment event_monitor_withdraw(self.channel_name) event_monitor_withdraw_update(self.channel_name) event_monitor_withdraw_settle(self.channel_name) self.next_stage()
def create(wallet, channel_name, asset_type, founder, founder_deposit, partner, partner_deposit, founder_commitment, comments=None): """ :param wallet: :param channel_name: :param asset_type: :param founder: :param founder_deposit: :param partner: :param partner_deposit: :param founder_commitment: :param comments: :return: """ # check the deposit founder_deposit = int(founder_deposit) partner_deposit = int(partner_deposit) FounderResponsesMessage.check_deposit(founder_deposit, partner_deposit) nonce = FounderResponsesMessage._FOUNDER_NONCE # start to sign content founder_address, _, _ = uri_parser(founder) partner_address, _, _ = uri_parser(partner) # Sign this data to the commitment = FounderResponsesMessage.sign_content( wallet, FounderResponsesMessage._sign_type_list, [ channel_name, nonce, founder_address, founder_deposit, partner_address, partner_deposit ]) # start add channel deposit = { founder_address: { asset_type: str(founder_deposit) }, partner_address: { asset_type: str(partner_deposit) } } hlock = { founder_address: { asset_type: '0' }, partner_address: { asset_type: '0' } } Channel.add_channel(channel=channel_name, src_addr=founder, dest_addr=partner, state=EnumChannelState.INIT.name, deposit=deposit, hlock=hlock, magic=get_magic()) APIStatistics.update_statistics(wallet.address, state=EnumChannelState.INIT.name) # add trade to database founder_trade = Channel.founder_trade( type=EnumTradeType.TRADE_TYPE_FOUNDER, role=EnumTradeRole.TRADE_ROLE_PARTNER, asset_type=asset_type, balance=partner_deposit, peer_balance=founder_deposit, commitment=commitment, peer_commitment=founder_commitment, state=EnumTradeState.confirming) Channel.add_trade(channel_name, nonce=nonce, **founder_trade) # create messages asset_type = asset_type.upper() message = FounderResponsesMessage.create_message_header( partner, founder, FounderResponsesMessage._message_name, channel_name, asset_type, nonce) message_body = {"Commitment": commitment} message.update({"MessageBody": message_body}) # Add comments in the messages if comments: message.update({"Comments": comments}) # fill message status message.update({'Status': EnumResponseStatus.RESPONSE_OK.name}) # send message to peer FounderResponsesMessage.send(message) return
def create(wallet, channel_name, asset_type, founder, founder_deposit, partner, partner_deposit=None, comments=None): """ :param wallet: :param channel_name: :param asset_type: :param founder: :param founder_deposit: :param partner: :param partner_deposit: :param comments: :return: """ founder_deposit = int(founder_deposit) if partner_deposit is None: partner_deposit = founder_deposit partner_deposit = int(partner_deposit) # check the deposit FounderMessage.check_deposit(founder_deposit, partner_deposit) # get addresses of peers nonce = FounderMessage._FOUNDER_NONCE founder_address, _, _ = uri_parser(founder) partner_address, _, _ = uri_parser(partner) # Sign this data to hash value commitment = FounderMessage.sign_content( wallet, FounderMessage._sign_type_list, [ channel_name, nonce, founder_address, founder_deposit, partner_address, partner_deposit ]) # add channel asset_type = asset_type.upper() deposit = { founder_address: { asset_type: str(founder_deposit) }, partner_address: { asset_type: str(partner_deposit) } } hlock = { founder_address: { asset_type: '0' }, partner_address: { asset_type: '0' } } Channel.add_channel(channel=channel_name, src_addr=founder, dest_addr=partner, state=EnumChannelState.INIT.name, deposit=deposit, magic=get_magic(), hlock=hlock) APIStatistics.update_statistics(wallet.address, state=EnumChannelState.INIT.name) # record the transaction founder_trade = Channel.founder_trade( type=EnumTradeType.TRADE_TYPE_FOUNDER, role=EnumTradeRole.TRADE_ROLE_FOUNDER, asset_type=asset_type, balance=founder_deposit, peer_balance=partner_deposit, commitment=commitment) Channel.add_trade(channel_name, nonce=nonce, **founder_trade) # create founder request message message = FounderMessage.create_message_header( founder, partner, FounderMessage._message_name, channel_name, asset_type, nonce) message_body = { "FounderDeposit": str(founder_deposit), "PartnerDeposit": str(partner_deposit), "Commitment": commitment, } message.update({'MessageBody': message_body}) # Add comments in the messages if comments: message.update({"Comments": comments}) FounderMessage.send(message) return
def json_rpc_method_handler(self, method, params): # if method == "SendRawtransaction": # return transaction.TrinityTransaction.sendrawtransaction(params[0]) if method == "TransactionMessage": LOG.info("<-- {}".format(params)) return MessageList.append(params) # elif method == "FunderTransaction": # return transaction.funder_trans(params) # elif method == "FunderCreate": # return transaction.funder_create(params) # elif method == "RSMCTransaction": # return transaction.rsmc_trans(params) # # elif method == "HTLCTransaction": # return transaction.hltc_trans(params) elif method == "SyncWallet": from wallet import prompt as PR wallet_info = PR.CurrentLiveWallet.wallet_info() return {"MessageType": "SyncWallet", "MessageBody": wallet_info} elif method == "GetChannelState": return { 'MessageType': 'GetChannelState', 'MessageBody': get_channel_via_name(params) } elif method == "GetChannelList": LOG.debug("GetChannelList") from wallet.utils import get_wallet_info if CurrentLiveWallet.Wallet: try: channel_list = query_channel_list( CurrentLiveWallet.Wallet.url) except Exception as e: channel_list = None LOG.error(e) wallet_info = get_wallet_info(CurrentLiveWallet.Wallet) return { "MessageType": "GetChannelList", "MessageBody": { "Channel": channel_list, "Wallet": wallet_info } } else: return { "MessageType": "GetChannelList", "MessageBody": { "Error": "Wallet No Open" } } elif method == "GetPayment": asset_type = params[0] payment = params[1] try: hash_r, rcode = Payment.create_hr() pycode = Payment.generate_payment_code( CurrentLiveWallet.Wallet.url, asset_type, payment, hash_r) Channel.add_payment(None, hash_r, rcode, payment) except Exception as e: LOG.error(e) pycode = None return { "MessageType": "GetPaymentAck", "MessageBody": { "pycode": pycode } } elif method == "GetWalletStatistics": try: statistics_data = APIStatistics.query_statistics(params[0])[0] except Exception as e: LOG.error(e) return { "MessageType": "GetWalletStatisticsAck", "MessageBody": { "Error": "data is null" } } else: return { "MessageType": "GetWalletStatisticsAck", "MessageBody": json.loads(statistics_data.__str__()) }
def trigger_htlc_to_next_jump(self): """""" self.router, _ = self.exclude_wallet_from_router( self.wallet.url, self.router) if not self.check_if_the_last_router(): next_router = self.next_jump LOG.debug('Get Next Router {} from {}'.format( str(next_router), self.router)) if not next_router: raise GoTo( EnumResponseStatus.RESPONSE_ROUTER_WITH_ILLEGAL_NEXT_JUMP, 'Illegal next jump<{}> in router<{}>'.format( next_router, self.router)) # to get channel between current wallet and next jump channel_set = Channel.get_channel(self.wallet.url, next_router, state=EnumChannelState.OPENED) if not (channel_set and channel_set[0]): raise GoTo( EnumResponseStatus.RESPONSE_CHANNEL_NOT_FOUND, 'No OPENED channel is found between {} and {}.'.format( self.wallet.url, next_router)) channel_set = channel_set[0] if channel_set.channel == self.channel_name: LOG.warn('Has reached receiver of HTLC transaction.') return # calculate fee and transfer to next jump fee = TrinityNumber(str(self.get_fee(self.wallet.url))).number payment = self.big_number_calculate(self.payment, fee, False) receiver = next_router HtlcMessage.create(channel_set.channel, self.asset_type, self.wallet.url, receiver, payment, self.hashcode, self.router, current_channel=self.channel_name, comments=self.comments) # record channel of next jump in current htlc trade Channel.update_trade(self.channel_name, self.nonce, channel=channel_set.channel) APIStatistics.update_statistics(self.wallet.address, htlc_free=fee) else: # trigger RResponse Channel.update_payment(self.channel_name, self.hashcode) payment_trade = Channel.query_payment(self.channel_name, self.hashcode) if not (payment_trade and payment_trade[0]): raise GoTo( EnumResponseStatus.RESPONSE_TRADE_HASHR_NOT_FOUND, 'Rcode not found for hashcode<{}>'.format(self.hashcode)) payment_trade = payment_trade[0] try: RResponse.create(self.channel_name, self.asset_type, self.nonce, self.wallet.url, self.sender, self.hashcode, payment_trade.rcode, self.comments) # update the htlc trade history Channel.update_trade(self.channel_name, self.nonce, rcode=payment_trade.rcode) except Exception as error: LOG.exception( 'Failed triggerring to send RResponse for HashR<{}>. Exception: {}' .format(self.hashcode, error))
def create(channel_name, asset_type, sender, receiver, payment, hashcode=None, comments=None): """ :param channel_name: :param asset_type: :param sender: :param receiver: :param payment: :param hashcode: :param comments: :param hlock_to_rsmc: :return: """ # check channel state RsmcMessage.check_asset_type(asset_type) channel = RsmcMessage.check_channel_state(channel_name) payer_address, _, _ = uri_parser(sender) payee_address, _, _ = uri_parser(receiver) asset_type = asset_type.upper() resign_body = None # get nonce in the offline account book nonce = Channel.new_nonce(channel_name) if RsmcMessage._FOUNDER_NONCE > nonce: raise GoTo(EnumResponseStatus.RESPONSE_TRADE_WITH_INCORRECT_NONCE, 'RsmcMessage::create: Incorrect nonce<{}> for channel<{}>'.format(nonce, channel_name)) # to check whether there's a transaction which is needed to resign resign_body, valid_trade = RsmcMessage.create_resign_message_body(sender, channel_name, nonce) # there're one trade need resign, we need adjust the nonce value if valid_trade and valid_trade.state in [EnumTradeState.confirming.name]: payer_balance = valid_trade.balance payee_balance = valid_trade.peer_balance else: # to get channel balance balance = channel.balance payer_balance = balance.get(payer_address).get(asset_type) payee_balance = balance.get(payee_address).get(asset_type) # judge whether it is htlc lock part to rsmc trade or not hlock_to_rsmc = RsmcMessage.is_hlock_to_rsmc(hashcode) _, payer_balance, payee_balance = RsmcBase.calculate_balance_after_payment( payer_balance, payee_balance, payment, hlock_to_rsmc=hlock_to_rsmc ) # sign the trade sign_hashcode, sign_rcode = RsmcMessage.get_rcode(channel_name, hashcode) rsmc_trade = Channel.rsmc_trade( type=EnumTradeType.TRADE_TYPE_RSMC, role=EnumTradeRole.TRADE_ROLE_FOUNDER, asset_type=asset_type, balance=payer_balance, peer_balance=payee_balance, payment=payment, hashcode=sign_hashcode, rcode=sign_rcode ) # create message rsmc_request = RsmcMessage.request(asset_type, payment, payer_balance, payee_balance, hashcode) # has transaction which is needed to be resigned if resign_body: rsmc_request.update({'ResignBody': resign_body}) # start to create message message = RsmcMessage.create_message_header(sender, receiver, RsmcMessage._message_name, channel_name, asset_type, nonce) message.update({'MessageBody': rsmc_request}) if comments: message.update({'Comments': comments}) RsmcMessage.send(message) # record the transaction Channel.add_trade(channel_name, nonce=nonce, **rsmc_trade) APIStatistics.update_statistics(payer_address, rsmc='rsmc') return None
def handle(self): super(RsmcMessage, self).handle() status = EnumResponseStatus.RESPONSE_OK resign_ack = None resign_request = None try: # common check self.check_channel_state(self.channel_name) self.verify() # resign request by peer ?? if self.resign_body: resign_ack, nego_trade = self.handle_resign_body(self.wallet, self.channel_name, self.resign_body) else: # check whether need resign previous transaction resign_request, nego_trade = self.create_resign_message_body(self.wallet.url, self.channel_name, self.nonce) # real nonce nego_nonce = nego_trade and (nego_trade.nonce+1) nonce = nego_nonce or self.nonce rsmc_sign_body = self.response(self.asset_type, self.payment, self.sender_balance, self.receiver_balance, self.rsmc_sign_role, self.hashcode) # record the transaction without any signature sign_hashcode, sign_rcode = self.get_rcode(self.channel_name, self.hashcode) rsmc_trade = Channel.rsmc_trade( type=EnumTradeType.TRADE_TYPE_RSMC, role=EnumTradeRole.TRADE_ROLE_PARTNER, asset_type=self.asset_type, balance=self.receiver_balance, peer_balance=self.sender_balance, payment=self.payment, hashcode=sign_hashcode, rcode=sign_rcode ) # if need resign, just send resign message body if resign_request: # add resign body to message body rsmc_sign_body.update({'ResignBody': resign_request}) else: # check the balance htlc_to_rsmc = self.is_hlock_to_rsmc(self.hashcode) _, payer_balance, payee_balance = self.check_balance( self.channel_name, self.asset_type, self.payer_address, self.sender_balance, self.payee_address, self.receiver_balance, hlock_to_rsmc=htlc_to_rsmc, payment=self.payment ) # sign the trade commitment = RsmcResponsesMessage.sign_content( self.wallet, RsmcMessage._sign_type_list, [self.channel_name, nonce, self.payer_address, payer_balance, self.payee_address, payee_balance, sign_hashcode, sign_rcode] ) # update the rsmc_trade rsmc_trade.update({'commitment': commitment, 'state':EnumTradeState.confirming.name}) # update the message body with signature rsmc_sign_body.update({'Commitment': commitment}) # add resign ack if resign_ack: rsmc_sign_body.update({'ResignBody': resign_ack}) # Response message header response_message = RsmcResponsesMessage.create_message_header( self.receiver, self.sender, RsmcResponsesMessage._message_name, self.channel_name, self.asset_type, self.nonce, nonce ) response_message.update({'MessageBody': rsmc_sign_body}) response_message.update({'Status': status.name}) self.send(response_message) # record the transaction self.record_transaction(nonce, **rsmc_trade) APIStatistics.update_statistics(self.wallet.address, rsmc='rsmc') except TrinityException as error: status = error.reason LOG.exception(error) except Exception as error: status = EnumResponseStatus.RESPONSE_EXCEPTION_HAPPENED LOG.exception('Failed to handle RsmcMessage. Exception: {}'.format(error)) finally: if EnumResponseStatus.RESPONSE_OK != status: # nofify error response RsmcResponsesMessage.send_error_response(self.sender, self.receiver, self.channel_name, self.asset_type, self.nonce, status) return
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) release_invoker, _, _ = uri_parser(uri) APIStatistics.update_statistics( release_invoker, state=EnumChannelState.SETTLED.name) return None nonce = latest_nonce except Exception as error: LOG.exception('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