def validate_params(self):
        UserUtil.verified_phone_and_email(self.event)
        # single
        # check price type is integer or decimal
        try:
            self.params['price'] = int(self.params['price'])
        except ValueError:
            raise ValidationError('Price must be integer')

        # check price value is not decimal
        price = self.params['price'] / 10**18
        if price.is_integer() is False:
            raise ValidationError('Decimal value is not allowed')
        validate(self.params, self.get_schema())

        # relation
        DBUtil.validate_article_existence(self.dynamodb,
                                          self.params['article_id'],
                                          status='public')
        DBUtil.validate_latest_price(self.dynamodb, self.params['article_id'],
                                     self.params['price'])
        DBUtil.validate_not_purchased(
            self.dynamodb, self.params['article_id'],
            self.event['requestContext']['authorizer']['claims']
            ['cognito:username'])
Esempio n. 2
0
 def test_validate_latest_price_ng(self):
     with self.assertRaises(ValidationError):
         price = 1000 * (10 ** 18)
         DBUtil.validate_latest_price(
             self.dynamodb,
             'testid000003',
             price
         )
 def test_validate_latest_price_ok(self):
     price = 1 * (10**18)
     self.assertTrue(
         DBUtil.validate_latest_price(self.dynamodb, 'testid000003', price))
Esempio n. 4
0
 def test_validate_latest_price_ok(self):
     price = int(1 * (10 ** 18) * Decimal(9) / Decimal(10))
     self.assertTrue(DBUtil.validate_latest_price(self.dynamodb, 'testid000003', price))
    def exec_main_proc(self):
        ################
        # get parameter
        ################
        # get article info
        article_info_table = self.dynamodb.Table(
            os.environ['ARTICLE_INFO_TABLE_NAME'])
        article_info = article_info_table.get_item(
            Key={
                'article_id': self.params['article_id']
            }).get('Item')
        # purchase article
        paid_articles_table = self.dynamodb.Table(
            os.environ['PAID_ARTICLES_TABLE_NAME'])
        paid_status_table = self.dynamodb.Table(
            os.environ['PAID_STATUS_TABLE_NAME'])
        # eth_address
        article_user_eth_address = UserUtil.get_private_eth_address(
            self.cognito, article_info['user_id'])
        user_eth_address = self.event['requestContext']['authorizer'][
            'claims']['custom:private_eth_address']
        # transaction_count
        transaction_count = PrivateChainUtil.get_transaction_count(
            user_eth_address)

        ################
        # validation
        ################
        # does not purchase same user's article
        user_id = self.event['requestContext']['authorizer']['claims'][
            'cognito:username']
        if article_info['user_id'] == user_id:
            raise ValidationError('Can not purchase own article')
        # validate raw_transaction
        # purchase
        purchase_data = PrivateChainUtil.get_data_from_raw_transaction(
            self.params['purchase_signed_transaction'], transaction_count)
        PrivateChainUtil.validate_erc20_transfer_data(
            purchase_data, article_user_eth_address)
        # burn
        transaction_count = PrivateChainUtil.increment_transaction_count(
            transaction_count)
        burn_data = PrivateChainUtil.get_data_from_raw_transaction(
            self.params['burn_signed_transaction'], transaction_count)
        PrivateChainUtil.validate_erc20_transfer_data(
            burn_data, '0x' + os.environ['BURN_ADDRESS'])

        # burn 量が正しいこと
        purchase_value = int(purchase_data[72:], 16)
        burn_value = int(burn_data[72:], 16)
        calc_burn_value = int(Decimal(purchase_value) / Decimal(9))
        if burn_value != calc_burn_value:
            raise ValidationError('burn_value is invalid.')

        # purchase_value が記事で指定されている金額に基づいた量が設定されていること
        DBUtil.validate_latest_price(self.dynamodb, self.params['article_id'],
                                     purchase_value)

        ################
        # purchase
        ################
        sort_key = TimeUtil.generate_sort_key()
        # 多重リクエストによる不必要なレコード生成を防ぐためにpaid_statusレコードを生成
        self.__create_paid_status(paid_status_table, user_id)
        # 購入のトランザクション処理
        purchase_transaction = PrivateChainUtil.send_raw_transaction(
            self.params['purchase_signed_transaction'])
        # 購入記事データを作成
        self.__create_paid_article(paid_articles_table, article_info,
                                   purchase_transaction, sort_key)
        # プライベートチェーンへのポーリングを行いトランザクションの承認状態を取得
        transaction_status = self.__polling_to_private_chain(
            purchase_transaction)
        # トランザクションの承認状態をpaid_articleとpaid_statusに格納
        self.__update_transaction_status(article_info, paid_articles_table,
                                         transaction_status, sort_key,
                                         paid_status_table, user_id)

        # 購入のトランザクションが成功した時のみバーンのトランザクションを発行する
        if transaction_status == 'done':
            try:
                # 購入に成功した場合、著者の未読通知フラグをTrueにする
                self.__update_unread_notification_manager(
                    article_info['user_id'])
                # 著者へ通知を作成
                self.__notify_author(article_info, user_id)
                # バーンのトランザクション処理
                burn_transaction = PrivateChainUtil.send_raw_transaction(
                    self.params['burn_signed_transaction'])
                # バーンのトランザクションを購入テーブルに格納
                self.__add_burn_transaction_to_paid_article(
                    burn_transaction, paid_articles_table, article_info,
                    sort_key)
            except Exception as err:
                logging.fatal(err)
                traceback.print_exc()
        # 記事購入者へは購入処理中の場合以外で通知を作成
        if transaction_status == 'done' or transaction_status == 'fail':
            self.__update_unread_notification_manager(user_id)
            self.__notify_purchaser(article_info, user_id, transaction_status)

        return {
            'statusCode': 200,
            'body': json.dumps({'status': transaction_status})
        }