def handle_partner_response(self): """ :return: response to send to peer """ # local variables need_update_balance = False # handle the resign body firstly resign_ack = None resign_trade = None if self.resign_body: resign_ack, resign_trade = self.handle_resign_body( self.wallet, self.channel_name, self.resign_body) # here if the peer not provide the signature, we need re-calculate the balance based on resign trade if not (self.commitment and self.delay_commitment): _, self.sender_balance, self.receiver_balance = self.calculate_balance_after_payment( resign_trade.balance, resign_trade.peer_balance, self.payment, is_htlc_type=True) # to check whether the negotiated nonce is legal or not self.validate_negotiated_nonce() # create htlc response message htlc_sign_body = self.response(self.sender_balance, self.receiver_balance, self.payment, self.hashcode, 1, self.delay_block) # get some common local variables sign_hashcode, sign_rcode = self.get_default_rcode() payer_balance = int(self.sender_balance) payee_balance = int(self.receiver_balance) payment = int(self.payment) # use this nonce for following message of current transaction nonce = self.nego_nonce or self.nonce # special case when nego-nonce is setting if nonce != self.nonce: # get previous record old_trade = Channel.query_trade(self.channel_name, self.nonce) # generate the htlc trade htlc_trade = Channel.htlc_trade( type=EnumTradeType.TRADE_TYPE_HTLC, role=EnumTradeRole.TRADE_ROLE_FOUNDER, asset_type=self.asset_type, balance=payer_balance, peer_balance=payee_balance, payment=payment, hashcode=self.hashcode, delay_block=old_trade.delay_block, channel=old_trade.channel) # # Update the trade or add new one self.record_transaction(nonce, **htlc_trade) # delete old one if nonce < self.nonce: Channel.delete_trade(self.channel_name, self.nonce) # Get current transaction record current_trade = Channel.query_trade(self.channel_name, nonce) if current_trade.commitment and current_trade.delay_commitment: # it means the htlc has already signed before. commitment = current_trade.commitment delay_commitment = current_trade.delay_commitment else: # start to sign this new transaction and save it # check balance self.check_balance(self.channel_name, self.asset_type, self.payer_address, payer_balance, self.payee_address, payee_balance, is_htcl_type=True, payment=payment) # sign the transaction with 2 parts: rsmc and htlc-locked part commitment = self.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 ]) delay_commitment = self.sign_content( self.wallet, self._sign_type_list, [ self.channel_name, self.payer_address, self.payee_address, int(self.delay_block), int(payment), self.hashcode ], start=5) # update the transaction info Channel.update_trade(self.channel_name, nonce, commitment=commitment, delay_commitment=delay_commitment) # peer has already sign the new transaction with nonce if self.commitment and self.delay_commitment: # check the signature of rsmc part self.check_signature(self.wallet, self.sender_address, type_list=RsmcMessage._sign_type_list, value_list=[ self.channel_name, nonce, self.payer_address, int(self.sender_balance), self.payee_address, int(self.receiver_balance), sign_hashcode, sign_rcode ], signature=self.commitment) # check signature of htlc-lock part self.check_signature(self.wallet, self.sender_address, type_list=self._sign_type_list, value_list=[ self.channel_name, self.payer_address, self.payee_address, int(self.delay_block), int(payment), self.hashcode ], signature=self.delay_commitment) # update this trade confirmed state Channel.update_trade(self.channel_name, nonce, commitment=commitment, delay_commitment=delay_commitment, peer_commitment=self.commitment, peer_delay_commitment=self.delay_commitment, state=EnumTradeState.confirming.name) need_update_balance = True htlc_sign_body.update({ 'Commitment': commitment, 'DelayCommitment': delay_commitment }) # has resign response ?? if resign_ack: htlc_sign_body.update({'ResignBody': resign_ack}) return need_update_balance, htlc_sign_body
def create(cls, channel_name, asset_type, sender, receiver, payment, hashcode, router, current_channel=None, comments=None): """ :param channel_name: :param asset_type: :param sender: :param receiver: :param payment: :param hashcode: :param router: :param current_channel: :param comments: :return: """ cls.check_channel_state(channel_name) cls.check_router(router, hashcode) cls.check_asset_type(asset_type) cls.check_both_urls(sender, receiver) payer_address, _, _ = uri_parser(sender) payee_address, _, _ = uri_parser(receiver) asset_type = asset_type.upper() channel = Channel(channel_name) # get nonce nonce = Channel.new_nonce(channel_name) if HtlcMessage._FOUNDER_NONCE > nonce: raise GoTo( EnumResponseStatus.RESPONSE_TRADE_WITH_INCORRECT_NONCE, 'HtlcMessage::create: Incorrect nonce<{}> for channel<{}>'. format(nonce, channel_name)) # to check whether previous transaction need be resigned or not resign_body, valid_trade = HtlcMessage.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) # To calculate the balance after payment _, payer_balance, payee_balance = HtlcMessage.calculate_balance_after_payment( payer_balance, payee_balance, payment, is_htlc_type=True) # exclude current router from the router end_block_height = cls.get_unlocked_block_height(len(router)) # generate htlc transaction and record it into database later htlc_trade = Channel.htlc_trade(type=EnumTradeType.TRADE_TYPE_HTLC, role=EnumTradeRole.TRADE_ROLE_FOUNDER, asset_type=asset_type, balance=payer_balance, peer_balance=payee_balance, payment=payment, hashcode=hashcode, delay_block=end_block_height) # record the channel if RResponse is triggered if current_channel: htlc_trade.update({'channel': current_channel}) # create message htlc_request = HtlcMessage.request(payer_balance, payee_balance, payment, hashcode, end_block_height) # has transaction which is needed to be resigned if resign_body: htlc_request.update({'ResignBody': resign_body}) # start to create message and send later message = HtlcMessage.create_message_header(sender, receiver, HtlcMessage._message_name, channel_name, asset_type, nonce) message.update({'Router': router}) message.update({'MessageBody': htlc_request}) if comments: message.update({'Comments': comments}) HtlcMessage.send(message) # record the transaction Channel.add_trade(channel_name, nonce=nonce, **htlc_trade) return None
def create(cls, wallet, channel_name, asset_type, tx_nonce, sender, receiver, payment, sender_balance, receiver_balance, hashcode, delay_block, peer_commitment, peer_hlock_commitment, router, next_router, comments=None): """ :param wallet: :param channel_name: :param asset_type: :param tx_nonce: :param sender: :param receiver: :param payment: :param sender_balance: :param receiver_balance: :param hashcode: :param delay_block: :param peer_commitment: :param peer_hlock_commitment: :param router: :param next_router: :param comments: :return: """ # get nonce from latest trade _, nonce = HtlcResponsesMessage.check_nonce(tx_nonce, channel_name) # start to verify balance payer_address, _, _ = uri_parser(sender) payee_address, _, _ = uri_parser(receiver) asset_type = asset_type.upper() # check balance _, payer_balance, payee_balance = HtlcResponsesMessage.check_balance( channel_name, asset_type, payer_address, sender_balance, payee_address, receiver_balance, is_htcl_type=True, payment=payment) # 2 parts in htlc message: conclusive and inconclusive part sign_hashcode, sign_rcode = cls.get_default_rcode() rsmc_commitment = HtlcResponsesMessage.sign_content( typeList=RsmcMessage._sign_type_list, valueList=[ channel_name, nonce, payer_address, payer_balance, payee_address, payee_balance, sign_hashcode, sign_rcode ], privtKey=wallet._key.private_key_string) hlock_commitment = HtlcResponsesMessage.sign_content( start=5, typeList=HtlcResponsesMessage._sign_type_list, valueList=[ channel_name, payer_address, payee_address, delay_block, int(payment), hashcode ], privtKey=wallet._key.private_key_string) # # ToDo: need re-sign all unconfirmed htlc message later htlc_trade = Channel.htlc_trade( type=EnumTradeType.TRADE_TYPE_HTLC, role=EnumTradeRole.TRADE_ROLE_PARTNER, asset_type=asset_type, balance=payee_balance, peer_balance=payer_balance, payment=payment, hashcode=hashcode, delay_block=delay_block, commitment=rsmc_commitment, peer_commitment=peer_commitment, delay_commitment=hlock_commitment, peer_delay_commitment=peer_hlock_commitment, channel=channel_name) Channel.add_trade(channel_name, nonce=nonce, **htlc_trade) # create message message = HtlcResponsesMessage.create_message_header( receiver, sender, HtlcResponsesMessage._message_name, channel_name, asset_type, tx_nonce) message = message.message_header message.update({ 'Router': router, 'Next': next_router, }) message_body = { 'SenderBalance': payer_balance, 'ReceiverBalance': payee_balance, 'Commitment': rsmc_commitment, 'Payment': payment, 'DelayBlock': delay_block, 'DelayCommitment': hlock_commitment, 'HashR': hashcode } # orgnize the message message.update({'MessageBody': message_body}) message.update({'Status': EnumResponseStatus.RESPONSE_OK.name}) if comments: message.update({'Comments': comments}) # Before the response ok, we could update the channel balance HtlcResponsesMessage.update_balance_for_channel(channel_name, asset_type, payer_address, payee_address, payment, is_htlc_type=True) # send response ok message to peer HtlcResponsesMessage.send(message) return
def handle(self): super(HtlcMessage, self).handle() status = EnumResponseStatus.RESPONSE_OK resign_ack = None resign_request = None nonce = None try: # common check self.check_channel_state(self.channel_name) self.check_router(self.router, self.hashcode) 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 # some local variables to save later htlc_sign_body = self.response(self.sender_balance, self.receiver_balance, self.payment, self.hashcode, self.htlc_sign_role, self.delay_block) htlc_trade = Channel.htlc_trade( type=EnumTradeType.TRADE_TYPE_HTLC, role=EnumTradeRole.TRADE_ROLE_PARTNER, asset_type=self.asset_type, balance=self.receiver_balance, peer_balance=self.sender_balance, payment=self.payment, hashcode=self.hashcode, delay_block=self.delay_block, channel=self.channel_name) # if need resign, just send resign message body sign_hashcode, sign_rcode = self.get_default_rcode() if resign_request: # add resign body to response body htlc_sign_body.update({'ResignBody': resign_request}) else: # check the balance _, 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, is_htcl_type=True, payment=self.payment) # sign the transaction # 2 parts in htlc message: conclusive and inconclusive part rsmc_commitment = HtlcMessage.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 ]) hlock_commitment = HtlcMessage.sign_content( self.wallet, self._sign_type_list, [ self.channel_name, self.payer_address, self.payee_address, int(self.delay_block), int(self.payment), self.hashcode ], start=5) # update the htlc_trade htlc_trade.update({ 'commitment': rsmc_commitment, 'delay_commitment': hlock_commitment, 'state': EnumTradeState.confirming.name }) # update the message body with signature htlc_sign_body.update({ 'Commitment': rsmc_commitment, 'DelayCommitment': hlock_commitment }) # add resign ack if resign_ack: htlc_sign_body.update({'ResignBody': resign_ack}) # Response message header response_message = HtlcResponsesMessage.create_message_header( self.receiver, self.sender, HtlcResponsesMessage._message_name, self.channel_name, self.asset_type, self.nonce, nonce) response_message.update({'MessageBody': htlc_sign_body}) response_message.update({'Router': self.router}) response_message.update({'Status': status.name}) self.send(response_message) # record the transaction self.record_transaction(nonce, **htlc_trade) except TrinityException as error: LOG.exception(error) status = error.reason except Exception as error: LOG.exception( 'Failed to handle Htlc message for channel<{}>, HashR<{}>. Exception:{}' .format(self.channel_name, self.hashcode, error)) status = EnumResponseStatus.RESPONSE_EXCEPTION_HAPPENED finally: # failed operation if EnumResponseStatus.RESPONSE_OK != status: # send error response HtlcResponsesMessage.send_error_response( self.sender, self.receiver, self.channel_name, self.asset_type, self.nonce, status) # delete some related resources self.rollback_resource(self.channel_name, nonce or self.nonce, self.payment, status.name) return
def create(cls, wallet, channel_name, asset_type, sender, receiver, payment, hashcode, router, next_router, current_channel=None, comments=None): """ :param channel_name: :param wallet: :param sender: :param receiver: :param asset_type: :param payment: :param nonce: :param hashcode: :param router: :param next_router: :param comments: :return: """ cls.check_channel_state(channel_name) cls.check_router(router, hashcode) cls.check_asset_type(asset_type) cls.check_both_urls(sender, receiver) cls.check_router(router, hashcode) # get nonce nonce = Channel.new_nonce(channel_name) if HtlcMessage._FOUNDER_NONCE > nonce: raise GoTo( EnumResponseStatus.RESPONSE_TRADE_WITH_INCORRECT_NONCE, 'HtlcMessage::create: Incorrect nonce<{}> for channel<{}>'. format(nonce, channel_name)) # get sender & receiver address from sender or receiver payer_address, _, _ = uri_parser(sender) payee_address, _, _ = uri_parser(receiver) asset_type = asset_type.upper() # check whether the balance is enough or not channel = Channel(channel_name) balance = channel.balance _, payer_balance, payee_balance = HtlcMessage.calculate_balance_after_payment( balance.get(payer_address, {}).get(asset_type), balance.get(payee_address, {}).get(asset_type), payment, is_htlc_type=True) # calculate the end block height jumps_index = HtlcMessage.this_jump(sender, router) end_block_height = cls.get_locked_block_height(len(router), jumps_index) # Htlc is made up of 2 parts: rsmc and hlock part sign_hashcode, sign_rcode = cls.get_default_rcode() rsmc_commitment = HtlcMessage.sign_content( typeList=RsmcMessage._sign_type_list, valueList=[ channel_name, nonce, payer_address, payer_balance, payee_address, payee_balance, sign_hashcode, sign_rcode ], privtKey=wallet._key.private_key_string) # hlock parts hlock_commitment = HtlcMessage.sign_content( start=5, typeList=HtlcMessage._sign_type_list, valueList=[ channel_name, payer_address, payee_address, end_block_height, int(payment), hashcode ], privtKey=wallet._key.private_key_string) # # add trade to database htlc_trade = Channel.htlc_trade(type=EnumTradeType.TRADE_TYPE_HTLC, role=EnumTradeRole.TRADE_ROLE_FOUNDER, asset_type=asset_type, balance=payer_balance, peer_balance=payee_balance, payment=payment, hashcode=hashcode, delay_block=end_block_height, commitment=rsmc_commitment, delay_commitment=hlock_commitment) if current_channel: htlc_trade.update({'channel': current_channel}) Channel.add_trade(channel_name, nonce=nonce, **htlc_trade) # generate the messages message_body = { 'SenderBalance': payer_balance, 'ReceiverBalance': payee_balance, 'Commitment': rsmc_commitment, 'Payment': payment, 'DelayBlock': end_block_height, 'DelayCommitment': hlock_commitment, 'HashR': hashcode } message = HtlcMessage.create_message_header(sender, receiver, HtlcMessage._message_name, channel_name, asset_type, nonce) message = message.message_header message.update({ 'Router': router, 'Next': next_router, }) message.update({'MessageBody': message_body}) if comments: message.update({'Comments': comments}) HtlcMessage.send(message) return None