Beispiel #1
0
    def notify_rcode_to_next_peer(self, next_channel):
        """

        :param next_channel:
        :return:
        """
        peer = None
        try:
            # original payer is found
            if not next_channel:
                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 next_channel != htlc_trade.channel:
                LOG.error('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)
            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)
        except Exception as error:
            LOG.error('Failed to notify RCode<{}> to peer<{}>'.format(
                self.rcode, peer))
Beispiel #2
0
    def create(wallet, receiver, channel_name, asset_type):
        """

        :param channel_name:
        :param wallet:
        :param sender:
        :param receiver:
        :param asset_type:
        :return:
        """
        sender = wallet.url
        channel = Channel(channel_name)
        nonce = SettleMessage._SETTLE_NONCE
        asset_type = asset_type.upper()

        sender_address, _, _, = uri_parser(sender)
        receiver_address, _, _, = uri_parser(receiver)

        balance = channel.balance
        sender_balance = balance.get(sender_address, {}).get(asset_type)
        receiver_balance = balance.get(receiver_address, {}).get(asset_type)

        commitment = SettleMessage.sign_content(
            typeList=SettleMessage._sign_type_list,
            valueList=[
                channel_name, nonce, sender_address,
                int(sender_balance), receiver_address,
                int(receiver_balance)
            ],
            privtKey=wallet._key.private_key_string)

        # add trade to database
        settle_trade = Channel.settle_trade(
            type=EnumTradeType.TRADE_TYPE_QIUCK_SETTLE,
            role=EnumTradeRole.TRADE_ROLE_FOUNDER,
            asset_type=asset_type,
            balance=sender_balance,
            peer_balance=receiver_balance,
            commitment=commitment)
        SettleMessage.add_or_update_quick_settle_trade(
            channel_name, EnumTradeRole.TRADE_ROLE_FOUNDER, **settle_trade)

        # create settle request message
        message = SettleMessage.create_message_header(
            sender, receiver, SettleMessage._message_name, channel_name,
            asset_type, nonce)
        message = message.message_header
        message_body = {
            "Commitment": commitment,
            "SenderBalance": sender_balance,
            "ReceiverBalance": receiver_balance
        }
        message.update({'MessageBody': message_body})

        Message.send(message)

        return
Beispiel #3
0
    def check_channel_state(cls, channel_name):
        channel = Channel(channel_name)

        if not channel.is_opened:
            raise GoTo(
                EnumResponseStatus.RESPONSE_CHANNEL_NOT_OPENED,
                'Channel is not OPENED. Current tate<{}>'.format(
                    channel.state))

        return channel
Beispiel #4
0
    def check_payment(cls, channel_name, address, asset_type, payment):
        """

        :param channel_name:
        :param address:
        :param asset_type:
        :param payment:
        :return:
        """
        channel_balance = Channel(channel_name).balance
        balance = channel_balance.get(address).get(asset_type.upper())

        if not 0 < int(payment) <= int(balance):
            raise GoTo(
                EnumResponseStatus.
                RESPONSE_TRADE_NO_ENOUGH_BALANCE_FOR_PAYMENT,
                'Invalid payment<{}>, payer balance<{}>'.format(
                    payment, balance))

        return True, int(balance)
Beispiel #5
0
    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))
Beispiel #6
0
    def update_balance_for_channel(cls,
                                   channel_name,
                                   asset_type,
                                   payer_address,
                                   payee_address,
                                   payment,
                                   is_hlock_to_rsmc=False,
                                   is_htlc_type=False):
        """

        :param channel_name:
        :param payer_address:
        :param payee_address:
        :param asset_type:
        :param payment:
        :param is_hlock_to_rsmc:
        :return:
        """
        channel = Channel(channel_name)

        try:
            asset_type = asset_type.upper()
            channel_balance = channel.balance
            channel_hlock = channel.hlock

            # calculate the left balance
            payer_balance = channel_balance.get(payer_address).get(asset_type)
            payee_balance = channel_balance.get(payee_address).get(asset_type)

            if is_hlock_to_rsmc:
                payer_hlock = channel_hlock.get(payer_address).get(asset_type)
                payer_hlock = cls.big_number_calculate(payer_hlock, payment,
                                                       False)
                if 0 > payer_hlock:
                    raise GoTo(
                        EnumResponseStatus.
                        RESPONSE_TRADE_LOCKED_ASSET_LESS_THAN_PAYMENT,
                        'Why here? Payment<{}> should less than locked asset'.
                        format(payment))
                channel_hlock.update(
                    {payer_address: {
                        asset_type: str(payer_hlock)
                    }})
                payee_balance = cls.big_number_calculate(
                    payee_balance, payment)
            elif is_htlc_type:
                payer_hlock = channel_hlock.get(payer_address).get(asset_type)
                payer_hlock = cls.big_number_calculate(payer_hlock, payment)
                channel_hlock.update(
                    {payer_address: {
                        asset_type: str(payer_hlock)
                    }})
                payer_balance = cls.big_number_calculate(
                    payer_balance, payment, False)
            else:
                payer_balance = cls.big_number_calculate(
                    payer_balance, payment, False)
                payee_balance = cls.big_number_calculate(
                    payee_balance, payment)

            if int(payer_balance) >= 0 and int(payee_balance) >= 0:
                Channel.update_channel(channel_name,
                                       balance={
                                           payer_address: {
                                               asset_type: str(payer_balance)
                                           },
                                           payee_address: {
                                               asset_type: str(payee_balance)
                                           }
                                       },
                                       hlock=channel_hlock)
            else:
                raise GoTo(
                    EnumResponseStatus.
                    RESPONSE_TRADE_NO_ENOUGH_BALANCE_FOR_PAYMENT,
                    'Payer has not enough balance for this payment<{}>'.format(
                        payment))

        except Exception as error:
            raise GoTo(
                EnumResponseStatus.RESPONSE_TRADE_BALANCE_UPDATE_FAILED,
                'Update channel<{}> balance error. payment<{}>. Exception: {}'.
                format(channel_name, payment, error))
Beispiel #7
0
    def __init__(self, channel_name, event_type, is_event_founder=True):
        super(ChannelOfflineEventBase, self).__init__(channel_name, event_type,
                                                      is_event_founder)

        self.channel_name = channel_name
        self.channel = Channel(channel_name)
Beispiel #8
0
    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 __init__(self, channel_name, event_type, wallet_address, is_event_founder=True):
        super(ChannelEventBase, self).__init__(channel_name, event_type, is_event_founder)

        self.channel_name = channel_name
        self.channel = Channel(channel_name)
        self.wallet_address = wallet_address
Beispiel #10
0
    def handle_resign_body(cls, wallet, channel_name, resign_body):
        """"""
        if not resign_body:
            return None

        if not (wallet and channel_name):
            raise GoTo(
                EnumResponseStatus.RESPONSE_ILLEGAL_INPUT_PARAMETER,
                'Void wallet<{}> or Illegal channel name<{}>'.format(
                    wallet, channel_name))

        resign_nonce = int(resign_body.get('Nonce'))
        update_channel_db = {}
        update_trade_db = {}

        # get transaction by the resign_nonce
        resign_trade = Channel.query_trade(channel_name, resign_nonce)
        asset_type = resign_trade.asset_type

        channel = Channel(channel_name)
        self_address, _, _ = uri_parser(wallet.url)
        peer_address, _, _ = uri_parser(channel.peer_uri(wallet.url))

        # Is this wallet sender side of this trade ?????
        if EnumTradeRole.TRADE_ROLE_PARTNER.name == resign_trade.role:
            payer_address = peer_address
            payee_address = self_address
            payer_balance = resign_trade.peer_balance
            payee_balance = resign_trade.balance
        elif EnumTradeRole.TRADE_ROLE_FOUNDER.name == resign_trade.role:
            payer_address = self_address
            payee_address = peer_address
            payer_balance = resign_trade.balance
            payee_balance = resign_trade.peer_balance
        else:
            raise GoTo(
                EnumResponseStatus.RESPONSE_DATABASE_ERROR_TRADE_ROLE,
                'Error trade role<{}> for nonce<{}>'.format(
                    resign_trade.role, resign_nonce))

        if EnumTradeType.TRADE_TYPE_HTLC.name == resign_trade.type:
            rsmc_sign_hashcode, rsmc_sign_rcode = cls.get_default_rcode()
        elif EnumTradeType.TRADE_TYPE_RSMC.name == resign_trade.type:
            rsmc_sign_hashcode, rsmc_sign_rcode = resign_trade.hashcode, resign_trade.rcode
        else:
            raise GoTo(
                EnumResponseStatus.RESPONSE_TRADE_RESIGN_NOT_SUPPORTED_TYPE,
                'Only support HTLC and RSMC resign. Current transaction type: {}'
                .format(resign_trade.type))

        rsmc_list = [
            channel_name, resign_nonce, payer_address,
            int(payer_balance), payee_address,
            int(payee_balance), rsmc_sign_hashcode, rsmc_sign_rcode
        ]

        # self commitment is None
        if not resign_trade.commitment:
            commitment = cls.sign_content(wallet, cls._rsmc_sign_type_list,
                                          rsmc_list)
            update_trade_db.update({'commitment': commitment})

        # self lock commitment is none
        htlc_list = None
        if EnumTradeType.TRADE_TYPE_HTLC.name == resign_trade.type:
            htlc_list = [
                channel_name, payer_address, payee_address,
                int(resign_trade.delay_block),
                int(resign_trade.payment), resign_trade.hashcode
            ]
            delay_commitment = cls.sign_content(wallet,
                                                cls._htlc_sign_type_list,
                                                htlc_list)
            update_trade_db.update({'delay_commitment': delay_commitment})

        # check whether the trade need update or not
        if resign_trade.state not in [
                EnumTradeState.confirmed.name,
                EnumTradeState.confirmed_onchain.name
        ]:
            peer_commitment = resign_body.get('Commitment')
            peer_delay_commitment = resign_body.get('DelayCommitment')

            # check peer signature
            cls.check_signature(wallet, peer_address, cls._rsmc_sign_type_list,
                                rsmc_list, peer_commitment)

            # update transaction and channel balance
            update_trade_db.update({'peer_commitment': peer_commitment})
            update_channel_db = {
                'balance': {
                    payer_address: {
                        asset_type: payer_balance
                    },
                    payee_address: {
                        asset_type: payee_balance
                    }
                }
            }

            # check the htlc part signature
            if EnumTradeType.TRADE_TYPE_HTLC.name == resign_trade.type:
                cls.check_signature(wallet, peer_address,
                                    cls._htlc_sign_type_list, htlc_list,
                                    peer_delay_commitment)
                update_trade_db.update(
                    {'peer_delay_commitment': peer_delay_commitment})
                update_trade_db.update(
                    {'state': EnumTradeState.confirming.name})

                # need update the hlock part
                if payer_address == self_address:
                    channel = Channel(channel_name)
                    channel_hlock = channel.hlock
                    if channel_hlock and channel_hlock.get(payer_address):
                        payer_hlock = int(resign_trade.payment) + int(
                            channel_hlock.get(payer_address).get(asset_type))
                        channel_hlock.update(
                            {payer_address: {
                                asset_type: str(payer_hlock)
                            }})
                        update_channel_db.update({'hlock': channel_hlock})
            else:  # RSMC type transaction
                update_trade_db.update(
                    {'state': EnumTradeState.confirmed.name})

            # update transaction
            Channel.update_trade(channel_name, resign_nonce, **update_trade_db)

            # update Channel balance to new one
            Channel.update_channel(channel_name, **update_channel_db)

        # return resign message body
        return cls.create_resign_message_body(wallet.url, channel_name,
                                              resign_nonce, True)
Beispiel #11
0
    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