def get_ready_deposits() -> List[Transaction]:
     pending_deposits = Transaction.objects.filter(
         status__in=[
             Transaction.STATUS.pending_user_transfer_start,
             Transaction.STATUS.pending_external,
         ],
         kind=Transaction.KIND.deposit,
     )
     ready_transactions = rri.poll_pending_deposits(pending_deposits)
     for transaction in ready_transactions:
         if transaction.kind != Transaction.KIND.deposit:
             raise ValueError(
                 "A non-deposit Transaction was returned from poll_pending_deposits()"
             )
         elif transaction.amount_in is None:
             raise ValueError(
                 "poll_pending_deposits() did not assign a value to the "
                 "amount_in field of a Transaction object returned"
             )
         elif transaction.amount_fee is None:
             if registered_fee_func is calculate_fee:
                 transaction.amount_fee = calculate_fee(
                     {
                         "amount": transaction.amount_in,
                         "operation": settings.OPERATION_DEPOSIT,
                         "asset_code": transaction.asset.code,
                     }
                 )
             else:
                 transaction.amount_fee = Decimal(0)
             transaction.save()
     return ready_transactions
Esempio n. 2
0
 def get_ready_deposits(cls) -> List[Transaction]:
     pending_deposits = Transaction.objects.filter(
         status__in=[
             Transaction.STATUS.pending_user_transfer_start,
             Transaction.STATUS.pending_external,
         ],
         kind=Transaction.KIND.deposit,
         pending_execution_attempt=False,
     ).select_for_update()
     with django.db.transaction.atomic():
         ready_transactions = rri.poll_pending_deposits(pending_deposits)
         Transaction.objects.filter(
             id__in=[t.id for t in ready_transactions]).update(
                 pending_execution_attempt=True)
     verified_ready_transactions = []
     for transaction in ready_transactions:
         # refresh from DB to pull pending_execution_attempt value and to ensure invalid
         # values were not assigned to the transaction in rri.poll_pending_deposits()
         transaction.refresh_from_db()
         if transaction.kind != transaction.KIND.deposit:
             cls.handle_error(
                 transaction,
                 "poll_pending_deposits() returned a non-deposit transaction",
             )
             continue
         if transaction.amount_in is None:
             cls.handle_error(
                 transaction,
                 "poll_pending_deposits() did not assign a value to the "
                 "amount_in field of a Transaction object returned",
             )
             continue
         elif transaction.amount_fee is None:
             if registered_fee_func is calculate_fee:
                 try:
                     transaction.amount_fee = calculate_fee({
                         "amount":
                         transaction.amount_in,
                         "operation":
                         settings.OPERATION_DEPOSIT,
                         "asset_code":
                         transaction.asset.code,
                     })
                 except ValueError as e:
                     cls.handle_error(transaction, str(e))
                     continue
             else:
                 transaction.amount_fee = Decimal(0)
             transaction.save()
         verified_ready_transactions.append(transaction)
     return verified_ready_transactions
Esempio n. 3
0
 def execute_deposits(cls):
     """
     Right now, execute_deposits assumes all pending deposits are SEP-6 or 24
     transactions. This may change in the future if Polaris adds support for
     another SEP that checks for incoming deposits.
     """
     module = sys.modules[__name__]
     pending_deposits = Transaction.objects.filter(
         kind=Transaction.KIND.deposit,
         status=Transaction.STATUS.pending_user_transfer_start,
     )
     try:
         ready_transactions = rri.poll_pending_deposits(pending_deposits)
     except Exception:  # pragma: no cover
         # We don't know if poll_pending_deposits() will raise an exception
         # every time its called, but we're going to assume it was a special
         # case and allow the process to continue running by returning instead
         # of re-raising the error. The anchor should see the log messages and
         # fix the issue if it is reoccurring.
         logger.exception(
             "poll_pending_deposits() threw an unexpected exception")
         return
     if ready_transactions is None:
         raise CommandError(
             "poll_pending_deposits() returned None. "
             "Ensure is returns a list of transaction objects.")
     for transaction in ready_transactions:
         if module.TERMINATE:
             break
         try:
             success = execute_deposit(transaction)
         except ValueError as e:
             logger.error(str(e))
             continue
         if success:
             # Get updated status
             transaction.refresh_from_db()
             try:
                 rdi.after_deposit(transaction)
             except Exception:  # pragma: no cover
                 # Same situation as poll_pending_deposits(), we should assume
                 # this won't happen every time, so we don't stop the loop.
                 logger.exception(
                     "after_deposit() threw an unexpected exception")
    def execute_deposits(cls):
        module = sys.modules[__name__]
        pending_deposits = Transaction.objects.filter(
            status__in=[
                Transaction.STATUS.pending_user_transfer_start,
                Transaction.STATUS.pending_external,
            ],
            kind=Transaction.KIND.deposit,
        )
        try:
            ready_transactions = rri.poll_pending_deposits(pending_deposits)
        except Exception:  # pragma: no cover
            logger.exception("poll_pending_deposits() threw an unexpected exception")
            return
        if ready_transactions is None:
            raise CommandError(
                "poll_pending_deposits() returned None. "
                "Ensure is returns a list of transaction objects."
            )
        for transaction in ready_transactions:
            if module.TERMINATE:
                break
            elif transaction.kind != Transaction.KIND.deposit:
                raise CommandError(
                    "A non-deposit Transaction was returned from poll_pending_deposits()"
                )
            elif transaction.amount_in is None:
                raise CommandError(
                    "poll_pending_deposits() did not assign a value to the "
                    "amount_in field of a Transaction object returned"
                )
            elif transaction.amount_fee is None:
                if registered_fee_func is calculate_fee:
                    transaction.amount_fee = calculate_fee(
                        {
                            "amount": transaction.amount_in,
                            "operation": settings.OPERATION_DEPOSIT,
                            "asset_code": transaction.asset.code,
                        }
                    )
                else:
                    transaction.amount_fee = Decimal(0)
            logger.info("calling get_or_create_transaction_destination_account()")
            try:
                (
                    _,
                    created,
                    pending_trust,
                ) = get_or_create_transaction_destination_account(transaction)
            except RuntimeError as e:
                transaction.status = Transaction.STATUS.error
                transaction.status_message = str(e)
                transaction.save()
                logger.error(transaction.status_message)
                continue

            # Transaction.status == pending_trust, wait for client
            # to add trustline for asset to send
            if created or pending_trust:
                logger.info(
                    f"destination account is pending_trust for transaction {transaction.id}"
                )
                if (
                    pending_trust
                    and transaction.status != Transaction.STATUS.pending_trust
                ):
                    transaction.status = Transaction.STATUS.pending_trust
                    transaction.save()
                continue

            if check_for_multisig(transaction):
                # Now Polaris waits for signatures to be collected by the anchor
                continue

            cls.execute_deposit(transaction)

        ready_multisig_transactions = Transaction.objects.filter(
            kind=Transaction.KIND.deposit,
            status=Transaction.STATUS.pending_anchor,
            pending_signatures=False,
            envelope_xdr__isnull=False,
        )
        for t in ready_multisig_transactions:
            cls.execute_deposit(t)
    def execute_deposits(cls):
        module = sys.modules[__name__]
        pending_deposits = Transaction.objects.filter(
            status__in=[
                Transaction.STATUS.pending_user_transfer_start,
                Transaction.STATUS.pending_external,
            ],
            kind=Transaction.KIND.deposit,
        )
        try:
            ready_transactions = rri.poll_pending_deposits(pending_deposits)
        except Exception:  # pragma: no cover
            logger.exception("poll_pending_deposits() threw an unexpected exception")
            return
        if ready_transactions is None:
            raise CommandError(
                "poll_pending_deposits() returned None. "
                "Ensure is returns a list of transaction objects."
            )
        for transaction in ready_transactions:
            if module.TERMINATE:
                break
            elif transaction.kind != Transaction.KIND.deposit:
                raise CommandError(
                    "A non-deposit Transaction was returned from poll_pending_deposits()"
                )
            elif transaction.amount_in is None:
                raise CommandError(
                    "poll_pending_deposits() did not assign a value to the "
                    "amount_in field of a Transaction object returned"
                )
            elif transaction.amount_fee is None:
                if registered_fee_func is calculate_fee:
                    transaction.amount_fee = calculate_fee(
                        {
                            "amount": transaction.amount_in,
                            "operation": settings.OPERATION_DEPOSIT,
                            "asset_code": transaction.asset.code,
                        }
                    )
                else:
                    transaction.amount_fee = Decimal(0)
            try:
                (
                    _,
                    created,
                    pending_trust,
                ) = get_or_create_transaction_destination_account(transaction)
            except RuntimeError as e:
                transaction.status = Transaction.STATUS.error
                transaction.status_message = str(e)
                transaction.save()
                logger.error(transaction.status_message)
                continue

            if (
                created or pending_trust
            ) and not transaction.claimable_balance_supported:
                # after checking/creating the account, we discovered the transaction
                # doesn't have a trustline.
                # And the transaction is does not support claimable balances
                # Transaction.status is definitely not
                # pending_trust yet because only the transactions with these statuses
                # are queried:
                # - Transaction.STATUS.pending_user_transfer_start
                # - Transaction.STATUS.pending_external
                logger.info(
                    f"destination account is pending_trust for transaction {transaction.id}"
                )
                transaction.status = Transaction.STATUS.pending_trust
                transaction.save()
                continue
            elif check_for_multisig(transaction):
                # We still have to check if the transaction requires additional
                # signatures.
                # If so we want to skip current transaction's execute_deposit call
                continue
            cls.execute_deposit(transaction)

        ready_multisig_transactions = Transaction.objects.filter(
            kind=Transaction.KIND.deposit,
            status=Transaction.STATUS.pending_anchor,
            pending_signatures=False,
            envelope_xdr__isnull=False,
        )
        for t in ready_multisig_transactions:
            cls.execute_deposit(t)