Example #1
0
def test_poll_pending_deposits_bad_integration(
    client,
    acc1_usd_deposit_transaction_factory,
    acc1_usd_withdrawal_transaction_factory,
):
    # execute_deposits() queries for pending deposits
    acc1_usd_deposit_transaction_factory()
    # integration returns withdraw transaction
    withdrawal_transaction = acc1_usd_withdrawal_transaction_factory()
    rri.poll_pending_deposits = Mock(return_value=[withdrawal_transaction])
    logger.error = Mock()

    with pytest.raises(ValueError):
        PendingDeposits.get_ready_deposits()
Example #2
0
def test_poll_pending_deposits_bad_integration(
    client,
    acc1_usd_deposit_transaction_factory,
    acc1_usd_withdrawal_transaction_factory,
):
    # execute_deposits() queries for pending deposits
    acc1_usd_deposit_transaction_factory()
    # integration returns withdraw transaction
    withdrawal_transaction = acc1_usd_withdrawal_transaction_factory()
    rri.poll_pending_deposits = Mock(return_value=[withdrawal_transaction])
    logger.error = Mock()

    PendingDeposits.get_ready_deposits()

    withdrawal_transaction.refresh_from_db()
    assert withdrawal_transaction.status == Transaction.STATUS.error
Example #3
0
def test_deposit_stellar_success(acc1_usd_deposit_transaction_factory):
    """
    `create_stellar_deposit` succeeds if the provided transaction's `stellar_account`
    has a trustline to the issuer for its `asset`, and the Stellar transaction completes
    successfully. All of these conditions and actions are mocked in this test to avoid
    network calls.
    """
    deposit = acc1_usd_deposit_transaction_factory()
    assert PendingDeposits.submit(deposit)
    assert Transaction.objects.get(id=deposit.id).status == Transaction.STATUS.completed
    def check_trustlines():
        """
        Create Stellar transaction for deposit transactions marked as pending
        trust, if a trustline has been created.
        """
        module = sys.modules[__name__]
        transactions = Transaction.objects.filter(
            kind=Transaction.KIND.deposit,
            status=Transaction.STATUS.pending_trust)
        server = settings.HORIZON_SERVER
        accounts = {}
        for transaction in transactions:
            if module.TERMINATE:
                break
            if accounts.get(transaction.stellar_account):
                account = accounts[transaction.stellar_account]
            else:
                try:
                    account = (server.accounts().account_id(
                        transaction.stellar_account).call())
                    accounts[transaction.stellar_account] = account
                except BaseRequestError:
                    logger.exception(
                        f"Failed to load account {transaction.stellar_account}"
                    )
                    continue
            for balance in account["balances"]:
                if balance.get("asset_type") == "native":
                    continue
                if (balance["asset_code"] == transaction.asset.code and
                        balance["asset_issuer"] == transaction.asset.issuer):
                    logger.info(
                        f"Account {account['id']} has established a trustline for "
                        f"{balance['asset_code']}:{balance['asset_issuer']}")
                    if MultiSigTransactions.requires_multisig(transaction):
                        MultiSigTransactions.save_as_pending_signatures(
                            transaction)
                        continue

                    if PendingDeposits.submit(transaction):
                        transaction.refresh_from_db()
                        try:
                            rdi.after_deposit(transaction)
                        except Exception:
                            logger.exception(
                                "An unexpected error was raised from "
                                "after_deposit() in check_trustlines")
Example #5
0
def test_deposit_stellar_no_trustline_with_claimable_bal(
    acc1_usd_deposit_transaction_factory, ):
    """
    This is the flow when a wallet/client submits a deposit and the sends
    "claimable_balance_supported=True" in their POST request body.
    Such that the deposit transaction is can continue as a
    claimable balance operation rather than a payment operation

    `execute_deposits` from `poll_pending_deposits.py`
    if the provided transaction's Stellar account has no trustline
    and sees claimable_balance_supported set to True.
    it will create_stellar_deposit()
    where it wil complete the deposit flow as a claimable balance
    """
    deposit = acc1_usd_deposit_transaction_factory()
    deposit.claimable_balance_supported = True
    deposit.save()
    assert PendingDeposits.submit(deposit)
    assert Transaction.objects.get(id=deposit.id).claimable_balance_id
    assert Transaction.objects.get(
        id=deposit.id).status == Transaction.STATUS.completed
Example #6
0
    def check_trustlines():
        """
        Create Stellar transaction for deposit transactions marked as pending
        trust, if a trustline has been created.
        """
        module = sys.modules[__name__]
        with django.db.transaction.atomic():
            transactions = list(
                Transaction.objects.filter(
                    kind=Transaction.KIND.deposit,
                    status=Transaction.STATUS.pending_trust,
                    pending_execution_attempt=False,
                ).select_for_update()
            )
            ids = []
            for t in transactions:
                t.pending_execution_attempt = True
                ids.append(t.id)
            Transaction.objects.filter(id__in=ids).update(
                pending_execution_attempt=True
            )
        server = settings.HORIZON_SERVER
        accounts = {}
        for i, transaction in enumerate(transactions):
            if module.TERMINATE:
                still_process_transactions = transactions[i:]
                Transaction.objects.filter(
                    id__in=[t.id for t in still_process_transactions]
                ).update(pending_execution_attempt=False)
                break
            if accounts.get(transaction.stellar_account):
                account = accounts[transaction.stellar_account]
            else:
                try:
                    account = (
                        server.accounts().account_id(transaction.stellar_account).call()
                    )
                    accounts[transaction.stellar_account] = account
                except BaseRequestError:
                    logger.exception(
                        f"Failed to load account {transaction.stellar_account}"
                    )
                    transaction.pending_execution_attempt = False
                    transaction.save()
                    continue
            for balance in account["balances"]:
                if balance.get("asset_type") == "native":
                    continue
                if (
                    balance["asset_code"] == transaction.asset.code
                    and balance["asset_issuer"] == transaction.asset.issuer
                ):
                    logger.info(
                        f"Account {account['id']} has established a trustline for "
                        f"{balance['asset_code']}:{balance['asset_issuer']}"
                    )
                    try:
                        requires_multisig = PendingDeposits.requires_multisig(
                            transaction
                        )
                    except NotFoundError:
                        PendingDeposits.handle_error(
                            transaction,
                            f"{transaction.asset.code} distribution account "
                            f"{transaction.asset.distribution_account} does not exist",
                        )
                        break
                    except ConnectionError:
                        logger.error("Failed to connect to Horizon")
                        transaction.pending_execution_attempt = False
                        transaction.save()
                        break
                    if requires_multisig:
                        PendingDeposits.save_as_pending_signatures(transaction)
                        break

                    try:
                        success = PendingDeposits.submit(transaction)
                    except Exception as e:
                        logger.exception("submit() threw an unexpected exception")
                        PendingDeposits.handle_error(
                            transaction, f"{e.__class__.__name__}: {str(e)}"
                        )
                        break

                    if success:
                        transaction.refresh_from_db()
                        try:
                            rdi.after_deposit(transaction)
                        except Exception:
                            logger.exception(
                                "after_deposit() threw an unexpected exception"
                            )
            if transaction.pending_execution_attempt:
                transaction.pending_execution_attempt = False
                transaction.save()