def test_auth_get_client_attribution_success(client): """`GET <auth>` succeeds with a valid TransactionEnvelope XDR.""" response = client.get( f"{endpoint}?account={STELLAR_ACCOUNT_1}&client_domain={CLIENT_ATTRIBUTION_DOMAIN}", 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) == 3 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 assert home_domain_op.source == STELLAR_ACCOUNT_1 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()) assert web_auth_domain_op.source == settings.SIGNING_KEY client_domain_op = transaction_object.operations[2] assert isinstance(client_domain_op, ManageData) assert client_domain_op.data_name == "client_domain" assert client_domain_op.data_value == CLIENT_ATTRIBUTION_DOMAIN.encode() assert client_domain_op.source == CLIENT_ATTRIBUTION_ADDRESS 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)
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_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 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 = [ 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, network_passphrase, med_threshold, 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 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 = [ 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, 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 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
def test_verify_challenge_transaction_signers_raise_no_client_signer_found( 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 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) challenge_tx = transaction.to_xdr() signers = [ Ed25519PublicKeySigner(Keypair.random().public_key, 1), Ed25519PublicKeySigner(Keypair.random().public_key, 2), Ed25519PublicKeySigner(Keypair.random().public_key, 4), ] with pytest.raises( InvalidSep10ChallengeError, match="Transaction not signed by any client signer.", ): verify_challenge_transaction_signers( challenge_tx, server_kp.public_key, home_domain, network_passphrase, signers, )
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 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) 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, domain_name, network_passphrase, signers, )
def test_auth_post_urlencode_success(client): """`POST <auth>` succeeds when given a proper URL-encoded transaction.""" response = client.get(f"/auth?account={CLIENT_ADDRESS}", follow=True) content = json.loads(response.content) # Sign the XDR with the client. 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=urlencode({"transaction": client_signed_envelope_xdr}), content_type="application/x-www-form-urlencoded", ) content = json.loads(response.content) assert content["token"]
def test_fee_authenticated_success(client, usd_asset_factory): """Succeeds for a valid fee, with successful authentication.""" from polaris.tests.auth_test import endpoint as auth_endpoint usd_asset_factory() client_address = "GDKFNRUATPH4BSZGVFDRBIGZ5QAFILVFRIRYNSQ4UO7V2ZQAPRNL73RI" client_seed = "SDKWSBERDHP3SXW5A3LXSI7FWMMO5H7HG33KNYBKWH2HYOXJG2DXQHQY" # SEP 10. response = client.get(f"{auth_endpoint}?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_endpoint, 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"{endpoint}?asset_code=USD&operation=withdraw&amount=100.0", follow=True, **header, ) content = json.loads(response.content) assert response.status_code == 200 assert content == {"fee": 5.0}
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_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_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
def test_verify_challenge_transaction(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) transaction.sign(client_kp) challenge_tx = transaction.to_xdr() verify_challenge_transaction( challenge_tx, server_kp.public_key, network_passphrase )
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 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.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, network_passphrase, signers)
def match_transaction(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: 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( response["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 return found_matching_payment_op
def test_verify_challenge_transaction_with_multi_domain_names(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) transaction.sign(client_kp) challenge_tx = transaction.to_xdr() verify_challenge_transaction( challenge_tx, server_kp.public_key, ["example.com2", "example.com1", home_domain], network_passphrase)
def test_verify_challenge_transaction_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 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) 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), ] signers_found = verify_challenge_transaction_signers( challenge_tx, server_kp.public_key, home_domain, network_passphrase, 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_auth_post_success_account_does_not_exist(client): """`POST <auth>` succeeds when given a proper JSON-encoded transaction.""" response = client.get(f"{endpoint}?account={CLIENT_ADDRESS}", 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) response = client.post( endpoint, data={"transaction": envelope.to_xdr()}, content_type="application/json", ) content = json.loads(response.content) assert content["token"] 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, None, mock_request)
def test_withdraw_authenticated_success( client, acc1_usd_withdrawal_transaction_factory): """`GET /withdraw` succeeds with the SEP 10 authentication flow.""" from polaris.tests.auth_test import endpoint as auth_endpoint client_address = "GDKFNRUATPH4BSZGVFDRBIGZ5QAFILVFRIRYNSQ4UO7V2ZQAPRNL73RI" client_seed = "SDKWSBERDHP3SXW5A3LXSI7FWMMO5H7HG33KNYBKWH2HYOXJG2DXQHQY" acc1_usd_withdrawal_transaction_factory() # SEP 10. response = client.get(f"{auth_endpoint}?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_endpoint, 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"
def process_response(cls, response, account): # We should not match valid pending transactions with ones that were # unsuccessful on the stellar network. If they were unsuccessful, the # client is also aware of the failure and will likely attempt to # resubmit it, in which case we should match the resubmitted transaction if not response.get("successful"): return try: _ = response["id"] envelope_xdr = response["envelope_xdr"] memo = response["memo"] result_xdr = response["result_xdr"] except KeyError: return # Query filters for SEP6 and 24 withdraw_filters = Q( status=Transaction.STATUS.pending_user_transfer_start, kind=Transaction.KIND.withdrawal, ) # Query filters for SEP31 send_filters = Q( status=Transaction.STATUS.pending_sender, kind=Transaction.KIND.send, ) transactions = Transaction.objects.filter( withdraw_filters | send_filters, memo=memo, receiving_anchor_account=account).all() if not transactions: logger.info( f"No match found for stellar transaction {response['id']}") return elif len(transactions) == 1: transaction = transactions[0] else: # in the prior implementation of watch_transactions, the first transaction # to have the same memo is matched, so we'll do the same in the refactored # version. logger.error( f"multiple Transaction objects returned for memo: {memo}") transaction = transactions[0] op_results = (Xdr.StellarXDRUnpacker( b64decode(result_xdr)).unpack_TransactionResult().result.results) horion_tx = TransactionEnvelope.from_xdr( envelope_xdr, network_passphrase=settings.STELLAR_NETWORK_PASSPHRASE, ).transaction payment_data = cls._find_matching_payment_data(response, horion_tx, op_results, transaction) if not payment_data: logger.warning( f"Transaction matching memo {memo} has no payment operation") return # Transaction.amount_in is overwritten with the actual amount sent in the stellar # transaction. This allows anchors to validate the actual amount sent in # execute_outgoing_transactions() and handle invalid amounts appropriately. transaction.amount_in = round( Decimal(payment_data["amount"]), transaction.asset.significant_decimals, ) # The stellar transaction has been matched with an existing record in the DB. # Now the anchor needs to initiate the off-chain transfer of the asset. if transaction.protocol == Transaction.PROTOCOL.sep31: # SEP-31 uses 'pending_receiver' status transaction.status = Transaction.STATUS.pending_receiver transaction.save() else: # SEP-6 and 24 uses 'pending_anchor' status transaction.status = Transaction.STATUS.pending_anchor transaction.save() maybe_make_callback(transaction) return
class TestStellarTransactionStellarUri: xdr = "AAAAAP+yw+ZEuNg533pUmwlYxfrq6/BoMJqiJ8vuQhf6rHWmAAAAZAB8NHAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAYAAAABSFVHAAAAAABAH0wIyY3BJBS2qHdRPAV80M8hF7NBpxRjXyjuT9kEbH//////////AAAAAAAAAAA=" tx = TransactionEnvelope.from_xdr(xdr, Network.TESTNET_NETWORK_PASSPHRASE) fee_bump_tx_xdr = "AAAABQAAAABDqliFalCjYChydhLNq0lXO8PWMJGrsl8naSePTpNR2wAAAAAAAAGQAAAAAgAAAABzdv3ojkzWHMD7KUoXhrPx0GH18vHKV0ZfqpMiEblG1gAAADIAAAAAAAAwOgAAAAAAAAAAAAAAAQAAAAAAAAABAAAAAGqka26GRAaOmdSR+9P28gWFYn/iQ3GPQsRGAuT5bF4JAAAAAAAAAAA7msoAAAAAAAAAAAERuUbWAAAAQM7EA7Pnkeyqzpa+KgOTDLX66WKBk32BSF4NocSAeOkfUDwIODWvgcToOAH4+jaeOqdWhmxjC0mQ0vO3V14u7AwAAAAAAAAAAU6TUdsAAABAt2wuZNUirKyCkv7Sc3B1kk5CUPt7eitpaZjRBpbrW1p8wVIZ6IsaRzzIVHP7YpGhzbPImXkahlolf0IL3sevBA==" fee_bump_tx = FeeBumpTransactionEnvelope.from_xdr( fee_bump_tx_xdr, Network.TESTNET_NETWORK_PASSPHRASE) @pytest.mark.parametrize( "tx, replace, callback, pubkey, message, network_passphrase, origin_domain, signature, signer, uri", [ ( tx, [ Replacement("sourceAccount", "X", "account on which to create the trustline") ], None, None, None, None, None, None, None, "web+stellar:tx?xdr=AAAAAP%2Byw%2BZEuNg533pUmwlYxfrq6%2FBoMJqiJ8vuQhf6rHWmAAAAZAB8NHAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAYAAAABSFVHAAAAAABAH0wIyY3BJBS2qHdRPAV80M8hF7NBpxRjXyjuT9kEbH%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FAAAAAAAAAAA%3D&replace=sourceAccount%3AX%3BX%3Aaccount%20on%20which%20to%20create%20the%20trustline", ), ( tx, None, None, None, None, None, None, None, None, "web+stellar:tx?xdr=AAAAAP%2Byw%2BZEuNg533pUmwlYxfrq6%2FBoMJqiJ8vuQhf6rHWmAAAAZAB8NHAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAYAAAABSFVHAAAAAABAH0wIyY3BJBS2qHdRPAV80M8hF7NBpxRjXyjuT9kEbH%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FAAAAAAAAAAA%3D", ), ( tx, [ Replacement("sourceAccount", "X", "account on which to create the trustline"), Replacement("seqNum", "Y", "sequence for sourceAccount"), ], None, None, None, None, None, None, None, "web+stellar:tx?xdr=AAAAAP%2Byw%2BZEuNg533pUmwlYxfrq6%2FBoMJqiJ8vuQhf6rHWmAAAAZAB8NHAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAYAAAABSFVHAAAAAABAH0wIyY3BJBS2qHdRPAV80M8hF7NBpxRjXyjuT9kEbH%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FAAAAAAAAAAA%3D&replace=sourceAccount%3AX%2CseqNum%3AY%3BX%3Aaccount%20on%20which%20to%20create%20the%20trustline%2CY%3Asequence%20for%20sourceAccount", ), ( tx, [ Replacement("sourceAccount", "X", "account on which to create the trustline") ], None, None, None, None, None, None, "SBPOVRVKTTV7W3IOX2FJPSMPCJ5L2WU2YKTP3HCLYPXNI5MDIGREVNYC", "web+stellar:tx?xdr=AAAAAP%2Byw%2BZEuNg533pUmwlYxfrq6%2FBoMJqiJ8vuQhf6rHWmAAAAZAB8NHAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAYAAAABSFVHAAAAAABAH0wIyY3BJBS2qHdRPAV80M8hF7NBpxRjXyjuT9kEbH%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FAAAAAAAAAAA%3D&replace=sourceAccount%3AX%3BX%3Aaccount%20on%20which%20to%20create%20the%20trustline&signature=VES7qVW1mbxV7PFDi8DvyrxP2EUhYqqGnw%2B%2BQEeIZeYIlrdvc9qqQo0dqtOR4qb2npoml1rlp%2F30WPikSaE6Bg%3D%3D", ), ( tx, [ Replacement("sourceAccount", "X", "account on which to create the trustline") ], None, None, None, None, None, "testSignature", "SBPOVRVKTTV7W3IOX2FJPSMPCJ5L2WU2YKTP3HCLYPXNI5MDIGREVNYC", "web+stellar:tx?xdr=AAAAAP%2Byw%2BZEuNg533pUmwlYxfrq6%2FBoMJqiJ8vuQhf6rHWmAAAAZAB8NHAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAYAAAABSFVHAAAAAABAH0wIyY3BJBS2qHdRPAV80M8hF7NBpxRjXyjuT9kEbH%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FAAAAAAAAAAA%3D&replace=sourceAccount%3AX%3BX%3Aaccount%20on%20which%20to%20create%20the%20trustline&signature=VES7qVW1mbxV7PFDi8DvyrxP2EUhYqqGnw%2B%2BQEeIZeYIlrdvc9qqQo0dqtOR4qb2npoml1rlp%2F30WPikSaE6Bg%3D%3D", ), ( tx, [ Replacement("sourceAccount", "X", "account on which to create the trustline") ], None, None, None, None, None, "testSignature", None, "web+stellar:tx?xdr=AAAAAP%2Byw%2BZEuNg533pUmwlYxfrq6%2FBoMJqiJ8vuQhf6rHWmAAAAZAB8NHAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAYAAAABSFVHAAAAAABAH0wIyY3BJBS2qHdRPAV80M8hF7NBpxRjXyjuT9kEbH%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FAAAAAAAAAAA%3D&replace=sourceAccount%3AX%3BX%3Aaccount%20on%20which%20to%20create%20the%20trustline&signature=testSignature", ), ( tx, [ Replacement("sourceAccount", "X", "account on which to create the trustline") ], None, None, None, None, None, None, Keypair.from_secret( "SBPOVRVKTTV7W3IOX2FJPSMPCJ5L2WU2YKTP3HCLYPXNI5MDIGREVNYC" ), "web+stellar:tx?xdr=AAAAAP%2Byw%2BZEuNg533pUmwlYxfrq6%2FBoMJqiJ8vuQhf6rHWmAAAAZAB8NHAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAYAAAABSFVHAAAAAABAH0wIyY3BJBS2qHdRPAV80M8hF7NBpxRjXyjuT9kEbH%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FAAAAAAAAAAA%3D&replace=sourceAccount%3AX%3BX%3Aaccount%20on%20which%20to%20create%20the%20trustline&signature=VES7qVW1mbxV7PFDi8DvyrxP2EUhYqqGnw%2B%2BQEeIZeYIlrdvc9qqQo0dqtOR4qb2npoml1rlp%2F30WPikSaE6Bg%3D%3D", ), ( tx, [ Replacement("sourceAccount", "X", "account on which to create the trustline") ], "https://someSigningService.com", "GAU2ZSYYEYO5S5ZQSMMUENJ2TANY4FPXYGGIMU6GMGKTNVDG5QYFW6JS", "a" * 300, Network.TESTNET_NETWORK_PASSPHRASE, "someDomain.com", "testSignature", "SBPOVRVKTTV7W3IOX2FJPSMPCJ5L2WU2YKTP3HCLYPXNI5MDIGREVNYC", "web+stellar:tx?xdr=AAAAAP%2Byw%2BZEuNg533pUmwlYxfrq6%2FBoMJqiJ8vuQhf6rHWmAAAAZAB8NHAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAYAAAABSFVHAAAAAABAH0wIyY3BJBS2qHdRPAV80M8hF7NBpxRjXyjuT9kEbH%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FAAAAAAAAAAA%3D&callback=url%3Ahttps%3A%2F%2FsomeSigningService.com&replace=sourceAccount%3AX%3BX%3Aaccount%20on%20which%20to%20create%20the%20trustline&pubkey=GAU2ZSYYEYO5S5ZQSMMUENJ2TANY4FPXYGGIMU6GMGKTNVDG5QYFW6JS&msg=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&network_passphrase=Test%20SDF%20Network%20%3B%20September%202015&origin_domain=someDomain.com&signature=8uiZ2r7KT3gRsO%2BrzmofGHyl%2FLFMfOgNtx5oOddK2rAy8M%2BOgBYOSQpASNbIm%2BIvZVojxv8tKTYuzOkbyhPODA%3D%3D", ), ( fee_bump_tx, [ Replacement("sourceAccount", "X", "account on which to create the trustline") ], "https://someSigningService.com", "GAU2ZSYYEYO5S5ZQSMMUENJ2TANY4FPXYGGIMU6GMGKTNVDG5QYFW6JS", "a" * 300, Network.TESTNET_NETWORK_PASSPHRASE, "someDomain.com", "testSignature", "SBPOVRVKTTV7W3IOX2FJPSMPCJ5L2WU2YKTP3HCLYPXNI5MDIGREVNYC", "web+stellar:tx?xdr=AAAABQAAAABDqliFalCjYChydhLNq0lXO8PWMJGrsl8naSePTpNR2wAAAAAAAAGQAAAAAgAAAABzdv3ojkzWHMD7KUoXhrPx0GH18vHKV0ZfqpMiEblG1gAAADIAAAAAAAAwOgAAAAAAAAAAAAAAAQAAAAAAAAABAAAAAGqka26GRAaOmdSR%2B9P28gWFYn%2FiQ3GPQsRGAuT5bF4JAAAAAAAAAAA7msoAAAAAAAAAAAERuUbWAAAAQM7EA7Pnkeyqzpa%2BKgOTDLX66WKBk32BSF4NocSAeOkfUDwIODWvgcToOAH4%2BjaeOqdWhmxjC0mQ0vO3V14u7AwAAAAAAAAAAU6TUdsAAABAt2wuZNUirKyCkv7Sc3B1kk5CUPt7eitpaZjRBpbrW1p8wVIZ6IsaRzzIVHP7YpGhzbPImXkahlolf0IL3sevBA%3D%3D&callback=url%3Ahttps%3A%2F%2FsomeSigningService.com&replace=sourceAccount%3AX%3BX%3Aaccount%20on%20which%20to%20create%20the%20trustline&pubkey=GAU2ZSYYEYO5S5ZQSMMUENJ2TANY4FPXYGGIMU6GMGKTNVDG5QYFW6JS&msg=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&network_passphrase=Test%20SDF%20Network%20%3B%20September%202015&origin_domain=someDomain.com&signature=x968yy0d2uFhx6At2mxd6yK%2F0ZZCz%2F%2FQT%2BmlIXmJwI6xe4RuYVIPZpROVx8HUYtcL9ok%2FrwzlQHnBg%2F%2Ba0YYDg%3D%3D", ), ], ) def test_to_uri( self, tx, replace, callback, pubkey, message, network_passphrase, origin_domain, signature, signer, uri, ): uri_builder = TransactionStellarUri( transaction_envelope=tx, replace=replace, callback=callback, pubkey=pubkey, message=message, network_passphrase=network_passphrase, origin_domain=origin_domain, signature=signature, ) if signer: uri_builder.sign(signer) assert uri_builder.to_uri() == uri restore_uri = TransactionStellarUri.from_uri( uri_builder.to_uri(), Network.TESTNET_NETWORK_PASSPHRASE).to_uri() if network_passphrase is None: restore_uri = restore_uri.replace( "&network_passphrase=Test%20SDF%20Network%20%3B%20September%202015", "") assert restore_uri == uri def test_message_too_long_raise(self): message = "_" * 301 with pytest.raises(ValueError, match="Message must not exceed 300 characters."): TransactionStellarUri( transaction_envelope=self.tx, message=message, ) def test_equal(self): assert TransactionStellarUri( transaction_envelope=self.tx) == TransactionStellarUri( transaction_envelope=self.tx) def test_invalid_callback_raise(self): uri = "web+stellar:tx?xdr=AAAAAP%2Byw%2BZEuNg533pUmwlYxfrq6%2FBoMJqiJ8vuQhf6rHWmAAAAZAB8NHAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAYAAAABSFVHAAAAAABAH0wIyY3BJBS2qHdRPAV80M8hF7NBpxRjXyjuT9kEbH%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FAAAAAAAAAAA%3D&callback=https%3A%2F%2FsomeSigningService.com&replace=sourceAccount%3AX%3BX%3Aaccount%20on%20which%20to%20create%20the%20trustline&pubkey=GAU2ZSYYEYO5S5ZQSMMUENJ2TANY4FPXYGGIMU6GMGKTNVDG5QYFW6JS&msg=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&network_passphrase=Test%20SDF%20Network%20%3B%20September%202015&origin_domain=someDomain.com&signature=8uiZ2r7KT3gRsO%2BrzmofGHyl%2FLFMfOgNtx5oOddK2rAy8M%2BOgBYOSQpASNbIm%2BIvZVojxv8tKTYuzOkbyhPODA%3D%3D" with pytest.raises(ValueError, match="`callback` should start with `url:`."): TransactionStellarUri.from_uri(uri, Network.TESTNET_NETWORK_PASSPHRASE) def test_missing_network_passphrase_raise(self): uri = "web+stellar:tx?xdr=AAAAAP%2Byw%2BZEuNg533pUmwlYxfrq6%2FBoMJqiJ8vuQhf6rHWmAAAAZAB8NHAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAYAAAABSFVHAAAAAABAH0wIyY3BJBS2qHdRPAV80M8hF7NBpxRjXyjuT9kEbH%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FAAAAAAAAAAA%3D&callback=https%3A%2F%2FsomeSigningService.com&replace=sourceAccount%3AX%3BX%3Aaccount%20on%20which%20to%20create%20the%20trustline&pubkey=GAU2ZSYYEYO5S5ZQSMMUENJ2TANY4FPXYGGIMU6GMGKTNVDG5QYFW6JS&msg=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&origin_domain=someDomain.com&signature=8uiZ2r7KT3gRsO%2BrzmofGHyl%2FLFMfOgNtx5oOddK2rAy8M%2BOgBYOSQpASNbIm%2BIvZVojxv8tKTYuzOkbyhPODA%3D%3D" with pytest.raises(ValueError, match="`network_passphrase` is required."): TransactionStellarUri.from_uri(uri, None) def test_invalid_scheme_raise(self): uri = "invalid+web+stellar:tx?xdr=AAAAAP%2Byw%2BZEuNg533pUmwlYxfrq6%2FBoMJqiJ8vuQhf6rHWmAAAAZAB8NHAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAYAAAABSFVHAAAAAABAH0wIyY3BJBS2qHdRPAV80M8hF7NBpxRjXyjuT9kEbH%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FAAAAAAAAAAA%3D&callback=url%3Ahttps%3A%2F%2FsomeSigningService.com&replace=sourceAccount%3AX%3BX%3Aaccount%20on%20which%20to%20create%20the%20trustline&pubkey=GAU2ZSYYEYO5S5ZQSMMUENJ2TANY4FPXYGGIMU6GMGKTNVDG5QYFW6JS&msg=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&network_passphrase=Test%20SDF%20Network%20%3B%20September%202015&origin_domain=someDomain.com&signature=8uiZ2r7KT3gRsO%2BrzmofGHyl%2FLFMfOgNtx5oOddK2rAy8M%2BOgBYOSQpASNbIm%2BIvZVojxv8tKTYuzOkbyhPODA%3D%3D" # TODO: recheck: Stellar URI scheme should be `web+stellar`, but got `invalid+web+stellar`. with pytest.raises(ValueError, match="Stellar URI scheme should be"): TransactionStellarUri.from_uri(uri, Network.TESTNET_NETWORK_PASSPHRASE) def test_invalid_path_raise(self): uri = "web+stellar:invalid_tx?xdr=AAAAAP%2Byw%2BZEuNg533pUmwlYxfrq6%2FBoMJqiJ8vuQhf6rHWmAAAAZAB8NHAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAYAAAABSFVHAAAAAABAH0wIyY3BJBS2qHdRPAV80M8hF7NBpxRjXyjuT9kEbH%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FAAAAAAAAAAA%3D&callback=url%3Ahttps%3A%2F%2FsomeSigningService.com&replace=sourceAccount%3AX%3BX%3Aaccount%20on%20which%20to%20create%20the%20trustline&pubkey=GAU2ZSYYEYO5S5ZQSMMUENJ2TANY4FPXYGGIMU6GMGKTNVDG5QYFW6JS&msg=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&network_passphrase=Test%20SDF%20Network%20%3B%20September%202015&origin_domain=someDomain.com&signature=8uiZ2r7KT3gRsO%2BrzmofGHyl%2FLFMfOgNtx5oOddK2rAy8M%2BOgBYOSQpASNbIm%2BIvZVojxv8tKTYuzOkbyhPODA%3D%3D" with pytest.raises( ValueError, match="Stellar URI path should be `tx`, but got `invalid_tx`." ): TransactionStellarUri.from_uri(uri, Network.TESTNET_NETWORK_PASSPHRASE) # TODO: add more tests def test_invalid_replace_raise(self): uri = "web+stellar:tx?xdr=AAAAAP%2Byw%2BZEuNg533pUmwlYxfrq6%2FBoMJqiJ8vuQhf6rHWmAAAAZAB8NHAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAYAAAABSFVHAAAAAABAH0wIyY3BJBS2qHdRPAV80M8hF7NBpxRjXyjuT9kEbH%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FAAAAAAAAAAA%3D&callback=url%3Ahttps%3A%2F%2FsomeSigningService.com&replace=sourceAccount%3AX%3BY%3Aaccount%20on%20which%20to%20create%20the%20trustline&pubkey=GAU2ZSYYEYO5S5ZQSMMUENJ2TANY4FPXYGGIMU6GMGKTNVDG5QYFW6JS&msg=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&network_passphrase=Test%20SDF%20Network%20%3B%20September%202015&origin_domain=someDomain.com&signature=8uiZ2r7KT3gRsO%2BrzmofGHyl%2FLFMfOgNtx5oOddK2rAy8M%2BOgBYOSQpASNbIm%2BIvZVojxv8tKTYuzOkbyhPODA%3D%3D" with pytest.raises(ValueError, match="Invalid `replace`."): TransactionStellarUri.from_uri(uri, Network.TESTNET_NETWORK_PASSPHRASE) def test_missing_xdr_passphrase_raise(self): uri = "web+stellar:tx?callback=url%3Ahttps%3A%2F%2FsomeSigningService.com&replace=sourceAccount%3AX%3BX%3Aaccount%20on%20which%20to%20create%20the%20trustline&pubkey=GAU2ZSYYEYO5S5ZQSMMUENJ2TANY4FPXYGGIMU6GMGKTNVDG5QYFW6JS&msg=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&network_passphrase=Test%20SDF%20Network%20%3B%20September%202015&origin_domain=someDomain.com&signature=8uiZ2r7KT3gRsO%2BrzmofGHyl%2FLFMfOgNtx5oOddK2rAy8M%2BOgBYOSQpASNbIm%2BIvZVojxv8tKTYuzOkbyhPODA%3D%3D" with pytest.raises(ValueError, match="`xdr` is missing from uri."): TransactionStellarUri.from_uri(uri, Network.TESTNET_NETWORK_PASSPHRASE) def test_network_passphrase_mismatch_raise(self): uri = "web+stellar:tx?xdr=AAAAAP%2Byw%2BZEuNg533pUmwlYxfrq6%2FBoMJqiJ8vuQhf6rHWmAAAAZAB8NHAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAYAAAABSFVHAAAAAABAH0wIyY3BJBS2qHdRPAV80M8hF7NBpxRjXyjuT9kEbH%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FAAAAAAAAAAA%3D&callback=url%3Ahttps%3A%2F%2FsomeSigningService.com&replace=sourceAccount%3AX%3BX%3Aaccount%20on%20which%20to%20create%20the%20trustline&pubkey=GAU2ZSYYEYO5S5ZQSMMUENJ2TANY4FPXYGGIMU6GMGKTNVDG5QYFW6JS&msg=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&network_passphrase=Test%20SDF%20Network%20%3B%20September%202015&origin_domain=someDomain.com&signature=8uiZ2r7KT3gRsO%2BrzmofGHyl%2FLFMfOgNtx5oOddK2rAy8M%2BOgBYOSQpASNbIm%2BIvZVojxv8tKTYuzOkbyhPODA%3D%3D" with pytest.raises( ValueError, match= "The `network_passphrase` in the function parameter does not " "match the `network_passphrase` in the uri.", ): TransactionStellarUri.from_uri(uri, Network.PUBLIC_NETWORK_PASSPHRASE)
class TestStellarTransactionStellarUri: xdr = "AAAAAP+yw+ZEuNg533pUmwlYxfrq6/BoMJqiJ8vuQhf6rHWmAAAAZAB8NHAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAYAAAABSFVHAAAAAABAH0wIyY3BJBS2qHdRPAV80M8hF7NBpxRjXyjuT9kEbH//////////AAAAAAAAAAA=" tx = TransactionEnvelope.from_xdr(xdr, Network.TESTNET_NETWORK_PASSPHRASE) @pytest.mark.parametrize( "tx, replace, callback, pubkey, message, network_passphrase, origin_domain, signature, signer, uri", [ ( tx, [ Replacement("sourceAccount", "X", "account on which to create the trustline") ], None, None, None, None, None, None, None, "web+stellar:tx?xdr=AAAAAP%2Byw%2BZEuNg533pUmwlYxfrq6%2FBoMJqiJ8vuQhf6rHWmAAAAZAB8NHAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAYAAAABSFVHAAAAAABAH0wIyY3BJBS2qHdRPAV80M8hF7NBpxRjXyjuT9kEbH%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FAAAAAAAAAAA%3D&replace=sourceAccount%3AX%3BX%3Aaccount%20on%20which%20to%20create%20the%20trustline", ), ( tx, None, None, None, None, None, None, None, None, "web+stellar:tx?xdr=AAAAAP%2Byw%2BZEuNg533pUmwlYxfrq6%2FBoMJqiJ8vuQhf6rHWmAAAAZAB8NHAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAYAAAABSFVHAAAAAABAH0wIyY3BJBS2qHdRPAV80M8hF7NBpxRjXyjuT9kEbH%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FAAAAAAAAAAA%3D", ), ( tx, [ Replacement("sourceAccount", "X", "account on which to create the trustline"), Replacement("seqNum", "Y", "sequence for sourceAccount"), ], None, None, None, None, None, None, None, "web+stellar:tx?xdr=AAAAAP%2Byw%2BZEuNg533pUmwlYxfrq6%2FBoMJqiJ8vuQhf6rHWmAAAAZAB8NHAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAYAAAABSFVHAAAAAABAH0wIyY3BJBS2qHdRPAV80M8hF7NBpxRjXyjuT9kEbH%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FAAAAAAAAAAA%3D&replace=sourceAccount%3AX%2CseqNum%3AY%3BX%3Aaccount%20on%20which%20to%20create%20the%20trustline%2CY%3Asequence%20for%20sourceAccount", ), ( tx, [ Replacement("sourceAccount", "X", "account on which to create the trustline") ], None, None, None, None, None, None, "SBPOVRVKTTV7W3IOX2FJPSMPCJ5L2WU2YKTP3HCLYPXNI5MDIGREVNYC", "web+stellar:tx?xdr=AAAAAP%2Byw%2BZEuNg533pUmwlYxfrq6%2FBoMJqiJ8vuQhf6rHWmAAAAZAB8NHAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAYAAAABSFVHAAAAAABAH0wIyY3BJBS2qHdRPAV80M8hF7NBpxRjXyjuT9kEbH%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FAAAAAAAAAAA%3D&replace=sourceAccount%3AX%3BX%3Aaccount%20on%20which%20to%20create%20the%20trustline&signature=VES7qVW1mbxV7PFDi8DvyrxP2EUhYqqGnw%2B%2BQEeIZeYIlrdvc9qqQo0dqtOR4qb2npoml1rlp%2F30WPikSaE6Bg%3D%3D", ), ( tx, [ Replacement("sourceAccount", "X", "account on which to create the trustline") ], None, None, None, None, None, "testSignature", "SBPOVRVKTTV7W3IOX2FJPSMPCJ5L2WU2YKTP3HCLYPXNI5MDIGREVNYC", "web+stellar:tx?xdr=AAAAAP%2Byw%2BZEuNg533pUmwlYxfrq6%2FBoMJqiJ8vuQhf6rHWmAAAAZAB8NHAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAYAAAABSFVHAAAAAABAH0wIyY3BJBS2qHdRPAV80M8hF7NBpxRjXyjuT9kEbH%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FAAAAAAAAAAA%3D&replace=sourceAccount%3AX%3BX%3Aaccount%20on%20which%20to%20create%20the%20trustline&signature=VES7qVW1mbxV7PFDi8DvyrxP2EUhYqqGnw%2B%2BQEeIZeYIlrdvc9qqQo0dqtOR4qb2npoml1rlp%2F30WPikSaE6Bg%3D%3D", ), ( tx, [ Replacement("sourceAccount", "X", "account on which to create the trustline") ], None, None, None, None, None, "testSignature", None, "web+stellar:tx?xdr=AAAAAP%2Byw%2BZEuNg533pUmwlYxfrq6%2FBoMJqiJ8vuQhf6rHWmAAAAZAB8NHAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAYAAAABSFVHAAAAAABAH0wIyY3BJBS2qHdRPAV80M8hF7NBpxRjXyjuT9kEbH%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FAAAAAAAAAAA%3D&replace=sourceAccount%3AX%3BX%3Aaccount%20on%20which%20to%20create%20the%20trustline&signature=testSignature", ), ( tx, [ Replacement("sourceAccount", "X", "account on which to create the trustline") ], None, None, None, None, None, None, Keypair.from_secret( "SBPOVRVKTTV7W3IOX2FJPSMPCJ5L2WU2YKTP3HCLYPXNI5MDIGREVNYC" ), "web+stellar:tx?xdr=AAAAAP%2Byw%2BZEuNg533pUmwlYxfrq6%2FBoMJqiJ8vuQhf6rHWmAAAAZAB8NHAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAYAAAABSFVHAAAAAABAH0wIyY3BJBS2qHdRPAV80M8hF7NBpxRjXyjuT9kEbH%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FAAAAAAAAAAA%3D&replace=sourceAccount%3AX%3BX%3Aaccount%20on%20which%20to%20create%20the%20trustline&signature=VES7qVW1mbxV7PFDi8DvyrxP2EUhYqqGnw%2B%2BQEeIZeYIlrdvc9qqQo0dqtOR4qb2npoml1rlp%2F30WPikSaE6Bg%3D%3D", ), ( tx, [ Replacement("sourceAccount", "X", "account on which to create the trustline") ], "https://someSigningService.com", "GAU2ZSYYEYO5S5ZQSMMUENJ2TANY4FPXYGGIMU6GMGKTNVDG5QYFW6JS", "a" * 300, Network.TESTNET_NETWORK_PASSPHRASE, "someDomain.com", "testSignature", "SBPOVRVKTTV7W3IOX2FJPSMPCJ5L2WU2YKTP3HCLYPXNI5MDIGREVNYC", "web+stellar:tx?xdr=AAAAAP%2Byw%2BZEuNg533pUmwlYxfrq6%2FBoMJqiJ8vuQhf6rHWmAAAAZAB8NHAAAAABAAAAAAAAAAAAAAABAAAAAAAAAAYAAAABSFVHAAAAAABAH0wIyY3BJBS2qHdRPAV80M8hF7NBpxRjXyjuT9kEbH%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FAAAAAAAAAAA%3D&callback=url%3Ahttps%3A%2F%2FsomeSigningService.com&replace=sourceAccount%3AX%3BX%3Aaccount%20on%20which%20to%20create%20the%20trustline&pubkey=GAU2ZSYYEYO5S5ZQSMMUENJ2TANY4FPXYGGIMU6GMGKTNVDG5QYFW6JS&msg=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa&network_passphrase=Test%20SDF%20Network%20%3B%20September%202015&origin_domain=someDomain.com&signature=8uiZ2r7KT3gRsO%2BrzmofGHyl%2FLFMfOgNtx5oOddK2rAy8M%2BOgBYOSQpASNbIm%2BIvZVojxv8tKTYuzOkbyhPODA%3D%3D", ), ], ) def test_to_uri( self, tx, replace, callback, pubkey, message, network_passphrase, origin_domain, signature, signer, uri, ): uri_builder = TransactionStellarUri( transaction_envelope=tx, replace=replace, callback=callback, pubkey=pubkey, message=message, network_passphrase=network_passphrase, origin_domain=origin_domain, signature=signature, ) if signer: uri_builder.sign(signer) assert uri_builder.to_uri() == uri def test_message_too_long_raise(self): message = "_" * 301 with pytest.raises(ValueError, match="Message must not exceed 300 characters."): TransactionStellarUri( transaction_envelope=self.tx, message=message, ) def test_equal(self): assert TransactionStellarUri( transaction_envelope=self.tx) == TransactionStellarUri( transaction_envelope=self.tx)
def process_response(cls, response, account): # We should not match valid pending transactions with ones that were # unsuccessful on the stellar network. If they were unsuccessful, the # client is also aware of the failure and will likely attempt to # resubmit it, in which case we should match the resubmitted transaction if not response.get("successful"): return try: stellar_transaction_id = response["id"] envelope_xdr = response["envelope_xdr"] memo = response["memo"] except KeyError: return horizon_tx = TransactionEnvelope.from_xdr( envelope_xdr, network_passphrase=settings.STELLAR_NETWORK_PASSPHRASE, ).transaction # Query filters for SEP6 and 24 withdraw_filters = Q( status=Transaction.STATUS.pending_user_transfer_start, kind=Transaction.KIND.withdrawal, ) # Query filters for SEP31 send_filters = Q( status=Transaction.STATUS.pending_sender, kind=Transaction.KIND.send, ) transactions = Transaction.objects.filter( withdraw_filters | send_filters, memo=memo, receiving_anchor_account=account).all() if not transactions: logger.info( f"No match found for stellar transaction {response['id']}") return elif len(transactions) == 1: transaction = transactions[0] else: # in the prior implementation of watch_transactions, the first transaction # to have the same memo is matched, so we'll do the same in the refactored # version. logger.error( f"multiple Transaction objects returned for memo: {memo}") transaction = transactions[0] payment_op = cls.find_matching_payment_op(response, horizon_tx, transaction) if not payment_op: logger.warning( f"Transaction matching memo {memo} has no payment operation") return # Transaction.amount_in is overwritten with the actual amount sent in the stellar # transaction. This allows anchors to validate the actual amount sent in # execute_outgoing_transactions() and handle invalid amounts appropriately. transaction.amount_in = round( Decimal(payment_op.amount), transaction.asset.significant_decimals, ) # The stellar transaction has been matched with an existing record in the DB. # Now the anchor needs to initiate the off-chain transfer of the asset. # # Prior to the 0.12 release, Polaris' SEP-6 and 24 integrations didn't not # provide an interface that allowed anchors to check on the state of # transactions on an external network. Now, ``poll_outgoing_transactions()`` # allows anchors to check on transactions that have been submitted to a # non-stellar payment network but have not completed, and expects anchors to # update them when they have. if transaction.protocol == Transaction.PROTOCOL.sep31: # SEP-31 uses 'pending_receiver' status transaction.status = Transaction.STATUS.pending_receiver transaction.save() else: # SEP-6 and 24 uses 'pending_anchor' status transaction.status = Transaction.STATUS.pending_anchor transaction.save() return