def test_to_xdr(self):
     # GDF5O4OWEMVBY5FLDHWA5RZTYSV2U276XGKZZ6VSHDDR3THSQ6OQS7UM
     source = Keypair.from_secret(
         "SCCS5ZBI7WVIJ4SW36WGOQQIWJYCL3VOAULSXX3FB57USIO25EDOYQHH")
     destination = "GDJJRRMBK4IWLEPJGIE6SXD2LP7REGZODU7WDC3I2D6MR37F4XSHBKX2"
     amount = "1000.0"
     sequence = 1
     memo = IdMemo(100)
     fee = 200
     asset = Asset.native()
     time_bounds = TimeBounds(12345, 56789)
     ops = [
         Payment(destination, asset, amount),
         ManageData("hello", "world")
     ]
     tx = Transaction(source, sequence, fee, ops, memo, time_bounds)
     network = Network.public_network()
     te = TransactionEnvelope(tx, network)
     assert binascii.hexlify(te.hash()).decode() == te.hash_hex()
     te.sign(source)
     hashx = bytes.fromhex(
         "94e8223a518ac16a8cb110ab1952ef14da2c10b264645c38c8b3d82bd2b20000")
     te.sign_hashx(hashx)
     te_xdr = "AAAAAMvXcdYjKhx0qxnsDsczxKuqa/65lZz6sjjHHczyh50JAAAAyAAAAAAAAAABAAAAAQAAAAAAADA5AAAAAAAA3dUAAAACAAAAAAAAAGQAAAACAAAAAAAAAAEAAAAA0pjFgVcRZZHpMgnpXHpb/xIbLh0/YYto0PzI7+Xl5HAAAAAAAAAAAlQL5AAAAAAAAAAACgAAAAVoZWxsbwAAAAAAAAEAAAAFd29ybGQAAAAAAAAAAAAAAvKHnQkAAABAM4dg0J1LEFBmbDESJ5d+60WCuZC8lnA80g45qyEgz2oRBSNw1mOfZETnL/BgrebkG/K03oI2Wqcs9lvDKrDGDE0sOBsAAAAglOgiOlGKwWqMsRCrGVLvFNosELJkZFw4yLPYK9KyAAA="
     assert te.to_xdr() == te_xdr
     restore_te = TransactionEnvelope.from_xdr(te_xdr, network)
     assert restore_te.to_xdr() == te_xdr
예제 #2
0
def deserialize(value):
    """
    Validation function for Transaction.envelope_xdr
    """
    from polaris import settings

    try:
        TransactionEnvelope.from_xdr(value, settings.STELLAR_NETWORK_PASSPHRASE)
    except SdkError as e:
        raise ValidationError(
            _("Cannot decode envelope XDR for transaction: %(error)s"),
            params={"error": str(e)},
        )
    def test_verify_challenge_transaction_signed_by_client_master_key_raise_unrecognized_signatures(
        self, ):
        server_kp = Keypair.random()
        client_kp_a = Keypair.random()
        client_kp_unrecognized = Keypair.random()

        timeout = 600
        network_passphrase = Network.PUBLIC_NETWORK_PASSPHRASE
        anchor_name = "SDF"

        challenge = build_challenge_transaction(
            server_secret=server_kp.secret,
            client_account_id=client_kp_a.public_key,
            anchor_name=anchor_name,
            network_passphrase=network_passphrase,
            timeout=timeout,
        )

        transaction = TransactionEnvelope.from_xdr(challenge,
                                                   network_passphrase)
        transaction.sign(client_kp_a)
        transaction.sign(client_kp_unrecognized)

        challenge_tx = transaction.to_xdr()
        with pytest.raises(InvalidSep10ChallengeError,
                           match="Transaction has unrecognized signatures."):
            verify_challenge_transaction_signed_by_client_master_key(
                challenge_tx, server_kp.public_key, network_passphrase)
예제 #4
0
def test_auth_get_account(client):
    """`GET <auth>` succeeds with a valid TransactionEnvelope XDR."""
    response = client.get(f"{endpoint}?account={STELLAR_ACCOUNT_1}",
                          follow=True)
    content = json.loads(response.content)
    assert content["network_passphrase"] == "Test SDF Network ; September 2015"
    assert content["transaction"]

    envelope_xdr = content["transaction"]
    envelope_object = TransactionEnvelope.from_xdr(
        envelope_xdr, network_passphrase=settings.STELLAR_NETWORK_PASSPHRASE)
    transaction_object = envelope_object.transaction
    assert transaction_object.sequence == 0
    assert len(transaction_object.operations) == 1

    manage_data_op = transaction_object.operations[0]
    assert manage_data_op.type_code() == Xdr.const.MANAGE_DATA
    assert manage_data_op.data_name == f"{urlparse(settings.HOST_URL).netloc} auth"
    assert len(manage_data_op.data_value) <= 64
    assert len(base64.b64decode(manage_data_op.data_value)) == 48

    signatures = envelope_object.signatures
    assert len(signatures) == 1
    server_signature = signatures[0]

    tx_hash = envelope_object.hash()
    server_public_key = Keypair.from_public_key(settings.SIGNING_KEY)
    server_public_key.verify(tx_hash, server_signature.signature)
    def test_verify_challenge_tx_source_is_different_to_server_account_id(
            self):
        server_kp = Keypair.random()
        client_kp = Keypair.random()
        network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE
        anchor_name = "SDF"

        challenge = build_challenge_transaction(server_kp.secret,
                                                client_kp.public_key,
                                                anchor_name,
                                                network_passphrase)

        transaction = TransactionEnvelope.from_xdr(challenge,
                                                   Network(network_passphrase))
        transaction.sign(client_kp)

        challenge_tx = transaction.to_xdr()
        with pytest.raises(
                InvalidSep10ChallengeError,
                match=
                "Transaction source account is not equal to server's account.",
        ):
            verify_challenge_transaction(challenge_tx,
                                         Keypair.random().public_key,
                                         network_passphrase)
    def test_verify_challenge_transaction_signed_by_client_raise_not_signed(
            self):
        server_kp = Keypair.random()
        client_kp = Keypair.random()
        timeout = 600
        network_passphrase = Network.PUBLIC_NETWORK_PASSPHRASE
        home_domain = "example.com"

        challenge = build_challenge_transaction(
            server_secret=server_kp.secret,
            client_account_id=client_kp.public_key,
            home_domain=home_domain,
            network_passphrase=network_passphrase,
            timeout=timeout,
        )

        transaction = TransactionEnvelope.from_xdr(challenge,
                                                   network_passphrase)
        challenge_tx = transaction.to_xdr()

        with pytest.raises(
                InvalidSep10ChallengeError,
                match="Transaction not signed by any client signer.",
        ):
            verify_challenge_transaction_signed_by_client_master_key(
                challenge_tx, server_kp.public_key, home_domain,
                network_passphrase)
    def test_challenge_transaction(self):
        server_kp = Keypair.random()
        client_account_id = "GBDIT5GUJ7R5BXO3GJHFXJ6AZ5UQK6MNOIDMPQUSMXLIHTUNR2Q5CFNF"
        timeout = 600
        network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE
        anchor_name = "SDF"

        challenge = build_challenge_transaction(
            server_secret=server_kp.secret,
            client_account_id=client_account_id,
            anchor_name=anchor_name,
            network_passphrase=network_passphrase,
            timeout=timeout,
        )

        transaction = TransactionEnvelope.from_xdr(
            challenge, network_passphrase
        ).transaction
        assert len(transaction.operations) == 1
        op = transaction.operations[0]
        assert isinstance(op, ManageData)
        assert op.data_name == "SDF auth"
        assert len(op.data_value) == 64
        assert len(base64.b64decode(op.data_value)) == 48
        assert op.source == client_account_id

        now = int(time.time())
        assert now - 3 < transaction.time_bounds.min_time < now + 3
        assert (
            transaction.time_bounds.max_time - transaction.time_bounds.min_time
            == timeout
        )
        assert transaction.source.public_key == server_kp.public_key
        assert transaction.sequence == 0
예제 #8
0
def test_auth_get_success(client):
    """`GET <auth>` succeeds with a valid TransactionEnvelope XDR."""
    response = client.get(f"{endpoint}?account={STELLAR_ACCOUNT_1}",
                          follow=True)
    content = json.loads(response.content)
    assert content["network_passphrase"] == "Test SDF Network ; September 2015"
    assert content["transaction"]

    envelope_xdr = content["transaction"]
    envelope_object = TransactionEnvelope.from_xdr(
        envelope_xdr, network_passphrase=settings.STELLAR_NETWORK_PASSPHRASE)
    transaction_object = envelope_object.transaction
    assert transaction_object.sequence == 0
    assert len(transaction_object.operations) == 2

    home_domain_op = transaction_object.operations[0]
    assert isinstance(home_domain_op, ManageData)
    assert home_domain_op.data_name == f"{urlparse(settings.HOST_URL).netloc} auth"
    assert len(home_domain_op.data_value) <= 64
    assert len(base64.b64decode(home_domain_op.data_value)) == 48

    web_auth_domain_op = transaction_object.operations[1]
    assert isinstance(web_auth_domain_op, ManageData)
    assert web_auth_domain_op.data_name == "web_auth_domain"
    assert (web_auth_domain_op.data_value ==
            f"{urlparse(settings.HOST_URL).netloc}".encode())

    signatures = envelope_object.signatures
    assert len(signatures) == 1

    tx_hash = envelope_object.hash()
    server_public_key = Keypair.from_public_key(settings.SIGNING_KEY)
    server_public_key.verify(tx_hash, signatures[0].signature)
예제 #9
0
def sign_challenge(content):
    envelope_xdr = content["transaction"]
    envelope_object = TransactionEnvelope.from_xdr(
        envelope_xdr, network_passphrase=settings.STELLAR_NETWORK_PASSPHRASE)
    client_signing_key = Keypair.from_secret(CLIENT_SEED)
    envelope_object.sign(client_signing_key)
    return envelope_object.to_xdr()
예제 #10
0
    def test_verify_challenge_transaction_domain_name_mismatch_raise(self):
        server_kp = Keypair.random()
        client_kp = Keypair.random()
        timeout = 600
        network_passphrase = Network.PUBLIC_NETWORK_PASSPHRASE
        domain_name = "example.com"
        invalid_domain_name = "invalid_example.com"

        challenge = build_challenge_transaction(
            server_secret=server_kp.secret,
            client_account_id=client_kp.public_key,
            domain_name=domain_name,
            network_passphrase=network_passphrase,
            timeout=timeout,
        )

        transaction = TransactionEnvelope.from_xdr(challenge, network_passphrase)
        transaction.sign(client_kp)
        challenge_tx = transaction.to_xdr()
        with pytest.raises(
            InvalidSep10ChallengeError,
            match="The transaction's operation key name "
            "does not include the expected home domain.",
        ):
            verify_challenge_transaction(
                challenge_tx,
                server_kp.public_key,
                invalid_domain_name,
                network_passphrase,
            )
    def test_verify_transaction_signatures(self):
        server_kp = Keypair.random()
        client_kp_a = Keypair.random()
        client_kp_b = Keypair.random()
        client_kp_c = Keypair.random()
        timeout = 600
        network_passphrase = Network.PUBLIC_NETWORK_PASSPHRASE
        anchor_name = "SDF"

        challenge = build_challenge_transaction(
            server_secret=server_kp.secret,
            client_account_id=client_kp_a.public_key,
            anchor_name=anchor_name,
            network_passphrase=network_passphrase,
            timeout=timeout,
        )

        transaction = TransactionEnvelope.from_xdr(challenge, network_passphrase)
        transaction.sign(client_kp_a)
        transaction.sign(client_kp_b)
        transaction.sign(client_kp_c)
        signers = [
            Ed25519PublicKeySigner(client_kp_a.public_key, 1),
            Ed25519PublicKeySigner(client_kp_b.public_key, 2),
            Ed25519PublicKeySigner(client_kp_c.public_key, 3),
            Ed25519PublicKeySigner(Keypair.random().public_key, 4),
        ]
        signers_found = _verify_transaction_signatures(transaction, signers)
        assert signers_found == [
            Ed25519PublicKeySigner(client_kp_a.public_key, 1),
            Ed25519PublicKeySigner(client_kp_b.public_key, 2),
            Ed25519PublicKeySigner(client_kp_c.public_key, 3),
        ]
    def test_verify_challenge_transaction_signed_by_client_raise_not_signed(self):
        server_kp = Keypair.random()
        client_kp = Keypair.random()
        timeout = 600
        network_passphrase = Network.PUBLIC_NETWORK_PASSPHRASE
        anchor_name = "SDF"

        challenge = build_challenge_transaction(
            server_secret=server_kp.secret,
            client_account_id=client_kp.public_key,
            anchor_name=anchor_name,
            network_passphrase=network_passphrase,
            timeout=timeout,
        )

        transaction = TransactionEnvelope.from_xdr(challenge, network_passphrase)
        challenge_tx = transaction.to_xdr()

        with pytest.raises(
            InvalidSep10ChallengeError,
            match="Transaction not signed by client: {}.".format(client_kp.public_key),
        ):
            verify_challenge_transaction_signed_by_client_master_key(
                challenge_tx, server_kp.public_key, network_passphrase
            )
    def test_verify_challenge_transaction_signers_raise_no_signers(self):
        server_kp = Keypair.random()
        client_kp_a = Keypair.random()
        client_kp_b = Keypair.random()
        client_kp_c = Keypair.random()
        timeout = 600
        network_passphrase = Network.PUBLIC_NETWORK_PASSPHRASE
        anchor_name = "SDF"

        challenge = build_challenge_transaction(
            server_secret=server_kp.secret,
            client_account_id=client_kp_a.public_key,
            anchor_name=anchor_name,
            network_passphrase=network_passphrase,
            timeout=timeout,
        )

        transaction = TransactionEnvelope.from_xdr(challenge, network_passphrase)
        transaction.sign(client_kp_a)
        transaction.sign(client_kp_b)
        transaction.sign(client_kp_c)

        challenge_tx = transaction.to_xdr()
        signers = []

        with pytest.raises(InvalidSep10ChallengeError, match="No signers provided."):
            verify_challenge_transaction_signers(
                challenge_tx, server_kp.public_key, network_passphrase, signers
            )
예제 #14
0
def test_withdraw_authenticated_success(
        client, acc1_usd_withdrawal_transaction_factory):
    """`GET /withdraw` succeeds with the SEP 10 authentication flow."""
    client_address = "GDKFNRUATPH4BSZGVFDRBIGZ5QAFILVFRIRYNSQ4UO7V2ZQAPRNL73RI"
    client_seed = "SDKWSBERDHP3SXW5A3LXSI7FWMMO5H7HG33KNYBKWH2HYOXJG2DXQHQY"
    acc1_usd_withdrawal_transaction_factory()

    # SEP 10.
    response = client.get(f"/auth?account={client_address}", follow=True)
    content = json.loads(response.content)

    envelope_xdr = content["transaction"]
    envelope_object = TransactionEnvelope.from_xdr(
        envelope_xdr, network_passphrase=settings.STELLAR_NETWORK_PASSPHRASE)
    client_signing_key = Keypair.from_secret(client_seed)
    envelope_object.sign(client_signing_key)
    client_signed_envelope_xdr = envelope_object.to_xdr()

    response = client.post(
        "/auth",
        data={"transaction": client_signed_envelope_xdr},
        content_type="application/json",
    )
    content = json.loads(response.content)
    encoded_jwt = content["token"]
    assert encoded_jwt

    header = {"HTTP_AUTHORIZATION": f"Bearer {encoded_jwt}"}
    response = client.post(WITHDRAW_PATH, {"asset_code": "USD"},
                           follow=True,
                           **header)
    content = json.loads(response.content)
    assert content["type"] == "interactive_customer_info_needed"
예제 #15
0
def test_auth_get_account(client):
    """`GET <auth>` succeeds with a valid TransactionEnvelope XDR."""
    response = client.get(f"/auth?account={STELLAR_ACCOUNT_1}", follow=True)
    content = json.loads(response.content)
    assert content["network_passphrase"] == "Test SDF Network ; September 2015"
    assert content["transaction"]

    envelope_xdr = content["transaction"]
    envelope_object = TransactionEnvelope.from_xdr(
        envelope_xdr, network_passphrase=settings.STELLAR_NETWORK_PASSPHRASE)
    transaction_object = envelope_object.transaction
    assert transaction_object.sequence == 0
    assert len(transaction_object.operations) == 1

    manage_data_op = transaction_object.operations[0]
    assert manage_data_op.type_code() == Xdr.const.MANAGE_DATA
    assert manage_data_op.data_name == "SEP 24 Reference auth"
    assert len(manage_data_op.data_value) == 64

    signatures = envelope_object.signatures
    assert len(signatures) == 1
    server_signature = signatures[0]

    tx_hash = envelope_object.hash()
    server_public_key = Keypair.from_public_key(
        settings.STELLAR_DISTRIBUTION_ACCOUNT_ADDRESS)
    server_public_key.verify(tx_hash, server_signature.signature)
    def test_verify_challenge_tx_transaction_is_not_signed_by_the_server(self):
        server_kp = Keypair.random()
        client_kp = Keypair.random()
        network_passphrase = Network.PUBLIC_NETWORK_PASSPHRASE
        anchor_name = "SDF"
        timeout = 900

        now = int(time.time())
        server_keypair = Keypair.from_secret(server_kp.secret)
        server_account = Account(account_id=server_keypair.public_key, sequence=-1)
        transaction_builder = TransactionBuilder(
            server_account, network_passphrase, 100
        )
        transaction_builder.add_time_bounds(min_time=now, max_time=now + timeout)
        nonce = os.urandom(48)
        nonce_encoded = base64.b64encode(nonce)
        transaction_builder.append_manage_data_op(
            data_name="{} auth".format(anchor_name),
            data_value=nonce_encoded,
            source=client_kp.public_key,
        )
        challenge = transaction_builder.build().to_xdr()

        transaction = TransactionEnvelope.from_xdr(challenge, network_passphrase)
        transaction.sign(client_kp)
        challenge_tx = transaction.to_xdr()
        with pytest.raises(
            InvalidSep10ChallengeError,
            match="Transaction not signed by server: {}".format(server_kp.public_key),
        ):
            verify_challenge_transaction(
                challenge_tx, server_kp.public_key, network_passphrase
            )
예제 #17
0
def test_auth_post_success_client_attribution_required_allowlist_provided(
        client):
    response = client.get(
        f"{endpoint}?account={CLIENT_ADDRESS}&client_domain={CLIENT_ATTRIBUTION_DOMAIN}",
        follow=True,
    )
    content = json.loads(response.content)

    # Sign the XDR with the client.
    envelope_xdr = content["transaction"]
    envelope = TransactionEnvelope.from_xdr(
        envelope_xdr, network_passphrase=settings.STELLAR_NETWORK_PASSPHRASE)
    envelope.sign(CLIENT_SEED)
    envelope.sign(CLIENT_ATTRIBUTION_SEED)
    response = client.post(
        endpoint,
        data={"transaction": envelope.to_xdr()},
        content_type="application/json",
    )

    content = json.loads(response.content)
    assert content["token"]
    jwt_contents = jwt.decode(content["token"],
                              settings.SERVER_JWT_KEY,
                              algorithms=["HS256"])
    assert jwt_contents["client_domain"] == CLIENT_ATTRIBUTION_DOMAIN
    mock_request.META["HTTP_AUTHORIZATION"] = auth_str.format(content["token"])
    mock_view_function = Mock()
    check_auth(mock_request, mock_view_function)
    mock_view_function.assert_called_once_with(CLIENT_ADDRESS,
                                               CLIENT_ATTRIBUTION_DOMAIN,
                                               mock_request)
예제 #18
0
 async def test_submit_transaction_with_te(self):
     xdr = "AAAAAHI7fpgo+b7tgpiFyYWimjV7L7IOYLwmQS7k7F8SronXAAAAZAE+QT4AAAAJAAAAAQAAAAAAAAAAAAAAAF1MG8cAAAAAAAAAAQAAAAAAAAAAAAAAAOvi1O/HEn+QgZJw+EMZBtwvTVNmpgvE9p8IRfwp0GY4AAAAAAExLQAAAAAAAAAAARKuidcAAABAJVc1ASGp35hUquGNbzzSqWPoTG0zgc89zc4p+19QkgbPqsdyEfHs7+ng9VJA49YneEXRa6Fv7pfKpEigb3VTCg=="
     te = TransactionEnvelope.from_xdr(xdr, Network.PUBLIC_NETWORK_PASSPHRASE)
     horizon_url = "https://horizon.stellar.org"
     client = AiohttpClient()
     async with Server(horizon_url, client) as server:
         resp = await server.submit_transaction(te, True)
         assert resp["envelope_xdr"] == xdr
예제 #19
0
def test_transaction_authenticated_success(
    mock_render,
    client,
    acc1_usd_deposit_transaction_factory,
    acc2_eth_withdrawal_transaction_factory,
):
    """
    Succeeds with expected response if authentication required.
    Though it filters using the stellar transaction ID, the logic
    should apply in any case.
    """
    del mock_render
    client_address = "GDKFNRUATPH4BSZGVFDRBIGZ5QAFILVFRIRYNSQ4UO7V2ZQAPRNL73RI"
    client_seed = "SDKWSBERDHP3SXW5A3LXSI7FWMMO5H7HG33KNYBKWH2HYOXJG2DXQHQY"
    acc1_usd_deposit_transaction_factory()
    withdrawal = acc2_eth_withdrawal_transaction_factory()
    withdrawal.stellar_address = client_address
    withdrawal.save()

    # SEP 10.
    response = client.get(f"/auth?account={client_address}", follow=True)
    content = json.loads(response.content)
    envelope_xdr = content["transaction"]
    envelope_object = TransactionEnvelope.from_xdr(
        envelope_xdr, network_passphrase=settings.STELLAR_NETWORK_PASSPHRASE)
    client_signing_key = Keypair.from_secret(client_seed)
    envelope_object.sign(client_signing_key)
    client_signed_envelope_xdr = envelope_object.to_xdr()

    response = client.post(
        "/auth",
        data={"transaction": client_signed_envelope_xdr},
        content_type="application/json",
    )
    content = json.loads(response.content)
    encoded_jwt = content["token"]
    assert encoded_jwt

    # For testing, we make the key `HTTP_AUTHORIZATION`. This is the value that
    # we expect due to the middleware.
    header = {"HTTP_AUTHORIZATION": f"Bearer {encoded_jwt}"}
    response = client.get(
        f"/transaction?stellar_transaction_id={withdrawal.stellar_transaction_id}",
        follow=True,
        **header,
    )
    content = json.loads(response.content)

    assert response.status_code == 200

    withdrawal_transaction = content.get("transaction")
    assert withdrawal_transaction["kind"] == "withdrawal"
    assert withdrawal_transaction["status"] == Transaction.STATUS.completed
예제 #20
0
    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
예제 #21
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
예제 #22
0
def test_transactions_authenticated_success(
    mock_render,
    client,
    acc2_eth_withdrawal_transaction_factory,
    acc2_eth_deposit_transaction_factory,
):
    """
    Response has correct length and status code, if the SEP 10 authentication
    token is required.
    """
    del mock_render
    client_address = "GDKFNRUATPH4BSZGVFDRBIGZ5QAFILVFRIRYNSQ4UO7V2ZQAPRNL73RI"
    client_seed = "SDKWSBERDHP3SXW5A3LXSI7FWMMO5H7HG33KNYBKWH2HYOXJG2DXQHQY"
    withdrawal = acc2_eth_withdrawal_transaction_factory()
    withdrawal.stellar_address = client_address
    withdrawal.save()
    acc2_eth_deposit_transaction_factory()

    # SEP 10.
    response = client.get(f"/auth?account={client_address}", follow=True)
    content = json.loads(response.content)
    envelope_xdr = content["transaction"]
    envelope_object = TransactionEnvelope.from_xdr(
        envelope_xdr, network_passphrase=settings.STELLAR_NETWORK_PASSPHRASE)
    client_signing_key = Keypair.from_secret(client_seed)
    envelope_object.sign(client_signing_key)
    client_signed_envelope_xdr = envelope_object.to_xdr()

    response = client.post(
        "/auth",
        data={"transaction": client_signed_envelope_xdr},
        content_type="application/json",
    )
    content = json.loads(response.content)
    encoded_jwt = content["token"]
    assert encoded_jwt

    # For testing, we make the key `HTTP_AUTHORIZATION`. This is the value that
    # we expect due to the middleware.
    header = {"HTTP_AUTHORIZATION": f"Bearer {encoded_jwt}"}

    response = client.get(
        f"/transactions?asset_code={withdrawal.asset.code}&account={withdrawal.stellar_account}",
        follow=True,
        **header,
    )
    content = json.loads(response.content)

    assert len(content.get("transactions")) == 2
    assert response.status_code == 200
예제 #23
0
def test_auth_get_no_client_attribution_required_domain_passed_denylist_provided_match(
    client, ):
    response = client.get(
        f"{endpoint}?account={CLIENT_ADDRESS}&client_domain={CLIENT_ATTRIBUTION_DOMAIN}",
        follow=True,
    )
    content = json.loads(response.content)

    # Sign the XDR with the client.
    envelope_xdr = content["transaction"]
    envelope = TransactionEnvelope.from_xdr(
        envelope_xdr, network_passphrase=settings.STELLAR_NETWORK_PASSPHRASE)
    # attribution manage data op not included
    assert len(envelope.transaction.operations) == 2
    def test_verify_challenge_transaction_signers_raise_no_server_signature(
            self):
        server_kp = Keypair.random()
        client_kp_a = Keypair.random()
        client_kp_b = Keypair.random()
        client_kp_c = Keypair.random()
        timeout = 600
        network_passphrase = Network.PUBLIC_NETWORK_PASSPHRASE
        domain_name = "example.com"

        challenge = build_challenge_transaction(
            server_secret=server_kp.secret,
            client_account_id=client_kp_a.public_key,
            domain_name=domain_name,
            network_passphrase=network_passphrase,
            timeout=timeout,
        )

        transaction = TransactionEnvelope.from_xdr(challenge,
                                                   network_passphrase)
        transaction.signatures = []
        transaction.sign(client_kp_a)
        transaction.sign(client_kp_b)
        transaction.sign(client_kp_c)

        challenge_tx = transaction.to_xdr()
        signers = [
            Ed25519PublicKeySigner(client_kp_a.public_key, 1),
            Ed25519PublicKeySigner(client_kp_b.public_key, 2),
            Ed25519PublicKeySigner(client_kp_c.public_key, 4),
            Ed25519PublicKeySigner(Keypair.random().public_key, 255),
        ]
        with pytest.raises(
                InvalidSep10ChallengeError,
                match="Transaction not signed by server: {}.".format(
                    server_kp.public_key),
        ):
            verify_challenge_transaction_signers(
                challenge_tx,
                server_kp.public_key,
                domain_name,
                network_passphrase,
                signers,
            )
    def test_verify_challenge_transaction_threshold(self):
        server_kp = Keypair.random()
        client_kp_a = Keypair.random()
        client_kp_b = Keypair.random()
        client_kp_c = Keypair.random()
        timeout = 600
        network_passphrase = Network.PUBLIC_NETWORK_PASSPHRASE
        domain_name = "example.com"

        challenge = build_challenge_transaction(
            server_secret=server_kp.secret,
            client_account_id=client_kp_a.public_key,
            domain_name=domain_name,
            network_passphrase=network_passphrase,
            timeout=timeout,
        )

        transaction = TransactionEnvelope.from_xdr(challenge,
                                                   network_passphrase)
        transaction.sign(client_kp_a)
        transaction.sign(client_kp_b)
        transaction.sign(client_kp_c)

        challenge_tx = transaction.to_xdr()
        signers = [
            Ed25519PublicKeySigner(client_kp_a.public_key, 1),
            Ed25519PublicKeySigner(client_kp_b.public_key, 2),
            Ed25519PublicKeySigner(client_kp_c.public_key, 4),
            Ed25519PublicKeySigner(Keypair.random().public_key, 255),
        ]
        med_threshold = 7
        signers_found = verify_challenge_transaction_threshold(
            challenge_tx,
            server_kp.public_key,
            domain_name,
            network_passphrase,
            med_threshold,
            signers,
        )
        assert signers_found == [
            Ed25519PublicKeySigner(client_kp_a.public_key, 1),
            Ed25519PublicKeySigner(client_kp_b.public_key, 2),
            Ed25519PublicKeySigner(client_kp_c.public_key, 4),
        ]
    def test_verify_challenge_transaction_threshold_raise_not_meet_threshold(
            self):
        server_kp = Keypair.random()
        client_kp_a = Keypair.random()
        client_kp_b = Keypair.random()
        client_kp_c = Keypair.random()
        timeout = 600
        network_passphrase = Network.PUBLIC_NETWORK_PASSPHRASE
        domain_name = "example.com"

        challenge = build_challenge_transaction(
            server_secret=server_kp.secret,
            client_account_id=client_kp_a.public_key,
            domain_name=domain_name,
            network_passphrase=network_passphrase,
            timeout=timeout,
        )

        transaction = TransactionEnvelope.from_xdr(challenge,
                                                   network_passphrase)
        transaction.sign(client_kp_a)
        transaction.sign(client_kp_b)
        transaction.sign(client_kp_c)

        challenge_tx = transaction.to_xdr()
        signers = [
            Ed25519PublicKeySigner(client_kp_a.public_key, 1),
            Ed25519PublicKeySigner(client_kp_b.public_key, 2),
            Ed25519PublicKeySigner(client_kp_c.public_key, 4),
            Ed25519PublicKeySigner(Keypair.random().public_key, 255),
        ]
        med_threshold = 10
        with pytest.raises(
                InvalidSep10ChallengeError,
                match="signers with weight 7 do not meet threshold 10.",
        ):
            verify_challenge_transaction_threshold(
                challenge_tx,
                server_kp.public_key,
                domain_name,
                network_passphrase,
                med_threshold,
                signers,
            )
예제 #27
0
 def test_to_xdr_v1(self):
     # GDF5O4OWEMVBY5FLDHWA5RZTYSV2U276XGKZZ6VSHDDR3THSQ6OQS7UM
     source = Keypair.from_secret(
         "SCCS5ZBI7WVIJ4SW36WGOQQIWJYCL3VOAULSXX3FB57USIO25EDOYQHH")
     destination = "GDJJRRMBK4IWLEPJGIE6SXD2LP7REGZODU7WDC3I2D6MR37F4XSHBKX2"
     amount = "1000.0"
     sequence = 1
     memo = IdMemo(100)
     fee = 200
     asset = Asset.native()
     time_bounds = TimeBounds(12345, 56789)
     ops = [Payment(destination, asset, amount)]
     tx = Transaction(source, sequence, fee, ops, memo, time_bounds, True)
     te = TransactionEnvelope(tx, Network.PUBLIC_NETWORK_PASSPHRASE)
     assert binascii.hexlify(te.hash()).decode() == te.hash_hex()
     te.sign(source)
     te_xdr = "AAAAAgAAAADL13HWIyocdKsZ7A7HM8Srqmv+uZWc+rI4xx3M8oedCQAAAMgAAAAAAAAAAQAAAAEAAAAAAAAwOQAAAAAAAN3VAAAAAgAAAAAAAABkAAAAAQAAAAAAAAABAAAAANKYxYFXEWWR6TIJ6Vx6W/8SGy4dP2GLaND8yO/l5eRwAAAAAAAAAAJUC+QAAAAAAAAAAAHyh50JAAAAQCXOQnmno3he687bKRtDc6+BXRUf8t+RnTuHy+sKf35UjfFiQbIge+txehmg0N61JsFWfwbL0JtgOjzyeZw5JAs="
     assert te.to_xdr() == te_xdr
     restore_te = TransactionEnvelope.from_xdr(
         te_xdr, Network.PUBLIC_NETWORK_PASSPHRASE)
     assert restore_te.to_xdr() == te_xdr
예제 #28
0
def sep10(client, address, seed):
    response = client.get(f"/auth?account={address}", follow=True)
    content = json.loads(response.content)
    envelope_xdr = content["transaction"]
    envelope_object = TransactionEnvelope.from_xdr(
        envelope_xdr, network_passphrase=settings.STELLAR_NETWORK_PASSPHRASE)
    client_signing_key = Keypair.from_secret(seed)
    envelope_object.sign(client_signing_key)
    client_signed_envelope_xdr = envelope_object.to_xdr()

    response = client.post(
        "/auth",
        data={"transaction": client_signed_envelope_xdr},
        headers={"Content-Type": "application/json"},
    )
    content = json.loads(response.content)
    encoded_jwt = content["token"]
    assert encoded_jwt
    return encoded_jwt
예제 #29
0
def _generate_jwt(request, envelope_xdr):
    """
    Generates the JSON web token from the challenge transaction XDR.

    See: https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0010.md#token
    """
    issued_at = time.time()
    hash_hex = binascii.hexlify(
        TransactionEnvelope.from_xdr(envelope_xdr, network_passphrase=settings.STELLAR_NETWORK_PASSPHRASE).hash()
    ).decode()
    jwt_dict = {
        "iss": request.build_absolute_uri("/auth"),
        "sub": settings.STELLAR_DISTRIBUTION_ACCOUNT_ADDRESS,
        "iat": issued_at,
        "exp": issued_at + 24 * 60 * 60,
        "jti": hash_hex,
    }
    encoded_jwt = jwt.encode(jwt_dict, settings.SERVER_JWT_KEY, algorithm="HS256")
    return encoded_jwt.decode("ascii")
    def test_verify_challenge_transaction_signers_raise_unrecognized_signatures(
            self):
        server_kp = Keypair.random()
        client_kp_a = Keypair.random()
        client_kp_b = Keypair.random()
        client_kp_c = Keypair.random()
        client_kp_unrecognized = Keypair.random()

        timeout = 600
        network_passphrase = Network.PUBLIC_NETWORK_PASSPHRASE
        home_domain = "example.com"

        challenge = build_challenge_transaction(
            server_secret=server_kp.secret,
            client_account_id=client_kp_a.public_key,
            home_domain=home_domain,
            network_passphrase=network_passphrase,
            timeout=timeout,
        )

        transaction = TransactionEnvelope.from_xdr(challenge,
                                                   network_passphrase)
        transaction.sign(client_kp_a)
        transaction.sign(client_kp_b)
        transaction.sign(client_kp_c)
        transaction.sign(client_kp_unrecognized)

        challenge_tx = transaction.to_xdr()
        signers = [
            Ed25519PublicKeySigner(client_kp_a.public_key, 1),
            Ed25519PublicKeySigner(client_kp_b.public_key, 2),
            Ed25519PublicKeySigner(client_kp_c.public_key, 4),
            Ed25519PublicKeySigner(Keypair.random().public_key, 255),
        ]
        with pytest.raises(InvalidSep10ChallengeError,
                           match="Transaction has unrecognized signatures."):
            verify_challenge_transaction_signers(
                challenge_tx,
                server_kp.public_key,
                home_domain,
                network_passphrase,
                signers,
            )