def test_process_response_success(mock_withdrawal, mock_xdr, client,
                                  acc1_usd_withdrawal_transaction_factory):
    del mock_withdrawal, mock_xdr
    transaction = acc1_usd_withdrawal_transaction_factory()
    json = deepcopy(TRANSACTION_JSON)
    json["successful"] = True
    json["id"] = transaction.id
    json["memo"] = format_memo_horizon(transaction.withdraw_memo)

    Command.process_response(json)

    transaction.refresh_from_db()
    assert transaction.status == Transaction.STATUS.completed
Exemple #2
0
def process_withdrawal(response, transaction):
    """
    Check if a Stellar transaction's memo matches the ID of a database transaction.
    """
    # Validate the Horizon response.
    try:
        memo_type = response["memo_type"]
        response_memo = response["memo"]
        successful = response["successful"]
        stellar_transaction_id = response["id"]
        envelope_xdr = response["envelope_xdr"]
    except KeyError:
        return False

    if memo_type != "hash":
        return False

    # The memo on the response will be base 64 string, due to XDR, while
    # the memo parameter is base 16. Thus, we convert the parameter
    # from hex to base 64, and then to a string without trailing whitespace.
    if response_memo != format_memo_horizon(transaction.withdraw_memo):
        return False

    horizon_tx = TransactionEnvelope.from_xdr(
        envelope_xdr,
        network_passphrase=settings.STELLAR_NETWORK_PASSPHRASE).transaction
    found_matching_payment_op = False
    for operation in horizon_tx.operations:
        if _check_payment_op(operation, transaction.asset.code,
                             transaction.amount_in):
            found_matching_payment_op = True
            break

    # If no matching payment operation is found in the Stellar response, return.
    if not found_matching_payment_op:
        return False

    # If the Stellar transaction succeeded, we mark the corresponding `Transaction`
    # accordingly in the database. Else, we mark it `pending_stellar`, so the wallet
    # knows to resubmit.
    if successful:
        transaction.completed_at = now()
        transaction.status = Transaction.STATUS.completed
        transaction.status_eta = 0
        transaction.amount_out = transaction.amount_in - transaction.amount_fee
    else:
        transaction.status = Transaction.STATUS.pending_stellar

    transaction.stellar_transaction_id = stellar_transaction_id
    transaction.save()
    return True
    def match_transaction(cls, response: Dict, transaction: Transaction) -> bool:
        """
        Determines whether or not the given ``response`` represents the given
        ``transaction``. Polaris does this by constructing the transaction memo
        from the transaction ID passed in the initial withdrawal request to
        ``/transactions/withdraw/interactive``. To be sure, we also check for
        ``transaction``'s payment operation in ``response``.

        :param response: a response body returned from Horizon for the transaction
        :param transaction: a database model object representing the transaction
        """
        try:
            memo_type = response["memo_type"]
            response_memo = response["memo"]
            successful = response["successful"]
            stellar_transaction_id = response["id"]
            envelope_xdr = response["envelope_xdr"]
        except KeyError:
            logger.warning(
                f"Stellar response for transaction missing expected arguments"
            )
            return False

        if memo_type != "hash":
            logger.warning(
                f"Transaction memo for {transaction.id} was not of type hash"
            )
            return False

        # The memo on the response will be base 64 string, due to XDR, while
        # the memo parameter is base 16. Thus, we convert the parameter
        # from hex to base 64, and then to a string without trailing whitespace.
        if response_memo != format_memo_horizon(transaction.withdraw_memo):
            return False

        horizon_tx = TransactionEnvelope.from_xdr(
            response["envelope_xdr"],
            network_passphrase=settings.STELLAR_NETWORK_PASSPHRASE,
        ).transaction
        found_matching_payment_op = False
        for operation in horizon_tx.operations:
            if cls._check_payment_op(
                operation, transaction.asset.code, transaction.amount_in
            ):
                transaction.stellar_transaction_id = stellar_transaction_id
                transaction.from_address = horizon_tx.source.public_key
                transaction.save()
                found_matching_payment_op = True
                break

        return found_matching_payment_op
def test_withdraw_interactive_success_transaction_successful(
        mock_check, client, acc1_usd_withdrawal_transaction_factory):
    """
    `GET /transactions/withdraw/webapp` changes transaction to `completed`
    with successful transaction.
    """
    del mock_check
    acc1_usd_withdrawal_transaction_factory()
    response = client.post(WITHDRAW_PATH, {"asset_code": "USD"}, follow=True)
    content = json.loads(response.content)
    assert content["type"] == "interactive_customer_info_needed"

    transaction_id = content["id"]
    url = content["url"]
    response = client.get(url)
    assert response.status_code == 200
    assert client.session["authenticated"] is True

    url, args_str = url.split("?")
    response = client.post(
        url + "/submit?" + args_str,
        {
            "amount": 50,
            "bank_account": "123456",
            "bank": "Bank",
            "account": "Account"
        },
    )
    assert response.status_code == 302
    transaction = Transaction.objects.get(id=transaction_id)
    assert transaction.status == Transaction.STATUS.pending_user_transfer_start

    withdraw_memo = transaction.withdraw_memo
    mock_response = {
        "memo_type":
        "hash",
        "memo":
        format_memo_horizon(withdraw_memo),
        "successful":
        True,
        "id":
        "c5e8ada72c0e3c248ac7e1ec0ec97e204c06c295113eedbe632020cd6dc29ff8",
        "envelope_xdr":
        "AAAAAEU1B1qeJrucdqkbk1mJsnuFaNORfrOAzJyaAy1yzW8TAAAAZAAE2s4AAAABAAAAAAAAAAAAAAABAAAAAAAAAAEAAAAAoUKq+1Z2GGB98qurLSmocHafvG6S+YzKNE6oiHIXo6kAAAABVVNEAAAAAACnUE2lfwuFZ+G+dkc+qiL0MwxB0CoR0au324j+JC9exQAAAAAdzWUAAAAAAAAAAAA=",
    }
    Command.update_transaction(mock_response, transaction)

    assert transaction.status == Transaction.STATUS.completed
    assert transaction.completed_at
def test_process_response_unsuccessful(
        mock_xdr, client, acc1_usd_withdrawal_transaction_factory):
    del mock_xdr
    transaction = acc1_usd_withdrawal_transaction_factory()
    json = deepcopy(TRANSACTION_JSON)
    json["successful"] = False
    json["id"] = transaction.id
    json["memo"] = format_memo_horizon(transaction.withdraw_memo)

    Command.process_response(json)

    transaction.refresh_from_db()
    assert transaction.status == Transaction.STATUS.error
    assert (transaction.status_message ==
            "The transaction failed to execute on the Stellar network")
Exemple #6
0
def test_withdraw_interactive_success_transaction_unsuccessful(
        mock_check, client, acc1_usd_withdrawal_transaction_factory):
    """
    `GET /withdraw/interactive_withdraw` changes transaction to `pending_stellar`
    with unsuccessful transaction.
    """
    del mock_check
    acc1_usd_withdrawal_transaction_factory()
    response = client.get(f"/withdraw?asset_code=USD", follow=True)
    content = json.loads(response.content)
    assert response.status_code == 403
    assert content["type"] == "interactive_customer_info_needed"

    transaction_id = content["id"]
    url = content["url"]
    response = client.post(url, {
        "amount": 50,
        "bank_account": "123456",
        "bank": "Bank"
    })
    assert response.status_code == 200
    transaction = Transaction.objects.get(id=transaction_id)
    assert transaction.status == Transaction.STATUS.pending_user_transfer_start

    withdraw_memo = transaction.withdraw_memo
    mock_response = {
        "memo_type":
        "hash",
        "memo":
        format_memo_horizon(withdraw_memo),
        "successful":
        False,
        "id":
        "c5e8ada72c0e3c248ac7e1ec0ec97e204c06c295113eedbe632020cd6dc29ff8",
        "envelope_xdr":
        "AAAAAEU1B1qeJrucdqkbk1mJsnuFaNORfrOAzJyaAy1yzW8TAAAAZAAE2s4AAAABAAAAAAAAAAAAAAABAAAAAAAAAAEAAAAAoUKq+1Z2GGB98qurLSmocHafvG6S+YzKNE6oiHIXo6kAAAABVVNEAAAAAACnUE2lfwuFZ+G+dkc+qiL0MwxB0CoR0au324j+JC9exQAAAAAdzWUAAAAAAAAAAAA=",
    }
    process_withdrawal(mock_response, transaction)
    assert (Transaction.objects.get(
        id=transaction_id).status == Transaction.STATUS.pending_stellar)