diff --git a/python/.changelog.d/1745.added b/python/.changelog.d/1745.added new file mode 100644 index 000000000..734ee5dea --- /dev/null +++ b/python/.changelog.d/1745.added @@ -0,0 +1 @@ +`trezorlib.stellar.from_envelope` was added, it includes support for the Stellar [TransactionV1](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0015.md#xdr) format transaction. diff --git a/python/.changelog.d/1745.incompatible b/python/.changelog.d/1745.incompatible new file mode 100644 index 000000000..e875e5ab3 --- /dev/null +++ b/python/.changelog.d/1745.incompatible @@ -0,0 +1 @@ +`trezorlib.stellar` was reworked to use stellar-sdk instead of providing local implementations diff --git a/python/README.md b/python/README.md index c11aa239e..42b158e0f 100644 --- a/python/README.md +++ b/python/README.md @@ -55,7 +55,14 @@ units) will not be recognized, unless you install HIDAPI support (see below). pip3 install trezor[ethereum] ``` -To install both, use `pip3 install trezor[hidapi,ethereum]`. +* **Stellar**: To support Stellar signing from command line, additional packages are + needed. Install with: + + ```sh + pip3 install trezor[stellar] + ``` + +To install all three, use `pip3 install trezor[hidapi,ethereum,stellar]`. ### Distro packages diff --git a/python/requirements-optional.txt b/python/requirements-optional.txt index 5e6a18370..cd93c7675 100644 --- a/python/requirements-optional.txt +++ b/python/requirements-optional.txt @@ -2,3 +2,4 @@ hidapi >= 0.7.99.post20 rlp >= 1.1.0 web3 >= 4.8 Pillow +stellar-sdk>=4.0.0,<5.0.0 diff --git a/python/setup.py b/python/setup.py index 43506d360..24be2253d 100755 --- a/python/setup.py +++ b/python/setup.py @@ -22,6 +22,7 @@ extras_require = { "ethereum": ["rlp>=1.1.0", "web3>=4.8"], "qt-widgets": ["PyQt5"], "extra": ["Pillow"], + "stellar": ["stellar-sdk>=4.0.0,<5.0.0"], } extras_require["full"] = sum(extras_require.values(), []) diff --git a/python/src/trezorlib/cli/stellar.py b/python/src/trezorlib/cli/stellar.py index c065e5833..5c93c57be 100644 --- a/python/src/trezorlib/cli/stellar.py +++ b/python/src/trezorlib/cli/stellar.py @@ -15,12 +15,21 @@ # If not, see . import base64 +import sys import click from .. import stellar, tools from . import with_client +try: + from stellar_sdk import ( + parse_transaction_envelope_from_xdr, + FeeBumpTransactionEnvelope, + ) +except ImportError: + pass + PATH_HELP = "BIP32 path. Always use hardened paths and the m/44'/148'/ prefix" @@ -68,8 +77,20 @@ def sign_transaction(client, b64envelope, address, network_passphrase): For testnet transactions, use the following network passphrase: 'Test SDF Network ; September 2015' """ + if not stellar.HAVE_STELLAR_SDK: + click.echo("Stellar requirements not installed.") + click.echo("Please run:") + click.echo() + click.echo(" pip install stellar-sdk") + sys.exit(1) + + envelope = parse_transaction_envelope_from_xdr(b64envelope, network_passphrase) + if isinstance(envelope, FeeBumpTransactionEnvelope): + click.echo("FeeBumpTransactionEnvelope is not supported") + sys.exit(1) + address_n = tools.parse_path(address) - tx, operations = stellar.parse_transaction_bytes(base64.b64decode(b64envelope)) + tx, operations = stellar.from_envelope(envelope) resp = stellar.sign_tx(client, tx, operations, address_n, network_passphrase) return base64.b64encode(resp.signature) diff --git a/python/src/trezorlib/stellar.py b/python/src/trezorlib/stellar.py index 37c82b370..d3b0f7b78 100644 --- a/python/src/trezorlib/stellar.py +++ b/python/src/trezorlib/stellar.py @@ -13,14 +13,45 @@ # # You should have received a copy of the License along with this library. # If not, see . - -import base64 -import struct -import xdrlib +from decimal import Decimal +from typing import Union from . import exceptions, messages from .tools import expect +try: + from stellar_sdk import ( + AccountMerge, + AllowTrust, + Asset, + BumpSequence, + ChangeTrust, + CreateAccount, + CreatePassiveSellOffer, + HashMemo, + IdMemo, + ManageData, + ManageSellOffer, + Operation, + PathPaymentStrictReceive, + Payment, + ReturnHashMemo, + SetOptions, + TextMemo, + TransactionEnvelope, + TrustLineEntryFlag, + Price, + Network, + ) + from stellar_sdk.xdr.signer_key_type import SignerKeyType + + HAVE_STELLAR_SDK = True + DEFAULT_NETWORK_PASSPHRASE = Network.PUBLIC_NETWORK_PASSPHRASE + +except ImportError: + HAVE_STELLAR_SDK = False + DEFAULT_NETWORK_PASSPHRASE = "Public Global Stellar Network ; September 2015" + # Memo types MEMO_TYPE_NONE = 0 MEMO_TYPE_TEXT = 1 @@ -33,309 +64,191 @@ ASSET_TYPE_NATIVE = 0 ASSET_TYPE_ALPHA4 = 1 ASSET_TYPE_ALPHA12 = 2 -# Operations -OP_CREATE_ACCOUNT = 0 -OP_PAYMENT = 1 -OP_PATH_PAYMENT = 2 -OP_MANAGE_OFFER = 3 -OP_CREATE_PASSIVE_OFFER = 4 -OP_SET_OPTIONS = 5 -OP_CHANGE_TRUST = 6 -OP_ALLOW_TRUST = 7 -OP_ACCOUNT_MERGE = 8 -OP_INFLATION = 9 # Included for documentation purposes, not supported by Trezor -OP_MANAGE_DATA = 10 -OP_BUMP_SEQUENCE = 11 - - DEFAULT_BIP32_PATH = "m/44h/148h/0h" # Stellar's BIP32 differs to Bitcoin's see https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0005.md -DEFAULT_NETWORK_PASSPHRASE = "Public Global Stellar Network ; September 2015" -def address_from_public_key(pk_bytes): - """Returns the base32-encoded version of pk_bytes (G...)""" - final_bytes = bytearray() - - # version - final_bytes.append(6 << 3) - # public key - final_bytes.extend(pk_bytes) - # checksum - final_bytes.extend(struct.pack(" max_timebound or tx.timebounds_start < 0: - raise ValueError( - "Starting timebound out of range (must be between 0 and " - + max_timebound - ) - if tx.timebounds_end > max_timebound or tx.timebounds_end < 0: - raise ValueError( - "Ending timebound out of range (must be between 0 and " + max_timebound - ) - - # memo type determines what optional fields are set - tx.memo_type = unpacker.unpack_uint() - - # text - if tx.memo_type == MEMO_TYPE_TEXT: - tx.memo_text = unpacker.unpack_string().decode() - # id (64-bit uint) - if tx.memo_type == MEMO_TYPE_ID: - tx.memo_id = unpacker.unpack_uhyper() - # hash / return are the same structure (32 bytes representing a hash) - if tx.memo_type == MEMO_TYPE_HASH or tx.memo_type == MEMO_TYPE_RETURN: - tx.memo_hash = unpacker.unpack_fopaque(32) - - tx.num_operations = unpacker.unpack_uint() - - operations = [] - for _ in range(tx.num_operations): - operations.append(_parse_operation_bytes(unpacker)) + memo = parsed_tx.memo + if isinstance(memo, TextMemo): + # memo_text is specified as UTF-8 string, but returned as bytes from the XDR parser + tx.memo_type = MEMO_TYPE_TEXT + tx.memo_text = memo.memo_text.decode("utf-8") + elif isinstance(memo, IdMemo): + tx.memo_type = MEMO_TYPE_ID + tx.memo_id = memo.memo_id + elif isinstance(memo, HashMemo): + tx.memo_type = MEMO_TYPE_HASH + tx.memo_hash = memo.memo_hash + elif isinstance(memo, ReturnHashMemo): + tx.memo_type = MEMO_TYPE_RETURN + tx.memo_hash = memo.memo_return + else: + tx.memo_type = MEMO_TYPE_NONE + tx.num_operations = len(parsed_tx.operations) + operations = [_read_operation(op) for op in parsed_tx.operations] return tx, operations -def _parse_operation_bytes(unpacker): - """Returns a protobuf message representing the next operation as read from - the byte stream in unpacker - """ - - # Check for and parse optional source account field - source_account = None - if unpacker.unpack_bool(): - source_account = unpacker.unpack_fopaque(32) - - # Operation type (See OP_ constants) - type = unpacker.unpack_uint() - - if type == OP_CREATE_ACCOUNT: +def _read_operation(op: "Operation"): + # TODO: Let's add muxed account support later. + if op.source: + source_account = op.source.account_id + else: + source_account = None + if isinstance(op, CreateAccount): return messages.StellarCreateAccountOp( source_account=source_account, - new_account=_xdr_read_address(unpacker), - starting_balance=unpacker.unpack_hyper(), + new_account=op.destination, + starting_balance=_read_amount(op.starting_balance), ) - - if type == OP_PAYMENT: + if isinstance(op, Payment): return messages.StellarPaymentOp( source_account=source_account, - destination_account=_xdr_read_address(unpacker), - asset=_xdr_read_asset(unpacker), - amount=unpacker.unpack_hyper(), + destination_account=op.destination.account_id, + asset=_read_asset(op.asset), + amount=_read_amount(op.amount), ) - - if type == OP_PATH_PAYMENT: - op = messages.StellarPathPaymentOp( + if isinstance(op, PathPaymentStrictReceive): + operation = messages.StellarPathPaymentOp( source_account=source_account, - send_asset=_xdr_read_asset(unpacker), - send_max=unpacker.unpack_hyper(), - destination_account=_xdr_read_address(unpacker), - destination_asset=_xdr_read_asset(unpacker), - destination_amount=unpacker.unpack_hyper(), - paths=[], + send_asset=_read_asset(op.send_asset), + send_max=_read_amount(op.send_max), + destination_account=op.destination.account_id, + destination_asset=_read_asset(op.dest_asset), + destination_amount=_read_amount(op.dest_amount), + paths=[_read_asset(asset) for asset in op.path], ) - - num_paths = unpacker.unpack_uint() - for _ in range(num_paths): - op.paths.append(_xdr_read_asset(unpacker)) - - return op - - if type == OP_MANAGE_OFFER: + return operation + if isinstance(op, ManageSellOffer): + price = _read_price(op.price) return messages.StellarManageOfferOp( source_account=source_account, - selling_asset=_xdr_read_asset(unpacker), - buying_asset=_xdr_read_asset(unpacker), - amount=unpacker.unpack_hyper(), - price_n=unpacker.unpack_uint(), - price_d=unpacker.unpack_uint(), - offer_id=unpacker.unpack_uhyper(), + selling_asset=_read_asset(op.selling), + buying_asset=_read_asset(op.buying), + amount=_read_amount(op.amount), + price_n=price.n, + price_d=price.d, + offer_id=op.offer_id, ) - - if type == OP_CREATE_PASSIVE_OFFER: + if isinstance(op, CreatePassiveSellOffer): + price = _read_price(op.price) return messages.StellarCreatePassiveOfferOp( source_account=source_account, - selling_asset=_xdr_read_asset(unpacker), - buying_asset=_xdr_read_asset(unpacker), - amount=unpacker.unpack_hyper(), - price_n=unpacker.unpack_uint(), - price_d=unpacker.unpack_uint(), + selling_asset=_read_asset(op.selling), + buying_asset=_read_asset(op.buying), + amount=_read_amount(op.amount), + price_n=price.n, + price_d=price.d, ) - - if type == OP_SET_OPTIONS: - op = messages.StellarSetOptionsOp(source_account=source_account) - - # Inflation destination - if unpacker.unpack_bool(): - op.inflation_destination_account = _xdr_read_address(unpacker) - - # clear flags - if unpacker.unpack_bool(): - op.clear_flags = unpacker.unpack_uint() - - # set flags - if unpacker.unpack_bool(): - op.set_flags = unpacker.unpack_uint() - - # master weight - if unpacker.unpack_bool(): - op.master_weight = unpacker.unpack_uint() - - # low threshold - if unpacker.unpack_bool(): - op.low_threshold = unpacker.unpack_uint() - - # medium threshold - if unpacker.unpack_bool(): - op.medium_threshold = unpacker.unpack_uint() - - # high threshold - if unpacker.unpack_bool(): - op.high_threshold = unpacker.unpack_uint() - - # home domain - if unpacker.unpack_bool(): - op.home_domain = unpacker.unpack_string().decode() - - # signer - if unpacker.unpack_bool(): - op.signer_type = unpacker.unpack_uint() - op.signer_key = unpacker.unpack_fopaque(32) - op.signer_weight = unpacker.unpack_uint() - - return op - - if type == OP_CHANGE_TRUST: + if isinstance(op, SetOptions): + operation = messages.StellarSetOptionsOp( + source_account=source_account, + inflation_destination_account=op.inflation_dest, + clear_flags=op.clear_flags, + set_flags=op.set_flags, + master_weight=op.master_weight, + low_threshold=op.low_threshold, + medium_threshold=op.med_threshold, + high_threshold=op.high_threshold, + home_domain=op.home_domain, + ) + if op.signer: + signer_type = op.signer.signer_key.signer_key.type + if signer_type == SignerKeyType.SIGNER_KEY_TYPE_ED25519: + signer_key = op.signer.signer_key.signer_key.ed25519.uint256 + elif signer_type == SignerKeyType.SIGNER_KEY_TYPE_HASH_X: + signer_key = op.signer.signer_key.signer_key.hash_x.uint256 + elif signer_type == SignerKeyType.SIGNER_KEY_TYPE_PRE_AUTH_TX: + signer_key = op.signer.signer_key.signer_key.pre_auth_tx.uint256 + else: + raise ValueError("Unsupported signer key type") + operation.signer_type = signer_type.value + operation.signer_key = signer_key + operation.signer_weight = op.signer.weight + return operation + if isinstance(op, ChangeTrust): return messages.StellarChangeTrustOp( source_account=source_account, - asset=_xdr_read_asset(unpacker), - limit=unpacker.unpack_uhyper(), + asset=_read_asset(op.asset), + limit=_read_amount(op.limit), ) - - if type == OP_ALLOW_TRUST: - op = messages.StellarAllowTrustOp( + if isinstance(op, AllowTrust): + is_authorized = False + if op.authorize is True or TrustLineEntryFlag.AUTHORIZED_FLAG == op.authorize: + is_authorized = True + asset_type = ( + ASSET_TYPE_ALPHA4 if len(op.asset_code) <= 4 else ASSET_TYPE_ALPHA12 + ) + return messages.StellarAllowTrustOp( source_account=source_account, - trusted_account=_xdr_read_address(unpacker), - asset_type=unpacker.unpack_uint(), + trusted_account=op.trustor, + asset_type=asset_type, + asset_code=op.asset_code, + is_authorized=is_authorized, ) - - if op.asset_type == ASSET_TYPE_ALPHA4: - op.asset_code = unpacker.unpack_fstring(4).decode() - if op.asset_type == ASSET_TYPE_ALPHA12: - op.asset_code = unpacker.unpack_fstring(12).decode() - - op.is_authorized = unpacker.unpack_bool() - - return op - - if type == OP_ACCOUNT_MERGE: + if isinstance(op, AccountMerge): return messages.StellarAccountMergeOp( source_account=source_account, - destination_account=_xdr_read_address(unpacker), + destination_account=op.destination.account_id, ) - # Inflation is not implemented since anyone can submit this operation to the network - - if type == OP_MANAGE_DATA: - op = messages.StellarManageDataOp( - source_account=source_account, key=unpacker.unpack_string().decode() + if isinstance(op, ManageData): + return messages.StellarManageDataOp( + source_account=source_account, + key=op.data_name, + value=op.data_value, ) - - # Only set value if the field is present - if unpacker.unpack_bool(): - op.value = unpacker.unpack_opaque() - - return op - - # Bump Sequence - # see: https://github.com/stellar/stellar-core/blob/master/src/xdr/Stellar-transaction.x#L269 - if type == OP_BUMP_SEQUENCE: + if isinstance(op, BumpSequence): return messages.StellarBumpSequenceOp( - source_account=source_account, bump_to=unpacker.unpack_uhyper() + source_account=source_account, bump_to=op.bump_to ) - - raise ValueError("Unknown operation type: " + str(type)) + raise ValueError(f"Unknown operation type: {op.__class__.__name__}") -def _xdr_read_asset(unpacker): +def _read_amount(amount: str) -> int: + return Operation.to_xdr_amount(amount) + + +def _read_price(price: Union["Price", str, Decimal]) -> "Price": + # In the coming stellar-sdk 5.x, the type of price must be Price, + # at that time we can remove this function + if isinstance(price, Price): + return price + return Price.from_raw_price(price) + + +def _read_asset(asset: "Asset") -> messages.StellarAssetType: """Reads a stellar Asset from unpacker""" - asset = messages.StellarAssetType(type=unpacker.unpack_uint()) - - if asset.type == ASSET_TYPE_ALPHA4: - asset.code = unpacker.unpack_fstring(4).decode() - asset.issuer = _xdr_read_address(unpacker) - - if asset.type == ASSET_TYPE_ALPHA12: - asset.code = unpacker.unpack_fstring(12).decode() - asset.issuer = _xdr_read_address(unpacker) - - return asset - - -def _xdr_read_address(unpacker): - """Reads a stellar address and returns the string representing the address - This method assumes the encoded address is a public address (starting with G) - """ - # First 4 bytes are the address type - address_type = unpacker.unpack_uint() - if address_type != 0: - raise ValueError("Unsupported address type") - - return address_from_public_key(unpacker.unpack_fopaque(32)) - - -def _crc16_checksum(bytes): - """Returns the CRC-16 checksum of bytearray bytes - - Ported from Java implementation at: http://introcs.cs.princeton.edu/java/61data/CRC16CCITT.java.html - - Initial value changed to 0x0000 to match Stellar configuration. - """ - crc = 0x0000 - polynomial = 0x1021 - - for byte in bytes: - for i in range(8): - bit = (byte >> (7 - i) & 1) == 1 - c15 = (crc >> 15 & 1) == 1 - crc <<= 1 - if c15 ^ bit: - crc ^= polynomial - - return crc & 0xFFFF + if asset.is_native(): + return messages.StellarAssetType(type=ASSET_TYPE_NATIVE) + if asset.guess_asset_type() == "credit_alphanum4": + return messages.StellarAssetType( + type=ASSET_TYPE_ALPHA4, code=asset.code, issuer=asset.issuer + ) + if asset.guess_asset_type() == "credit_alphanum12": + return messages.StellarAssetType( + type=ASSET_TYPE_ALPHA12, code=asset.code, issuer=asset.issuer + ) + raise ValueError("Unsupported asset type") # ====== Client functions ====== # diff --git a/python/tests/test_stellar.py b/python/tests/test_stellar.py index 35cfeeefc..a66d88a14 100644 --- a/python/tests/test_stellar.py +++ b/python/tests/test_stellar.py @@ -1,6 +1,6 @@ # This file is part of the Trezor project. # -# Copyright (C) 2012-2019 SatoshiLabs and contributors +# Copyright (C) 2012-2021 SatoshiLabs and contributors # # This library is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License version 3 @@ -14,378 +14,2095 @@ # You should have received a copy of the License along with this library. # If not, see . -import base64 + +from stellar_sdk import Account, Asset, Network, TransactionBuilder, TrustLineEntryFlag +from stellar_sdk.strkey import StrKey from trezorlib import messages, stellar -def test_stellar_parse_transaction_bytes_simple(): - b64 = b"AAAAABXWSL/k028ZbPtXNf/YylTNS4Iz90PyJEnefPMBzbRpAAAAZAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAXVVkJGaxhbhDFS6eIZFR28WJICfsQBAaUXvtXKAwwuAAAAAAO5/eyAAAAAA=" +def test_stellar_parse_operation_simple_v0(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + data_name = "Trezor" + data_value = b"Hello, Stellar" + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + base_fee = 200 - tx, operations = stellar.parse_transaction_bytes(base64.b64decode(b64)) - - assert ( - tx.source_account == "GAK5MSF74TJW6GLM7NLTL76YZJKM2S4CGP3UH4REJHPHZ4YBZW2GSBPW" - ) - assert tx.fee == 100 - assert tx.sequence_number == 4294967296 - assert tx.timebounds_start is None - assert tx.timebounds_end is None - assert tx.memo_type == stellar.MEMO_TYPE_NONE - assert tx.memo_text is None - assert tx.memo_id is None - assert tx.memo_hash is None - assert tx.num_operations == len(operations) - - -def test_stellar_parse_transaction_bytes_memo_text(): - b64 = b"AAAAABXWSL/k028ZbPtXNf/YylTNS4Iz90PyJEnefPMBzbRpAAAAZAAAAAEAAAAAAAAAAAAAAAEAAAAMZXhhbXBsZSBtZW1vAAAAAQAAAAAAAAAAAAAAAF1VZCRmsYW4QxUuniGRUdvFiSAn7EAQGlF77VygMMLgAAAAADuf3sgAAAAA" - - tx, operations = stellar.parse_transaction_bytes(base64.b64decode(b64)) - - assert ( - tx.source_account == "GAK5MSF74TJW6GLM7NLTL76YZJKM2S4CGP3UH4REJHPHZ4YBZW2GSBPW" - ) - assert tx.fee == 100 - assert tx.sequence_number == 4294967296 - assert tx.timebounds_start is None - assert tx.timebounds_end is None - assert tx.memo_type == stellar.MEMO_TYPE_TEXT - assert tx.memo_text == "example memo" - assert tx.memo_id is None - assert tx.memo_hash is None - assert tx.num_operations == len(operations) - - -def test_stellar_parse_transaction_bytes_memo_id(): - b64 = b"AAAAABXWSL/k028ZbPtXNf/YylTNS4Iz90PyJEnefPMBzbRpAAAAZAAAAAEAAAAAAAAAAAAAAAIAAAAAB1vNFQAAAAEAAAAAAAAAAAAAAABdVWQkZrGFuEMVLp4hkVHbxYkgJ+xAEBpRe+1coDDC4AAAAAA7n97IAAAAAA==" - - tx, operations = stellar.parse_transaction_bytes(base64.b64decode(b64)) - - assert ( - tx.source_account == "GAK5MSF74TJW6GLM7NLTL76YZJKM2S4CGP3UH4REJHPHZ4YBZW2GSBPW" - ) - assert tx.fee == 100 - assert tx.sequence_number == 4294967296 - assert tx.timebounds_start is None - assert tx.timebounds_end is None - assert tx.memo_type == stellar.MEMO_TYPE_ID - assert tx.memo_text is None - assert tx.memo_id == 123456789 - assert tx.memo_hash is None - assert tx.num_operations == len(operations) - - -def test_stellar_parse_transaction_bytes_memo_hash(): - b64 = b"AAAAABXWSL/k028ZbPtXNf/YylTNS4Iz90PyJEnefPMBzbRpAAAAZAAAAAEAAAAAAAAAAAAAAAMjLtb5+r8U47tVOSsYz+PQ/ryU0gzGMnw4odB11uoRjAAAAAEAAAAAAAAAAAAAAABdVWQkZrGFuEMVLp4hkVHbxYkgJ+xAEBpRe+1coDDC4AAAAAA7n97IAAAAAA==" - - tx, operations = stellar.parse_transaction_bytes(base64.b64decode(b64)) - - assert ( - tx.source_account == "GAK5MSF74TJW6GLM7NLTL76YZJKM2S4CGP3UH4REJHPHZ4YBZW2GSBPW" - ) - assert tx.fee == 100 - assert tx.sequence_number == 4294967296 - assert tx.timebounds_start is None - assert tx.timebounds_end is None - assert tx.memo_type == stellar.MEMO_TYPE_HASH - assert tx.memo_text is None - assert tx.memo_id is None - # base-64 encoding of the raw bytes of sha256('stellar') - assert ( - base64.b64encode(tx.memo_hash) - == b"Iy7W+fq/FOO7VTkrGM/j0P68lNIMxjJ8OKHQddbqEYw=" - ) - assert tx.num_operations == len(operations) - - -def test_stellar_parse_transaction_bytes_memo_return(): - b64 = b"AAAAABXWSL/k028ZbPtXNf/YylTNS4Iz90PyJEnefPMBzbRpAAAAZAAAAAEAAAAAAAAAAAAAAAQjLtb5+r8U47tVOSsYz+PQ/ryU0gzGMnw4odB11uoRjAAAAAEAAAAAAAAAAAAAAABdVWQkZrGFuEMVLp4hkVHbxYkgJ+xAEBpRe+1coDDC4AAAAAA7n97IAAAAAA==" - - tx, operations = stellar.parse_transaction_bytes(base64.b64decode(b64)) - - assert ( - tx.source_account == "GAK5MSF74TJW6GLM7NLTL76YZJKM2S4CGP3UH4REJHPHZ4YBZW2GSBPW" - ) - assert tx.fee == 100 - assert tx.sequence_number == 4294967296 - assert tx.timebounds_start is None - assert tx.timebounds_end is None - assert tx.memo_type == stellar.MEMO_TYPE_RETURN - assert tx.memo_text is None - assert tx.memo_id is None - # base-64 encoding of the raw bytes of sha256('stellar') - assert ( - base64.b64encode(tx.memo_hash) - == b"Iy7W+fq/FOO7VTkrGM/j0P68lNIMxjJ8OKHQddbqEYw=" - ) - assert tx.num_operations == len(operations) - - -def test_stellar_parse_operation_bytes_create_account_simple(): - b64 = b"AAAAABXWSL/k028ZbPtXNf/YylTNS4Iz90PyJEnefPMBzbRpAAAAZAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAXVVkJGaxhbhDFS6eIZFR28WJICfsQBAaUXvtXKAwwuAAAAAAO5/eyAAAAAA=" - - tx, operations = stellar.parse_transaction_bytes(base64.b64decode(b64)) - op = operations[0] - - assert isinstance(op, messages.StellarCreateAccountOp) - assert op.source_account is None - assert op.new_account == "GBOVKZBEM2YYLOCDCUXJ4IMRKHN4LCJAE7WEAEA2KF562XFAGDBOB64V" - assert op.starting_balance == 1000333000 - - -def test_stellar_parse_operation_bytes_payment_native(): - b64 = b"AAAAABXWSL/k028ZbPtXNf/YylTNS4Iz90PyJEnefPMBzbRpAAAAZAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAEAAAAAXVVkJGaxhbhDFS6eIZFR28WJICfsQBAaUXvtXKAwwuAAAAAAAAAAAB3PFpgAAAAA" - - tx, operations = stellar.parse_transaction_bytes(base64.b64decode(b64)) - op = operations[0] - - assert isinstance(op, messages.StellarPaymentOp) - assert op.source_account is None - assert ( - op.destination_account - == "GBOVKZBEM2YYLOCDCUXJ4IMRKHN4LCJAE7WEAEA2KF562XFAGDBOB64V" - ) - assert op.asset.type == stellar.ASSET_TYPE_NATIVE - assert op.amount == 500111000 - - -def test_stellar_parse_operation_bytes_payment_custom4(): - b64 = b"AAAAABXWSL/k028ZbPtXNf/YylTNS4Iz90PyJEnefPMBzbRpAAAAZAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAEAAAAAXVVkJGaxhbhDFS6eIZFR28WJICfsQBAaUXvtXKAwwuAAAAABVEVTVAAAAAAphJYCwg5YNl8SPBLYehykVQ0QzSGwrg4Y1E4+Vv1qFQAAAAAdzxaYAAAAAA==" - - tx, operations = stellar.parse_transaction_bytes(base64.b64decode(b64)) - op = operations[0] - - assert op.source_account is None - assert ( - op.destination_account - == "GBOVKZBEM2YYLOCDCUXJ4IMRKHN4LCJAE7WEAEA2KF562XFAGDBOB64V" - ) - assert op.asset.type == stellar.ASSET_TYPE_ALPHA4 - assert op.asset.code == "TEST" - assert op.asset.issuer == "GAUYJFQCYIHFQNS7CI6BFWD2DSSFKDIQZUQ3BLQODDKE4PSW7VVBKENC" - assert op.amount == 500111000 - - -def test_stellar_parse_operation_bytes_payment_custom7(): - b64 = b"AAAAABXWSL/k028ZbPtXNf/YylTNS4Iz90PyJEnefPMBzbRpAAAAZAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAEAAAAAXVVkJGaxhbhDFS6eIZFR28WJICfsQBAaUXvtXKAwwuAAAAACU0VWRU5YWAAAAAAAAAAAACmElgLCDlg2XxI8Eth6HKRVDRDNIbCuDhjUTj5W/WoVAAAAAB3PFpgAAAAA" - - tx, operations = stellar.parse_transaction_bytes(base64.b64decode(b64)) - op = operations[0] - - assert isinstance(op, messages.StellarPaymentOp) - assert op.source_account is None - assert ( - op.destination_account - == "GBOVKZBEM2YYLOCDCUXJ4IMRKHN4LCJAE7WEAEA2KF562XFAGDBOB64V" - ) - assert op.asset.type == stellar.ASSET_TYPE_ALPHA12 - # asset codes are either 4 or 12 characters, so this will be null-padded at the end - assert op.asset.code == "SEVENXX\x00\x00\x00\x00\x00" - assert op.asset.issuer == "GAUYJFQCYIHFQNS7CI6BFWD2DSSFKDIQZUQ3BLQODDKE4PSW7VVBKENC" - assert op.amount == 500111000 - - -def test_stellar_parse_operation_bytes_path_payment_none(): - b64 = b"AAAAABXWSL/k028ZbPtXNf/YylTNS4Iz90PyJEnefPMBzbRpAAAAZAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAIAAAAAAAAAAHfOKn8AAAAAXVVkJGaxhbhDFS6eIZFR28WJICfsQBAaUXvtXKAwwuAAAAABSlBZAAAAAADE+xa3Eb3cy85WSdqgwnUtC6UDwrC41YDANuCqe8vGxgAAAAAL68IBAAAAAAAAAAA=" - - tx, operations = stellar.parse_transaction_bytes(base64.b64decode(b64)) - op = operations[0] - - assert isinstance(op, messages.StellarPathPaymentOp) - assert op.source_account is None - assert ( - op.destination_account - == "GBOVKZBEM2YYLOCDCUXJ4IMRKHN4LCJAE7WEAEA2KF562XFAGDBOB64V" + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=base_fee, + v1=False, + ) + .append_manage_data_op( + data_name=data_name, data_value=data_value, source=operation_source + ) + .build() ) - assert op.send_asset.type == stellar.ASSET_TYPE_NATIVE - assert op.send_max == 2009999999 + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert parsed_tx.source_account == tx_source + assert parsed_tx.fee == envelope.transaction.fee + assert parsed_tx.sequence_number == sequence + 1 + assert parsed_tx.timebounds_start is None + assert parsed_tx.timebounds_end is None + assert parsed_tx.memo_type == stellar.MEMO_TYPE_NONE + assert parsed_tx.memo_text is None + assert parsed_tx.memo_id is None + assert parsed_tx.memo_hash is None + assert len(parsed_operations) == 1 - assert ( - op.destination_account - == "GBOVKZBEM2YYLOCDCUXJ4IMRKHN4LCJAE7WEAEA2KF562XFAGDBOB64V" - ) - assert op.destination_asset.type == stellar.ASSET_TYPE_ALPHA4 - # asset codes are either 4 or 12 characters, so this will be null-padded at the end - assert op.destination_asset.code == "JPY\x00" - assert ( - op.destination_asset.issuer - == "GDCPWFVXCG65ZS6OKZE5VIGCOUWQXJIDYKYLRVMAYA3OBKT3ZPDMNTIJ" + +def test_stellar_parse_transaction_memo_text_v0(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + data_name = "Trezor" + data_value = b"Hello, Stellar" + memo_text = b"Have a nice day!" + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + base_fee = 200 + + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=base_fee, + v1=False, + ) + .add_text_memo(memo_text=memo_text) + .append_manage_data_op( + data_name=data_name, data_value=data_value, source=operation_source + ) + .build() ) - assert len(op.paths) == 0 + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert parsed_tx.source_account == tx_source + assert parsed_tx.fee == envelope.transaction.fee + assert parsed_tx.sequence_number == sequence + 1 + assert parsed_tx.timebounds_start is None + assert parsed_tx.timebounds_end is None + assert parsed_tx.memo_type == stellar.MEMO_TYPE_TEXT + assert parsed_tx.memo_text == memo_text.decode("utf-8") + assert parsed_tx.memo_id is None + assert parsed_tx.memo_hash is None + assert len(parsed_operations) == 1 -def test_stellar_parse_operation_bytes_path_payment_one(): - b64 = b"AAAAABXWSL/k028ZbPtXNf/YylTNS4Iz90PyJEnefPMBzbRpAAAAZAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAIAAAAAAAAAAHfOKn8AAAAAXVVkJGaxhbhDFS6eIZFR28WJICfsQBAaUXvtXKAwwuAAAAABSlBZAAAAAADE+xa3Eb3cy85WSdqgwnUtC6UDwrC41YDANuCqe8vGxgAAAAAL68IBAAAAAQAAAAFQVEgxAAAAAMz/d9fJ3rFifblw3jT7sRZv/Ja+fqLfob//aLZQRQibAAAAAA==" +def test_stellar_parse_transaction_bytes_memo_id_v0(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + data_name = "Trezor" + data_value = b"Hello, Stellar" + memo_id = 123456789 + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + base_fee = 200 - tx, operations = stellar.parse_transaction_bytes(base64.b64decode(b64)) - op = operations[0] - - assert isinstance(op, messages.StellarPathPaymentOp) - assert op.source_account is None - assert ( - op.destination_account - == "GBOVKZBEM2YYLOCDCUXJ4IMRKHN4LCJAE7WEAEA2KF562XFAGDBOB64V" + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=base_fee, + v1=False, + ) + .add_id_memo(memo_id) + .append_manage_data_op( + data_name=data_name, data_value=data_value, source=operation_source + ) + .build() ) - assert op.send_asset.type == stellar.ASSET_TYPE_NATIVE - assert op.send_max == 2009999999 + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert parsed_tx.source_account == tx_source + assert parsed_tx.fee == envelope.transaction.fee + assert parsed_tx.sequence_number == sequence + 1 + assert parsed_tx.timebounds_start is None + assert parsed_tx.timebounds_end is None + assert parsed_tx.memo_type == stellar.MEMO_TYPE_ID + assert parsed_tx.memo_text is None + assert parsed_tx.memo_id == memo_id + assert parsed_tx.memo_hash is None + assert len(parsed_operations) == 1 - assert ( - op.destination_account - == "GBOVKZBEM2YYLOCDCUXJ4IMRKHN4LCJAE7WEAEA2KF562XFAGDBOB64V" - ) - assert op.destination_asset.type == stellar.ASSET_TYPE_ALPHA4 - # asset codes are either 4 or 12 characters, so this will be null-padded at the end - assert op.destination_asset.code == "JPY\x00" - assert ( - op.destination_asset.issuer - == "GDCPWFVXCG65ZS6OKZE5VIGCOUWQXJIDYKYLRVMAYA3OBKT3ZPDMNTIJ" - ) - assert op.destination_amount == 200000001 - assert len(op.paths) == 1 - assert op.paths[0].type == stellar.ASSET_TYPE_ALPHA4 - assert op.paths[0].code == "PTH1" - assert ( - op.paths[0].issuer == "GDGP656XZHPLCYT5XFYN4NH3WELG77EWXZ7KFX5BX77WRNSQIUEJXAJK" +def test_stellar_parse_transaction_memo_hash_v0(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + data_name = "Trezor" + data_value = b"Hello, Stellar" + memo_hash = "b77cd735095e1b58da2d7415c1f51f423a722b34d7d5002d8896608a9130a74b" + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + base_fee = 200 + + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=base_fee, + v1=False, + ) + .add_hash_memo(memo_hash) + .append_manage_data_op( + data_name=data_name, data_value=data_value, source=operation_source + ) + .build() ) + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert parsed_tx.source_account == tx_source + assert parsed_tx.fee == envelope.transaction.fee + assert parsed_tx.sequence_number == sequence + 1 + assert parsed_tx.timebounds_start is None + assert parsed_tx.timebounds_end is None + assert parsed_tx.memo_type == stellar.MEMO_TYPE_HASH + assert parsed_tx.memo_text is None + assert parsed_tx.memo_id is None + assert parsed_tx.memo_hash.hex() == memo_hash + assert len(parsed_operations) == 1 -def test_stellar_parse_operation_bytes_manage_offer_new(): - b64 = b"AAAAABXWSL/k028ZbPtXNf/YylTNS4Iz90PyJEnefPMBzbRpAAAAZAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAMAAAAAAAAAAVVTRAAAAAAABkAD8fq0d+bofA1LCatUL0dCTJexnyYYd4Y1ghnNUXMAAAAAdzWUAAAKSzYAD0JAAAAAAAAAAAAAAAAA" - tx, operations = stellar.parse_transaction_bytes(base64.b64decode(b64)) - op = operations[0] +def test_stellar_parse_transaction_memo_return_hash_v0(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + data_name = "Trezor" + data_value = b"Hello, Stellar" + memo_return = "b77cd735095e1b58da2d7415c1f51f423a722b34d7d5002d8896608a9130a74b" + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + base_fee = 200 - assert isinstance(op, messages.StellarManageOfferOp) - assert op.source_account is None - - assert op.selling_asset.type == stellar.ASSET_TYPE_NATIVE - - assert op.buying_asset.type == stellar.ASSET_TYPE_ALPHA4 - # asset codes are either 4 or 12 characters, so this will be null-padded at the end - assert op.buying_asset.code == "USD\x00" - assert ( - op.buying_asset.issuer - == "GADEAA7R7K2HPZXIPQGUWCNLKQXUOQSMS6YZ6JQYO6DDLAQZZVIXG74A" + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=base_fee, + v1=False, + ) + .add_return_hash_memo(memo_return) + .append_manage_data_op( + data_name=data_name, data_value=data_value, source=operation_source + ) + .build() ) - assert op.amount == 2000000000 - assert op.price_n == 674614 - assert op.price_d == 1000000 - assert op.offer_id == 0 # indicates a new offer + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert parsed_tx.source_account == tx_source + assert parsed_tx.fee == envelope.transaction.fee + assert parsed_tx.sequence_number == sequence + 1 + assert parsed_tx.timebounds_start is None + assert parsed_tx.timebounds_end is None + assert parsed_tx.memo_type == stellar.MEMO_TYPE_RETURN + assert parsed_tx.memo_text is None + assert parsed_tx.memo_id is None + assert parsed_tx.memo_hash.hex() == memo_return + assert len(parsed_operations) == 1 -def test_stellar_parse_operation_bytes_passive_offer_new(): - b64 = b"AAAAABXWSL/k028ZbPtXNf/YylTNS4Iz90PyJEnefPMBzbRpAAAAZAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAQAAAAAAAAAAVVTRAAAAAAABkAD8fq0d+bofA1LCatUL0dCTJexnyYYd4Y1ghnNUXMAAAAAdzWUAAAKSzYAD0JAAAAAAA==" +def test_stellar_parse_transaction_time_bounds_v0(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + data_name = "Trezor" + data_value = b"Hello, Stellar" + min_time = 1628089098 + max_time = 1628090000 + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + base_fee = 200 - tx, operations = stellar.parse_transaction_bytes(base64.b64decode(b64)) - op = operations[0] - - assert isinstance(op, messages.StellarCreatePassiveOfferOp) - assert op.source_account is None - - assert op.selling_asset.type == stellar.ASSET_TYPE_NATIVE - - assert op.buying_asset.type == stellar.ASSET_TYPE_ALPHA4 - # asset codes are either 4 or 12 characters, so this will be null-padded at the end - assert op.buying_asset.code == "USD\x00" - assert ( - op.buying_asset.issuer - == "GADEAA7R7K2HPZXIPQGUWCNLKQXUOQSMS6YZ6JQYO6DDLAQZZVIXG74A" + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=base_fee, + v1=False, + ) + .add_time_bounds(min_time=min_time, max_time=max_time) + .append_manage_data_op( + data_name=data_name, data_value=data_value, source=operation_source + ) + .build() ) - assert op.amount == 2000000000 - assert op.price_n == 674614 - assert op.price_d == 1000000 + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert parsed_tx.source_account == tx_source + assert parsed_tx.fee == envelope.transaction.fee + assert parsed_tx.sequence_number == sequence + 1 + assert parsed_tx.timebounds_start == min_time + assert parsed_tx.timebounds_end == max_time + assert parsed_tx.memo_type == stellar.MEMO_TYPE_NONE + assert parsed_tx.memo_text is None + assert parsed_tx.memo_id is None + assert parsed_tx.memo_hash is None + assert len(parsed_operations) == 1 -def test_stellar_parse_operation_bytes_set_options_inflation(): - b64 = b"AAAAABXWSL/k028ZbPtXNf/YylTNS4Iz90PyJEnefPMBzbRpAAAAZAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAUAAAABAAAAAAt5i66vbwH70/2M4Oj0rQW81SNLAjfOsMV2bavzocXhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" +def test_stellar_parse_operation_multiple_operations_v0(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + base_fee = 200 + data_name = "Trezor" + data_value = b"Hello, Stellar" + operation1_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + destination = "GDNSSYSCSSJ76FER5WEEXME5G4MTCUBKDRQSKOYP36KUKVDB2VCMERS6" + amount = "50.0111" + asset_code = "XLM" + asset_issuer = None + operation2_source = "GBHWKBPP3O4H2BUUKSFXE4PK5WHLQYVZIZUNUJ4AU5VUZZEVBDMXISAS" - tx, operations = stellar.parse_transaction_bytes(base64.b64decode(b64)) - op = operations[0] - - assert isinstance(op, messages.StellarSetOptionsOp) - assert op.source_account is None - - assert ( - op.inflation_destination_account - == "GAFXTC5OV5XQD66T7WGOB2HUVUC3ZVJDJMBDPTVQYV3G3K7TUHC6CLBR" + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=base_fee, + v1=False, + ) + .append_manage_data_op( + data_name=data_name, data_value=data_value, source=operation1_source + ) + .append_payment_op( + destination=destination, + amount=amount, + asset_code=asset_code, + asset_issuer=asset_issuer, + source=operation2_source, + ) + .build() ) - -def test_stellar_parse_operation_bytes_change_trust_add(): - b64 = b"AAAAABXWSL/k028ZbPtXNf/YylTNS4Iz90PyJEnefPMBzbRpAAAAZAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAYAAAABVVNEAAAAAACkn7CoQZEWAlyO6z6VBUAddrDDR078TtLt/nP/hZJ9KQAAAAJUC+QAAAAAAA==" - - tx, operations = stellar.parse_transaction_bytes(base64.b64decode(b64)) - op = operations[0] - - assert isinstance(op, messages.StellarChangeTrustOp) - assert op.source_account is None - - assert op.asset.type == stellar.ASSET_TYPE_ALPHA4 - assert op.asset.code == "USD\x00" - assert op.asset.issuer == "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" - - assert op.limit == 10000000000 + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert parsed_tx.source_account == tx_source + assert parsed_tx.fee == envelope.transaction.fee + assert parsed_tx.sequence_number == sequence + 1 + assert parsed_tx.timebounds_start is None + assert parsed_tx.timebounds_end is None + assert parsed_tx.memo_type == stellar.MEMO_TYPE_NONE + assert parsed_tx.memo_text is None + assert parsed_tx.memo_id is None + assert parsed_tx.memo_hash is None + assert len(parsed_operations) == 2 + assert isinstance(parsed_operations[0], messages.StellarManageDataOp) + assert parsed_operations[0].source_account == operation1_source + assert parsed_operations[0].key == data_name + assert parsed_operations[0].value == data_value + assert isinstance(parsed_operations[1], messages.StellarPaymentOp) + assert parsed_operations[1].source_account == operation2_source + assert parsed_operations[1].destination_account == destination + assert parsed_operations[1].asset.type == stellar.ASSET_TYPE_NATIVE + assert parsed_operations[1].asset.code is None + assert parsed_operations[1].asset.issuer is None + assert parsed_operations[1].amount == 500111000 -def test_stellar_parse_operation_bytes_allow_trust_allow(): - b64 = b"AAAAABXWSL/k028ZbPtXNf/YylTNS4Iz90PyJEnefPMBzbRpAAAAZAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAcAAAAAZ0Me3OnxI2tuaC8qt95THF1fuB42qARTnP2ookJapQUAAAABSlBZAAAAAAEAAAAA" +def test_stellar_parse_operation_create_account_v0(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + destination = "GDNSSYSCSSJ76FER5WEEXME5G4MTCUBKDRQSKOYP36KUKVDB2VCMERS6" + starting_balance = "100.0333" + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" - tx, operations = stellar.parse_transaction_bytes(base64.b64decode(b64)) - op = operations[0] - - assert isinstance(op, messages.StellarAllowTrustOp) - assert op.source_account is None - - assert op.asset_type == stellar.ASSET_TYPE_ALPHA4 - assert op.asset_code == "JPY\x00" - - assert ( - op.trusted_account == "GBTUGHW45HYSG23ONAXSVN66KMOF2X5YDY3KQBCTTT62RISCLKSQLYF4" + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=100, + v1=False, + ) + .append_create_account_op( + destination=destination, + starting_balance=starting_balance, + source=operation_source, + ) + .build() ) + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert len(parsed_operations) == 1 + parsed_operation = parsed_operations[0] + assert isinstance(parsed_operation, messages.StellarCreateAccountOp) + assert parsed_operation.source_account == operation_source + assert parsed_operation.new_account == destination + assert parsed_operation.starting_balance == 1000333000 -def test_stellar_parse_operation_bytes_account_merge_simple(): - b64 = b"AAAAABXWSL/k028ZbPtXNf/YylTNS4Iz90PyJEnefPMBzbRpAAAAZAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAgAAAAAXVVkJGaxhbhDFS6eIZFR28WJICfsQBAaUXvtXKAwwuAAAAAA" - tx, operations = stellar.parse_transaction_bytes(base64.b64decode(b64)) - op = operations[0] +def test_stellar_parse_operation_payment_native_asset_v0(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + destination = "GDNSSYSCSSJ76FER5WEEXME5G4MTCUBKDRQSKOYP36KUKVDB2VCMERS6" + amount = "50.0111" + asset_code = "XLM" + asset_issuer = None + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" - assert isinstance(op, messages.StellarAccountMergeOp) - assert op.source_account is None - - assert ( - op.destination_account - == "GBOVKZBEM2YYLOCDCUXJ4IMRKHN4LCJAE7WEAEA2KF562XFAGDBOB64V" + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=100, + v1=False, + ) + .append_payment_op( + destination=destination, + amount=amount, + asset_code=asset_code, + asset_issuer=asset_issuer, + source=operation_source, + ) + .build() ) - -def test_stellar_parse_operation_bytes_manage_data_set_simple(): - b64 = b"AAAAABXWSL/k028ZbPtXNf/YylTNS4Iz90PyJEnefPMBzbRpAAAAZAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAoAAAAJdGVzdCBkYXRhAAAAAAAAAQAAAARhc2RmAAAAAA==" - - tx, operations = stellar.parse_transaction_bytes(base64.b64decode(b64)) - op = operations[0] - - assert isinstance(op, messages.StellarManageDataOp) - assert op.source_account is None - - assert op.key == "test data" - assert op.value == b"asdf" + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert len(parsed_operations) == 1 + parsed_operation = parsed_operations[0] + assert isinstance(parsed_operation, messages.StellarPaymentOp) + assert parsed_operation.source_account == operation_source + assert parsed_operation.destination_account == destination + assert parsed_operation.asset.type == stellar.ASSET_TYPE_NATIVE + assert parsed_operation.asset.code is None + assert parsed_operation.asset.issuer is None + assert parsed_operation.amount == 500111000 -def test_stellar_parse_operation_bytes_bump_sequence_simple(): - b64 = b"AAAAABXWSL/k028ZbPtXNf/YylTNS4Iz90PyJEnefPMBzbRpAAAAZAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAsAAAAASZYC0gAAAAA=" +def test_stellar_parse_operation_payment_alpha4_asset_v0(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + destination = "GDNSSYSCSSJ76FER5WEEXME5G4MTCUBKDRQSKOYP36KUKVDB2VCMERS6" + amount = "50.0111" + asset_code = "USD" + asset_issuer = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" - tx, operations = stellar.parse_transaction_bytes(base64.b64decode(b64)) - op = operations[0] + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=100, + v1=False, + ) + .append_payment_op( + destination=destination, + amount=amount, + asset_code=asset_code, + asset_issuer=asset_issuer, + source=operation_source, + ) + .build() + ) - assert isinstance(op, messages.StellarBumpSequenceOp) - assert op.source_account is None + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert len(parsed_operations) == 1 + parsed_operation = parsed_operations[0] + assert isinstance(parsed_operation, messages.StellarPaymentOp) + assert parsed_operation.source_account == operation_source + assert parsed_operation.destination_account == destination + assert parsed_operation.asset.type == stellar.ASSET_TYPE_ALPHA4 + assert parsed_operation.asset.code == asset_code + assert parsed_operation.asset.issuer == asset_issuer + assert parsed_operation.amount == 500111000 - assert op.bump_to == 1234567890 + +def test_stellar_parse_operation_payment_alpha12_asset_v0(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + destination = "GDNSSYSCSSJ76FER5WEEXME5G4MTCUBKDRQSKOYP36KUKVDB2VCMERS6" + amount = "50.0111" + asset_code = "BANANA" + asset_issuer = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=100, + v1=False, + ) + .append_payment_op( + destination=destination, + amount=amount, + asset_code=asset_code, + asset_issuer=asset_issuer, + source=operation_source, + ) + .build() + ) + + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert len(parsed_operations) == 1 + parsed_operation = parsed_operations[0] + assert isinstance(parsed_operation, messages.StellarPaymentOp) + assert parsed_operation.source_account == operation_source + assert parsed_operation.destination_account == destination + assert parsed_operation.asset.type == stellar.ASSET_TYPE_ALPHA12 + assert parsed_operation.asset.code == asset_code + assert parsed_operation.asset.issuer == asset_issuer + assert parsed_operation.amount == 500111000 + + +def test_stellar_parse_operation_path_payment_strict_receive_v0(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + destination = "GDNSSYSCSSJ76FER5WEEXME5G4MTCUBKDRQSKOYP36KUKVDB2VCMERS6" + send_max = "50.0111" + dest_amount = "100" + send_code = "XLM" + send_issuer = None + dest_code = "USD" + dest_issuer = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + path_asset1 = Asset( + "JPY", "GD6PV7DXQJX7AGVXFQ2MTCLTCH6LR3E6IO2EO2YDZD7F7IOZZCCB5DSQ" + ) + path_asset2 = Asset( + "BANANA", "GC7EKO37HNSKQ3V6RZ274EO7SFOWASQRHLX3OR5FIZK6UMV6LIEDXHGZ" + ) + + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=100, + v1=False, + ) + .append_path_payment_strict_receive_op( + destination=destination, + send_code=send_code, + send_issuer=send_issuer, + send_max=send_max, + dest_code=dest_code, + dest_issuer=dest_issuer, + dest_amount=dest_amount, + path=[path_asset1, path_asset2], + source=operation_source, + ) + .build() + ) + + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert len(parsed_operations) == 1 + parsed_operation = parsed_operations[0] + + assert isinstance(parsed_operation, messages.StellarPathPaymentOp) + assert parsed_operation.source_account == operation_source + assert parsed_operation.destination_account == destination + assert parsed_operation.send_asset.type == stellar.ASSET_TYPE_NATIVE + assert parsed_operation.send_max == 500111000 + assert parsed_operation.destination_asset.type == stellar.ASSET_TYPE_ALPHA4 + assert parsed_operation.destination_asset.code == dest_code + assert parsed_operation.destination_asset.issuer == dest_issuer + assert len(parsed_operation.paths) == 2 + assert parsed_operation.paths[0].type == stellar.ASSET_TYPE_ALPHA4 + assert parsed_operation.paths[0].code == path_asset1.code + assert parsed_operation.paths[0].issuer == path_asset1.issuer + assert parsed_operation.paths[1].type == stellar.ASSET_TYPE_ALPHA12 + assert parsed_operation.paths[1].code == path_asset2.code + assert parsed_operation.paths[1].issuer == path_asset2.issuer + + +def test_stellar_parse_operation_path_payment_strict_receive_empty_path_v0(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + destination = "GDNSSYSCSSJ76FER5WEEXME5G4MTCUBKDRQSKOYP36KUKVDB2VCMERS6" + send_max = "50.0111" + dest_amount = "100" + send_code = "XLM" + send_issuer = None + dest_code = "USD" + dest_issuer = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=100, + v1=False, + ) + .append_path_payment_strict_receive_op( + destination=destination, + send_code=send_code, + send_issuer=send_issuer, + send_max=send_max, + dest_code=dest_code, + dest_issuer=dest_issuer, + dest_amount=dest_amount, + path=[], + source=operation_source, + ) + .build() + ) + + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert len(parsed_operations) == 1 + parsed_operation = parsed_operations[0] + assert isinstance(parsed_operation, messages.StellarPathPaymentOp) + assert parsed_operation.source_account == operation_source + assert parsed_operation.destination_account == destination + assert parsed_operation.send_asset.type == stellar.ASSET_TYPE_NATIVE + assert parsed_operation.send_max == 500111000 + assert parsed_operation.destination_asset.type == stellar.ASSET_TYPE_ALPHA4 + assert parsed_operation.destination_asset.code == dest_code + assert parsed_operation.destination_asset.issuer == dest_issuer + assert len(parsed_operation.paths) == 0 + + +def test_stellar_parse_operation_manage_sell_offer_new_offer_v0(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + price = "0.5" + amount = "50.0111" + selling_code = "XLM" + selling_issuer = None + buying_code = "USD" + buying_issuer = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=100, + v1=False, + ) + .append_manage_sell_offer_op( + selling_code=selling_code, + selling_issuer=selling_issuer, + buying_code=buying_code, + buying_issuer=buying_issuer, + amount=amount, + price=price, + source=operation_source, + ) + .build() + ) + + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert len(parsed_operations) == 1 + parsed_operation = parsed_operations[0] + assert isinstance(parsed_operation, messages.StellarManageOfferOp) + assert parsed_operation.source_account == operation_source + assert parsed_operation.selling_asset.type == stellar.ASSET_TYPE_NATIVE + assert parsed_operation.buying_asset.type == stellar.ASSET_TYPE_ALPHA4 + assert parsed_operation.buying_asset.code == buying_code + assert parsed_operation.buying_asset.issuer == buying_issuer + assert parsed_operation.amount == 500111000 + assert parsed_operation.price_n == 1 + assert parsed_operation.price_d == 2 + assert parsed_operation.offer_id == 0 # indicates a new offer + + +def test_stellar_parse_operation_manage_sell_offer_update_offer_v0(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + price = "0.5" + amount = "50.0111" + selling_code = "XLM" + selling_issuer = None + buying_code = "USD" + buying_issuer = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + offer_id = 12345 + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=100, + v1=False, + ) + .append_manage_sell_offer_op( + selling_code=selling_code, + selling_issuer=selling_issuer, + buying_code=buying_code, + buying_issuer=buying_issuer, + amount=amount, + price=price, + offer_id=offer_id, + source=operation_source, + ) + .build() + ) + + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert len(parsed_operations) == 1 + parsed_operation = parsed_operations[0] + assert isinstance(parsed_operation, messages.StellarManageOfferOp) + assert parsed_operation.source_account == operation_source + assert parsed_operation.selling_asset.type == stellar.ASSET_TYPE_NATIVE + assert parsed_operation.buying_asset.type == stellar.ASSET_TYPE_ALPHA4 + assert parsed_operation.buying_asset.code == buying_code + assert parsed_operation.buying_asset.issuer == buying_issuer + assert parsed_operation.amount == 500111000 + assert parsed_operation.price_n == 1 + assert parsed_operation.price_d == 2 + assert parsed_operation.offer_id == offer_id + + +def test_stellar_parse_operation_create_passive_sell_offer_v0(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + price = "0.5" + amount = "50.0111" + selling_code = "XLM" + selling_issuer = None + buying_code = "USD" + buying_issuer = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=100, + v1=False, + ) + .append_create_passive_sell_offer_op( + selling_code=selling_code, + selling_issuer=selling_issuer, + buying_code=buying_code, + buying_issuer=buying_issuer, + amount=amount, + price=price, + source=operation_source, + ) + .build() + ) + + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert len(parsed_operations) == 1 + parsed_operation = parsed_operations[0] + assert isinstance(parsed_operation, messages.StellarCreatePassiveOfferOp) + assert parsed_operation.source_account == operation_source + assert parsed_operation.selling_asset.type == stellar.ASSET_TYPE_NATIVE + assert parsed_operation.buying_asset.type == stellar.ASSET_TYPE_ALPHA4 + assert parsed_operation.buying_asset.code == buying_code + assert parsed_operation.buying_asset.issuer == buying_issuer + assert parsed_operation.amount == 500111000 + assert parsed_operation.price_n == 1 + assert parsed_operation.price_d == 2 + + +def test_stellar_parse_operation_set_options_v0(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + inflation_dest = "GAXN7HZQTHIPW7N2HGPAXMR42LPJ5VLYXMCCOX4D3JC4CQZGID3UYUPF" + clear_flags = 1 + set_flags = 6 + master_weight = 255 + low_threshold = 10 + med_threshold = 20 + high_threshold = 30 + home_domain = "example.com" + + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=100, + v1=False, + ) + .append_set_options_op( + inflation_dest=inflation_dest, + clear_flags=clear_flags, + set_flags=set_flags, + master_weight=master_weight, + low_threshold=low_threshold, + med_threshold=med_threshold, + high_threshold=high_threshold, + home_domain=home_domain, + source=operation_source, + ) + .build() + ) + + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert len(parsed_operations) == 1 + parsed_operation = parsed_operations[0] + assert isinstance(parsed_operation, messages.StellarSetOptionsOp) + assert parsed_operation.source_account == operation_source + assert parsed_operation.inflation_destination_account == inflation_dest + assert parsed_operation.clear_flags == clear_flags + assert parsed_operation.set_flags == set_flags + assert parsed_operation.master_weight == master_weight + assert parsed_operation.low_threshold == low_threshold + assert parsed_operation.medium_threshold == med_threshold + assert parsed_operation.high_threshold == high_threshold + assert parsed_operation.home_domain == home_domain + assert parsed_operation.signer_type is None + assert parsed_operation.signer_key is None + assert parsed_operation.signer_weight is None + + +def test_stellar_parse_operation_set_options_ed25519_signer_v0(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + signer = "GAXN7HZQTHIPW7N2HGPAXMR42LPJ5VLYXMCCOX4D3JC4CQZGID3UYUPF" + weight = 10 + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=100, + v1=False, + ) + .append_ed25519_public_key_signer( + account_id=signer, weight=weight, source=operation_source + ) + .build() + ) + + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert len(parsed_operations) == 1 + parsed_operation = parsed_operations[0] + assert isinstance(parsed_operation, messages.StellarSetOptionsOp) + assert parsed_operation.source_account == operation_source + assert parsed_operation.inflation_destination_account is None + assert parsed_operation.clear_flags is None + assert parsed_operation.set_flags is None + assert parsed_operation.master_weight is None + assert parsed_operation.low_threshold is None + assert parsed_operation.medium_threshold is None + assert parsed_operation.high_threshold is None + assert parsed_operation.home_domain is None + assert parsed_operation.signer_type == 0 + assert parsed_operation.signer_key == StrKey.decode_ed25519_public_key(signer) + assert parsed_operation.signer_weight == weight + + +def test_stellar_parse_operation_set_options_pre_auth_tx_signer_v0(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + signer = bytes.fromhex( + "2db4b22ca018119c5027a80578813ffcf582cda4aa9e31cd92b43cfa4fc5a000" + ) + weight = 30 + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=100, + v1=False, + ) + .append_pre_auth_tx_signer( + pre_auth_tx_hash=signer, weight=weight, source=operation_source + ) + .build() + ) + + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert len(parsed_operations) == 1 + parsed_operation = parsed_operations[0] + assert isinstance(parsed_operation, messages.StellarSetOptionsOp) + assert parsed_operation.source_account == operation_source + assert parsed_operation.inflation_destination_account is None + assert parsed_operation.clear_flags is None + assert parsed_operation.set_flags is None + assert parsed_operation.master_weight is None + assert parsed_operation.low_threshold is None + assert parsed_operation.medium_threshold is None + assert parsed_operation.high_threshold is None + assert parsed_operation.home_domain is None + assert parsed_operation.signer_type == 1 + assert parsed_operation.signer_key == signer + assert parsed_operation.signer_weight == weight + + +def test_stellar_parse_operation_set_options_hashx_signer_v0(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + signer = bytes.fromhex( + "3389e9f0f1a65f19736cacf544c2e825313e8447f569233bb8db39aa607c8000" + ) + weight = 20 + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=100, + v1=False, + ) + .append_hashx_signer(sha256_hash=signer, weight=weight, source=operation_source) + .build() + ) + + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert len(parsed_operations) == 1 + parsed_operation = parsed_operations[0] + assert isinstance(parsed_operation, messages.StellarSetOptionsOp) + assert parsed_operation.source_account == operation_source + assert parsed_operation.inflation_destination_account is None + assert parsed_operation.clear_flags is None + assert parsed_operation.set_flags is None + assert parsed_operation.master_weight is None + assert parsed_operation.low_threshold is None + assert parsed_operation.medium_threshold is None + assert parsed_operation.high_threshold is None + assert parsed_operation.home_domain is None + assert parsed_operation.signer_type == 2 + assert parsed_operation.signer_key == signer + assert parsed_operation.signer_weight == weight + + +def test_stellar_parse_operation_change_trust_v0(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + asset_code = "USD" + asset_issuer = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + limit = "1000" + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=100, + v1=False, + ) + .append_change_trust_op( + asset_code=asset_code, + asset_issuer=asset_issuer, + limit=limit, + source=operation_source, + ) + .build() + ) + + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert len(parsed_operations) == 1 + parsed_operation = parsed_operations[0] + assert isinstance(parsed_operation, messages.StellarChangeTrustOp) + assert parsed_operation.source_account == operation_source + assert parsed_operation.asset.type == stellar.ASSET_TYPE_ALPHA4 + assert parsed_operation.asset.code == asset_code + assert parsed_operation.asset.issuer == asset_issuer + assert parsed_operation.limit == 10000000000 + + +def test_stellar_parse_operation_allow_trust_v0(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + asset_code = "USD" + trustor = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=100, + v1=False, + ) + .append_allow_trust_op( + trustor=trustor, + asset_code=asset_code, + authorize=TrustLineEntryFlag.AUTHORIZED_FLAG, + source=operation_source, + ) + .build() + ) + + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert len(parsed_operations) == 1 + parsed_operation = parsed_operations[0] + assert isinstance(parsed_operation, messages.StellarAllowTrustOp) + assert parsed_operation.source_account == operation_source + assert parsed_operation.asset_type == stellar.ASSET_TYPE_ALPHA4 + assert parsed_operation.asset_code == asset_code + assert parsed_operation.trusted_account == trustor + + +def test_stellar_parse_operation_account_merge_v0(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + destination = "GDNSSYSCSSJ76FER5WEEXME5G4MTCUBKDRQSKOYP36KUKVDB2VCMERS6" + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=100, + v1=False, + ) + .append_account_merge_op(destination=destination, source=operation_source) + .build() + ) + + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert len(parsed_operations) == 1 + parsed_operation = parsed_operations[0] + assert isinstance(parsed_operation, messages.StellarAccountMergeOp) + assert parsed_operation.source_account == operation_source + assert parsed_operation.destination_account == destination + + +def test_stellar_parse_operation_manage_data_v0(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + data_name = "Trezor" + data_value = b"Hello, Stellar" + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=100, + v1=False, + ) + .append_manage_data_op( + data_name=data_name, data_value=data_value, source=operation_source + ) + .build() + ) + + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert len(parsed_operations) == 1 + parsed_operation = parsed_operations[0] + assert isinstance(parsed_operation, messages.StellarManageDataOp) + assert parsed_operation.source_account == operation_source + assert parsed_operation.key == data_name + assert parsed_operation.value == data_value + + +def test_stellar_parse_operation_manage_data_remove_data_entity_v0(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + data_name = "Trezor" + data_value = None # remove data entity + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=100, + v1=False, + ) + .append_manage_data_op( + data_name=data_name, data_value=data_value, source=operation_source + ) + .build() + ) + + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert len(parsed_operations) == 1 + parsed_operation = parsed_operations[0] + assert isinstance(parsed_operation, messages.StellarManageDataOp) + assert parsed_operation.source_account == operation_source + assert parsed_operation.key == data_name + assert parsed_operation.value is None + + +def test_stellar_parse_operation_bump_sequence_v0(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + bump_to = 143487250972278900 + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=100, + v1=False, + ) + .append_bump_sequence_op(bump_to=bump_to, source=operation_source) + .build() + ) + + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert len(parsed_operations) == 1 + parsed_operation = parsed_operations[0] + assert isinstance(parsed_operation, messages.StellarBumpSequenceOp) + assert parsed_operation.source_account == operation_source + assert parsed_operation.bump_to == bump_to + + +def test_stellar_parse_operation_simple_v1(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + data_name = "Trezor" + data_value = b"Hello, Stellar" + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + base_fee = 200 + + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=base_fee, + ) + .append_manage_data_op( + data_name=data_name, data_value=data_value, source=operation_source + ) + .build() + ) + + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert parsed_tx.source_account == tx_source + assert parsed_tx.fee == envelope.transaction.fee + assert parsed_tx.sequence_number == sequence + 1 + assert parsed_tx.timebounds_start is None + assert parsed_tx.timebounds_end is None + assert parsed_tx.memo_type == stellar.MEMO_TYPE_NONE + assert parsed_tx.memo_text is None + assert parsed_tx.memo_id is None + assert parsed_tx.memo_hash is None + assert len(parsed_operations) == 1 + + +def test_stellar_parse_transaction_memo_text_v1(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + data_name = "Trezor" + data_value = b"Hello, Stellar" + memo_text = b"Have a nice day!" + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + base_fee = 200 + + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=base_fee, + ) + .add_text_memo(memo_text=memo_text) + .append_manage_data_op( + data_name=data_name, data_value=data_value, source=operation_source + ) + .build() + ) + + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert parsed_tx.source_account == tx_source + assert parsed_tx.fee == envelope.transaction.fee + assert parsed_tx.sequence_number == sequence + 1 + assert parsed_tx.timebounds_start is None + assert parsed_tx.timebounds_end is None + assert parsed_tx.memo_type == stellar.MEMO_TYPE_TEXT + assert parsed_tx.memo_text == memo_text.decode("utf-8") + assert parsed_tx.memo_id is None + assert parsed_tx.memo_hash is None + assert len(parsed_operations) == 1 + + +def test_stellar_parse_transaction_bytes_memo_id_v1(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + data_name = "Trezor" + data_value = b"Hello, Stellar" + memo_id = 123456789 + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + base_fee = 200 + + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=base_fee, + ) + .add_id_memo(memo_id) + .append_manage_data_op( + data_name=data_name, data_value=data_value, source=operation_source + ) + .build() + ) + + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert parsed_tx.source_account == tx_source + assert parsed_tx.fee == envelope.transaction.fee + assert parsed_tx.sequence_number == sequence + 1 + assert parsed_tx.timebounds_start is None + assert parsed_tx.timebounds_end is None + assert parsed_tx.memo_type == stellar.MEMO_TYPE_ID + assert parsed_tx.memo_text is None + assert parsed_tx.memo_id == memo_id + assert parsed_tx.memo_hash is None + assert len(parsed_operations) == 1 + + +def test_stellar_parse_transaction_memo_hash_v1(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + data_name = "Trezor" + data_value = b"Hello, Stellar" + memo_hash = "b77cd735095e1b58da2d7415c1f51f423a722b34d7d5002d8896608a9130a74b" + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + base_fee = 200 + + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=base_fee, + ) + .add_hash_memo(memo_hash) + .append_manage_data_op( + data_name=data_name, data_value=data_value, source=operation_source + ) + .build() + ) + + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert parsed_tx.source_account == tx_source + assert parsed_tx.fee == envelope.transaction.fee + assert parsed_tx.sequence_number == sequence + 1 + assert parsed_tx.timebounds_start is None + assert parsed_tx.timebounds_end is None + assert parsed_tx.memo_type == stellar.MEMO_TYPE_HASH + assert parsed_tx.memo_text is None + assert parsed_tx.memo_id is None + assert parsed_tx.memo_hash.hex() == memo_hash + assert len(parsed_operations) == 1 + + +def test_stellar_parse_transaction_memo_return_hash_v1(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + data_name = "Trezor" + data_value = b"Hello, Stellar" + memo_return = "b77cd735095e1b58da2d7415c1f51f423a722b34d7d5002d8896608a9130a74b" + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + base_fee = 200 + + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=base_fee, + ) + .add_return_hash_memo(memo_return) + .append_manage_data_op( + data_name=data_name, data_value=data_value, source=operation_source + ) + .build() + ) + + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert parsed_tx.source_account == tx_source + assert parsed_tx.fee == envelope.transaction.fee + assert parsed_tx.sequence_number == sequence + 1 + assert parsed_tx.timebounds_start is None + assert parsed_tx.timebounds_end is None + assert parsed_tx.memo_type == stellar.MEMO_TYPE_RETURN + assert parsed_tx.memo_text is None + assert parsed_tx.memo_id is None + assert parsed_tx.memo_hash.hex() == memo_return + assert len(parsed_operations) == 1 + + +def test_stellar_parse_transaction_time_bounds_v1(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + data_name = "Trezor" + data_value = b"Hello, Stellar" + min_time = 1628089098 + max_time = 1628090000 + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + base_fee = 200 + + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=base_fee, + v1=False, + ) + .add_time_bounds(min_time=min_time, max_time=max_time) + .append_manage_data_op( + data_name=data_name, data_value=data_value, source=operation_source + ) + .build() + ) + + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert parsed_tx.source_account == tx_source + assert parsed_tx.fee == envelope.transaction.fee + assert parsed_tx.sequence_number == sequence + 1 + assert parsed_tx.timebounds_start == min_time + assert parsed_tx.timebounds_end == max_time + assert parsed_tx.memo_type == stellar.MEMO_TYPE_NONE + assert parsed_tx.memo_text is None + assert parsed_tx.memo_id is None + assert parsed_tx.memo_hash is None + assert len(parsed_operations) == 1 + + +def test_stellar_parse_operation_multiple_operations_v1(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + base_fee = 200 + data_name = "Trezor" + data_value = b"Hello, Stellar" + operation1_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + destination = "GDNSSYSCSSJ76FER5WEEXME5G4MTCUBKDRQSKOYP36KUKVDB2VCMERS6" + amount = "50.0111" + asset_code = "XLM" + asset_issuer = None + operation2_source = "GBHWKBPP3O4H2BUUKSFXE4PK5WHLQYVZIZUNUJ4AU5VUZZEVBDMXISAS" + + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=base_fee, + ) + .append_manage_data_op( + data_name=data_name, data_value=data_value, source=operation1_source + ) + .append_payment_op( + destination=destination, + amount=amount, + asset_code=asset_code, + asset_issuer=asset_issuer, + source=operation2_source, + ) + .build() + ) + + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert parsed_tx.source_account == tx_source + assert parsed_tx.fee == envelope.transaction.fee + assert parsed_tx.sequence_number == sequence + 1 + assert parsed_tx.timebounds_start is None + assert parsed_tx.timebounds_end is None + assert parsed_tx.memo_type == stellar.MEMO_TYPE_NONE + assert parsed_tx.memo_text is None + assert parsed_tx.memo_id is None + assert parsed_tx.memo_hash is None + assert len(parsed_operations) == 2 + assert isinstance(parsed_operations[0], messages.StellarManageDataOp) + assert parsed_operations[0].source_account == operation1_source + assert parsed_operations[0].key == data_name + assert parsed_operations[0].value == data_value + assert isinstance(parsed_operations[1], messages.StellarPaymentOp) + assert parsed_operations[1].source_account == operation2_source + assert parsed_operations[1].destination_account == destination + assert parsed_operations[1].asset.type == stellar.ASSET_TYPE_NATIVE + assert parsed_operations[1].asset.code is None + assert parsed_operations[1].asset.issuer is None + assert parsed_operations[1].amount == 500111000 + + +def test_stellar_parse_operation_create_account_v1(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + destination = "GDNSSYSCSSJ76FER5WEEXME5G4MTCUBKDRQSKOYP36KUKVDB2VCMERS6" + starting_balance = "100.0333" + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=100, + ) + .append_create_account_op( + destination=destination, + starting_balance=starting_balance, + source=operation_source, + ) + .build() + ) + + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert len(parsed_operations) == 1 + parsed_operation = parsed_operations[0] + assert isinstance(parsed_operation, messages.StellarCreateAccountOp) + assert parsed_operation.source_account == operation_source + assert parsed_operation.new_account == destination + assert parsed_operation.starting_balance == 1000333000 + + +def test_stellar_parse_operation_payment_native_asset_v1(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + destination = "GDNSSYSCSSJ76FER5WEEXME5G4MTCUBKDRQSKOYP36KUKVDB2VCMERS6" + amount = "50.0111" + asset_code = "XLM" + asset_issuer = None + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=100, + ) + .append_payment_op( + destination=destination, + amount=amount, + asset_code=asset_code, + asset_issuer=asset_issuer, + source=operation_source, + ) + .build() + ) + + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert len(parsed_operations) == 1 + parsed_operation = parsed_operations[0] + assert isinstance(parsed_operation, messages.StellarPaymentOp) + assert parsed_operation.source_account == operation_source + assert parsed_operation.destination_account == destination + assert parsed_operation.asset.type == stellar.ASSET_TYPE_NATIVE + assert parsed_operation.asset.code is None + assert parsed_operation.asset.issuer is None + assert parsed_operation.amount == 500111000 + + +def test_stellar_parse_operation_payment_alpha4_asset_v1(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + destination = "GDNSSYSCSSJ76FER5WEEXME5G4MTCUBKDRQSKOYP36KUKVDB2VCMERS6" + amount = "50.0111" + asset_code = "USD" + asset_issuer = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=100, + ) + .append_payment_op( + destination=destination, + amount=amount, + asset_code=asset_code, + asset_issuer=asset_issuer, + source=operation_source, + ) + .build() + ) + + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert len(parsed_operations) == 1 + parsed_operation = parsed_operations[0] + assert isinstance(parsed_operation, messages.StellarPaymentOp) + assert parsed_operation.source_account == operation_source + assert parsed_operation.destination_account == destination + assert parsed_operation.asset.type == stellar.ASSET_TYPE_ALPHA4 + assert parsed_operation.asset.code == asset_code + assert parsed_operation.asset.issuer == asset_issuer + assert parsed_operation.amount == 500111000 + + +def test_stellar_parse_operation_payment_alpha12_asset_v1(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + destination = "GDNSSYSCSSJ76FER5WEEXME5G4MTCUBKDRQSKOYP36KUKVDB2VCMERS6" + amount = "50.0111" + asset_code = "BANANA" + asset_issuer = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=100, + ) + .append_payment_op( + destination=destination, + amount=amount, + asset_code=asset_code, + asset_issuer=asset_issuer, + source=operation_source, + ) + .build() + ) + + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert len(parsed_operations) == 1 + parsed_operation = parsed_operations[0] + assert isinstance(parsed_operation, messages.StellarPaymentOp) + assert parsed_operation.source_account == operation_source + assert parsed_operation.destination_account == destination + assert parsed_operation.asset.type == stellar.ASSET_TYPE_ALPHA12 + assert parsed_operation.asset.code == asset_code + assert parsed_operation.asset.issuer == asset_issuer + assert parsed_operation.amount == 500111000 + + +def test_stellar_parse_operation_path_payment_strict_receive_v1(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + destination = "GDNSSYSCSSJ76FER5WEEXME5G4MTCUBKDRQSKOYP36KUKVDB2VCMERS6" + send_max = "50.0111" + dest_amount = "100" + send_code = "XLM" + send_issuer = None + dest_code = "USD" + dest_issuer = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + path_asset1 = Asset( + "JPY", "GD6PV7DXQJX7AGVXFQ2MTCLTCH6LR3E6IO2EO2YDZD7F7IOZZCCB5DSQ" + ) + path_asset2 = Asset( + "BANANA", "GC7EKO37HNSKQ3V6RZ274EO7SFOWASQRHLX3OR5FIZK6UMV6LIEDXHGZ" + ) + + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=100, + ) + .append_path_payment_strict_receive_op( + destination=destination, + send_code=send_code, + send_issuer=send_issuer, + send_max=send_max, + dest_code=dest_code, + dest_issuer=dest_issuer, + dest_amount=dest_amount, + path=[path_asset1, path_asset2], + source=operation_source, + ) + .build() + ) + + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert len(parsed_operations) == 1 + parsed_operation = parsed_operations[0] + + assert isinstance(parsed_operation, messages.StellarPathPaymentOp) + assert parsed_operation.source_account == operation_source + assert parsed_operation.destination_account == destination + assert parsed_operation.send_asset.type == stellar.ASSET_TYPE_NATIVE + assert parsed_operation.send_max == 500111000 + assert parsed_operation.destination_asset.type == stellar.ASSET_TYPE_ALPHA4 + assert parsed_operation.destination_asset.code == dest_code + assert parsed_operation.destination_asset.issuer == dest_issuer + assert len(parsed_operation.paths) == 2 + assert parsed_operation.paths[0].type == stellar.ASSET_TYPE_ALPHA4 + assert parsed_operation.paths[0].code == path_asset1.code + assert parsed_operation.paths[0].issuer == path_asset1.issuer + assert parsed_operation.paths[1].type == stellar.ASSET_TYPE_ALPHA12 + assert parsed_operation.paths[1].code == path_asset2.code + assert parsed_operation.paths[1].issuer == path_asset2.issuer + + +def test_stellar_parse_operation_path_payment_strict_receive_empty_path_v1(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + destination = "GDNSSYSCSSJ76FER5WEEXME5G4MTCUBKDRQSKOYP36KUKVDB2VCMERS6" + send_max = "50.0111" + dest_amount = "100" + send_code = "XLM" + send_issuer = None + dest_code = "USD" + dest_issuer = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=100, + ) + .append_path_payment_strict_receive_op( + destination=destination, + send_code=send_code, + send_issuer=send_issuer, + send_max=send_max, + dest_code=dest_code, + dest_issuer=dest_issuer, + dest_amount=dest_amount, + path=[], + source=operation_source, + ) + .build() + ) + + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert len(parsed_operations) == 1 + parsed_operation = parsed_operations[0] + assert isinstance(parsed_operation, messages.StellarPathPaymentOp) + assert parsed_operation.source_account == operation_source + assert parsed_operation.destination_account == destination + assert parsed_operation.send_asset.type == stellar.ASSET_TYPE_NATIVE + assert parsed_operation.send_max == 500111000 + assert parsed_operation.destination_asset.type == stellar.ASSET_TYPE_ALPHA4 + assert parsed_operation.destination_asset.code == dest_code + assert parsed_operation.destination_asset.issuer == dest_issuer + assert len(parsed_operation.paths) == 0 + + +def test_stellar_parse_operation_manage_sell_offer_new_offer_v1(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + price = "0.5" + amount = "50.0111" + selling_code = "XLM" + selling_issuer = None + buying_code = "USD" + buying_issuer = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=100, + ) + .append_manage_sell_offer_op( + selling_code=selling_code, + selling_issuer=selling_issuer, + buying_code=buying_code, + buying_issuer=buying_issuer, + amount=amount, + price=price, + source=operation_source, + ) + .build() + ) + + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert len(parsed_operations) == 1 + parsed_operation = parsed_operations[0] + assert isinstance(parsed_operation, messages.StellarManageOfferOp) + assert parsed_operation.source_account == operation_source + assert parsed_operation.selling_asset.type == stellar.ASSET_TYPE_NATIVE + assert parsed_operation.buying_asset.type == stellar.ASSET_TYPE_ALPHA4 + assert parsed_operation.buying_asset.code == buying_code + assert parsed_operation.buying_asset.issuer == buying_issuer + assert parsed_operation.amount == 500111000 + assert parsed_operation.price_n == 1 + assert parsed_operation.price_d == 2 + assert parsed_operation.offer_id == 0 # indicates a new offer + + +def test_stellar_parse_operation_manage_sell_offer_update_offer_v1(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + price = "0.5" + amount = "50.0111" + selling_code = "XLM" + selling_issuer = None + buying_code = "USD" + buying_issuer = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + offer_id = 12345 + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=100, + ) + .append_manage_sell_offer_op( + selling_code=selling_code, + selling_issuer=selling_issuer, + buying_code=buying_code, + buying_issuer=buying_issuer, + amount=amount, + price=price, + offer_id=offer_id, + source=operation_source, + ) + .build() + ) + + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert len(parsed_operations) == 1 + parsed_operation = parsed_operations[0] + assert isinstance(parsed_operation, messages.StellarManageOfferOp) + assert parsed_operation.source_account == operation_source + assert parsed_operation.selling_asset.type == stellar.ASSET_TYPE_NATIVE + assert parsed_operation.buying_asset.type == stellar.ASSET_TYPE_ALPHA4 + assert parsed_operation.buying_asset.code == buying_code + assert parsed_operation.buying_asset.issuer == buying_issuer + assert parsed_operation.amount == 500111000 + assert parsed_operation.price_n == 1 + assert parsed_operation.price_d == 2 + assert parsed_operation.offer_id == offer_id + + +def test_stellar_parse_operation_create_passive_sell_offer_v1(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + price = "0.5" + amount = "50.0111" + selling_code = "XLM" + selling_issuer = None + buying_code = "USD" + buying_issuer = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=100, + ) + .append_create_passive_sell_offer_op( + selling_code=selling_code, + selling_issuer=selling_issuer, + buying_code=buying_code, + buying_issuer=buying_issuer, + amount=amount, + price=price, + source=operation_source, + ) + .build() + ) + + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert len(parsed_operations) == 1 + parsed_operation = parsed_operations[0] + assert isinstance(parsed_operation, messages.StellarCreatePassiveOfferOp) + assert parsed_operation.source_account == operation_source + assert parsed_operation.selling_asset.type == stellar.ASSET_TYPE_NATIVE + assert parsed_operation.buying_asset.type == stellar.ASSET_TYPE_ALPHA4 + assert parsed_operation.buying_asset.code == buying_code + assert parsed_operation.buying_asset.issuer == buying_issuer + assert parsed_operation.amount == 500111000 + assert parsed_operation.price_n == 1 + assert parsed_operation.price_d == 2 + + +def test_stellar_parse_operation_set_options_v1(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + inflation_dest = "GAXN7HZQTHIPW7N2HGPAXMR42LPJ5VLYXMCCOX4D3JC4CQZGID3UYUPF" + clear_flags = 1 + set_flags = 6 + master_weight = 255 + low_threshold = 10 + med_threshold = 20 + high_threshold = 30 + home_domain = "example.com" + + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=100, + ) + .append_set_options_op( + inflation_dest=inflation_dest, + clear_flags=clear_flags, + set_flags=set_flags, + master_weight=master_weight, + low_threshold=low_threshold, + med_threshold=med_threshold, + high_threshold=high_threshold, + home_domain=home_domain, + source=operation_source, + ) + .build() + ) + + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert len(parsed_operations) == 1 + parsed_operation = parsed_operations[0] + assert isinstance(parsed_operation, messages.StellarSetOptionsOp) + assert parsed_operation.source_account == operation_source + assert parsed_operation.inflation_destination_account == inflation_dest + assert parsed_operation.clear_flags == clear_flags + assert parsed_operation.set_flags == set_flags + assert parsed_operation.master_weight == master_weight + assert parsed_operation.low_threshold == low_threshold + assert parsed_operation.medium_threshold == med_threshold + assert parsed_operation.high_threshold == high_threshold + assert parsed_operation.home_domain == home_domain + assert parsed_operation.signer_type is None + assert parsed_operation.signer_key is None + assert parsed_operation.signer_weight is None + + +def test_stellar_parse_operation_set_options_ed25519_signer_v1(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + signer = "GAXN7HZQTHIPW7N2HGPAXMR42LPJ5VLYXMCCOX4D3JC4CQZGID3UYUPF" + weight = 10 + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=100, + ) + .append_ed25519_public_key_signer( + account_id=signer, weight=weight, source=operation_source + ) + .build() + ) + + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert len(parsed_operations) == 1 + parsed_operation = parsed_operations[0] + assert isinstance(parsed_operation, messages.StellarSetOptionsOp) + assert parsed_operation.source_account == operation_source + assert parsed_operation.inflation_destination_account is None + assert parsed_operation.clear_flags is None + assert parsed_operation.set_flags is None + assert parsed_operation.master_weight is None + assert parsed_operation.low_threshold is None + assert parsed_operation.medium_threshold is None + assert parsed_operation.high_threshold is None + assert parsed_operation.home_domain is None + assert parsed_operation.signer_type == 0 + assert parsed_operation.signer_key == StrKey.decode_ed25519_public_key(signer) + assert parsed_operation.signer_weight == weight + + +def test_stellar_parse_operation_set_options_pre_auth_tx_signer_v1(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + signer = bytes.fromhex( + "2db4b22ca018119c5027a80578813ffcf582cda4aa9e31cd92b43cfa4fc5a000" + ) + weight = 30 + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=100, + ) + .append_pre_auth_tx_signer( + pre_auth_tx_hash=signer, weight=weight, source=operation_source + ) + .build() + ) + + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert len(parsed_operations) == 1 + parsed_operation = parsed_operations[0] + assert isinstance(parsed_operation, messages.StellarSetOptionsOp) + assert parsed_operation.source_account == operation_source + assert parsed_operation.inflation_destination_account is None + assert parsed_operation.clear_flags is None + assert parsed_operation.set_flags is None + assert parsed_operation.master_weight is None + assert parsed_operation.low_threshold is None + assert parsed_operation.medium_threshold is None + assert parsed_operation.high_threshold is None + assert parsed_operation.home_domain is None + assert parsed_operation.signer_type == 1 + assert parsed_operation.signer_key == signer + assert parsed_operation.signer_weight == weight + + +def test_stellar_parse_operation_set_options_hashx_signer_v1(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + signer = bytes.fromhex( + "3389e9f0f1a65f19736cacf544c2e825313e8447f569233bb8db39aa607c8000" + ) + weight = 20 + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=100, + ) + .append_hashx_signer(sha256_hash=signer, weight=weight, source=operation_source) + .build() + ) + + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert len(parsed_operations) == 1 + parsed_operation = parsed_operations[0] + assert isinstance(parsed_operation, messages.StellarSetOptionsOp) + assert parsed_operation.source_account == operation_source + assert parsed_operation.inflation_destination_account is None + assert parsed_operation.clear_flags is None + assert parsed_operation.set_flags is None + assert parsed_operation.master_weight is None + assert parsed_operation.low_threshold is None + assert parsed_operation.medium_threshold is None + assert parsed_operation.high_threshold is None + assert parsed_operation.home_domain is None + assert parsed_operation.signer_type == 2 + assert parsed_operation.signer_key == signer + assert parsed_operation.signer_weight == weight + + +def test_stellar_parse_operation_change_trust_v1(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + asset_code = "USD" + asset_issuer = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + limit = "1000" + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=100, + ) + .append_change_trust_op( + asset_code=asset_code, + asset_issuer=asset_issuer, + limit=limit, + source=operation_source, + ) + .build() + ) + + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert len(parsed_operations) == 1 + parsed_operation = parsed_operations[0] + assert isinstance(parsed_operation, messages.StellarChangeTrustOp) + assert parsed_operation.source_account == operation_source + assert parsed_operation.asset.type == stellar.ASSET_TYPE_ALPHA4 + assert parsed_operation.asset.code == asset_code + assert parsed_operation.asset.issuer == asset_issuer + assert parsed_operation.limit == 10000000000 + + +def test_stellar_parse_operation_allow_trust_v1(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + asset_code = "USD" + trustor = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=100, + ) + .append_allow_trust_op( + trustor=trustor, + asset_code=asset_code, + authorize=TrustLineEntryFlag.AUTHORIZED_FLAG, + source=operation_source, + ) + .build() + ) + + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert len(parsed_operations) == 1 + parsed_operation = parsed_operations[0] + assert isinstance(parsed_operation, messages.StellarAllowTrustOp) + assert parsed_operation.source_account == operation_source + assert parsed_operation.asset_type == stellar.ASSET_TYPE_ALPHA4 + assert parsed_operation.asset_code == asset_code + assert parsed_operation.trusted_account == trustor + + +def test_stellar_parse_operation_account_merge_v1(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + destination = "GDNSSYSCSSJ76FER5WEEXME5G4MTCUBKDRQSKOYP36KUKVDB2VCMERS6" + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=100, + ) + .append_account_merge_op(destination=destination, source=operation_source) + .build() + ) + + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert len(parsed_operations) == 1 + parsed_operation = parsed_operations[0] + assert isinstance(parsed_operation, messages.StellarAccountMergeOp) + assert parsed_operation.source_account == operation_source + assert parsed_operation.destination_account == destination + + +def test_stellar_parse_operation_manage_data_v1(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + data_name = "Trezor" + data_value = b"Hello, Stellar" + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=100, + ) + .append_manage_data_op( + data_name=data_name, data_value=data_value, source=operation_source + ) + .build() + ) + + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert len(parsed_operations) == 1 + parsed_operation = parsed_operations[0] + assert isinstance(parsed_operation, messages.StellarManageDataOp) + assert parsed_operation.source_account == operation_source + assert parsed_operation.key == data_name + assert parsed_operation.value == data_value + + +def test_stellar_parse_operation_manage_data_remove_data_entity_v1(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + data_name = "Trezor" + data_value = None # remove data entity + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=100, + ) + .append_manage_data_op( + data_name=data_name, data_value=data_value, source=operation_source + ) + .build() + ) + + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert len(parsed_operations) == 1 + parsed_operation = parsed_operations[0] + assert isinstance(parsed_operation, messages.StellarManageDataOp) + assert parsed_operation.source_account == operation_source + assert parsed_operation.key == data_name + assert parsed_operation.value is None + + +def test_stellar_parse_operation_bump_sequence_v1(): + network_passphrase = Network.TESTNET_NETWORK_PASSPHRASE + tx_source = "GCSJ7MFIIGIRMAS4R3VT5FIFIAOXNMGDI5HPYTWS5X7HH74FSJ6STSGF" + sequence = 123456 + bump_to = 143487250972278900 + operation_source = "GAEB4MRKRCONK4J7MVQXAHTNDPAECUCCCNE7YC5CKM34U3OJ673A4D6V" + + source_account = Account(account_id=tx_source, sequence=sequence) + envelope = ( + TransactionBuilder( + source_account=source_account, + network_passphrase=network_passphrase, + base_fee=100, + ) + .append_bump_sequence_op(bump_to=bump_to, source=operation_source) + .build() + ) + + parsed_tx, parsed_operations = stellar.from_envelope(envelope) + assert len(parsed_operations) == 1 + parsed_operation = parsed_operations[0] + assert isinstance(parsed_operation, messages.StellarBumpSequenceOp) + assert parsed_operation.source_account == operation_source + assert parsed_operation.bump_to == bump_to diff --git a/python/tox.ini b/python/tox.ini index ce42d3fd1..5e70cc03e 100644 --- a/python/tox.ini +++ b/python/tox.ini @@ -8,6 +8,7 @@ envlist = [testenv] deps = -rrequirements.txt + -rrequirements-optional.txt pytest>=3.6 pytest-random-order importlib-metadata!=0.21