From ec60c6307f113bbfb64891605d861c9d49c2d16f Mon Sep 17 00:00:00 2001 From: Al Date: Thu, 12 Oct 2023 10:33:53 +0200 Subject: [PATCH] feat: task send transaction(s) (#326) * feat: send transaction task * test: snapshot tests * docs: regen docs * docs: adding command docs * fix: fixing decoding of dictified txns in sign method to support AppCall txns * chore: addressing pr comments --- docs/cli/index.md | 49 +++++- docs/features/tasks.md | 2 +- docs/features/tasks/send.md | 56 ++++++ docs/features/tasks/sign.md | 2 +- src/algokit/cli/task.py | 2 + src/algokit/cli/tasks/send_transaction.py | 166 ++++++++++++++++++ src/algokit/cli/tasks/sign_transaction.py | 40 ++--- src/algokit/cli/tasks/utils.py | 39 ++++ tests/tasks/conftest.py | 17 ++ tests/tasks/test_send_transaction.py | 162 +++++++++++++++++ ...ansaction.test_decoding_error.approved.txt | 2 + ...st_file_decoding_no_txn_error.approved.txt | 1 + ...st_mutually_exclusive_options.approved.txt | 1 + ...d_atomic_txn_group_successful.approved.txt | 2 + ...est_send_from_file_successful.approved.txt | 80 +++++++++ ...d_from_piped_input_successful.approved.txt | 80 +++++++++ ...d_from_transaction_successful.approved.txt | 4 + tests/tasks/test_sign_transaction.py | 17 +- ...n_atomic_txn_group_successful.approved.txt | 50 +++--- ...stdin_with_address_successful.approved.txt | 24 +-- ...m_stdin_with_alias_successful.approved.txt | 24 +-- ..._file_with_address_successful.approved.txt | 72 +++----- ...om_file_with_alias_successful.approved.txt | 72 +++----- ...t_transaction_decoding_errors.approved.txt | 2 +- 24 files changed, 768 insertions(+), 198 deletions(-) create mode 100644 docs/features/tasks/send.md create mode 100644 src/algokit/cli/tasks/send_transaction.py create mode 100644 tests/tasks/conftest.py create mode 100644 tests/tasks/test_send_transaction.py create mode 100644 tests/tasks/test_send_transaction.test_decoding_error.approved.txt create mode 100644 tests/tasks/test_send_transaction.test_file_decoding_no_txn_error.approved.txt create mode 100644 tests/tasks/test_send_transaction.test_mutually_exclusive_options.approved.txt create mode 100644 tests/tasks/test_send_transaction.test_send_atomic_txn_group_successful.approved.txt create mode 100644 tests/tasks/test_send_transaction.test_send_from_file_successful.approved.txt create mode 100644 tests/tasks/test_send_transaction.test_send_from_piped_input_successful.approved.txt create mode 100644 tests/tasks/test_send_transaction.test_send_from_transaction_successful.approved.txt diff --git a/docs/cli/index.md b/docs/cli/index.md index e68bbc8f..d8a5841e 100644 --- a/docs/cli/index.md +++ b/docs/cli/index.md @@ -100,23 +100,28 @@ - [status](#status) - [stop](#stop) - [task](#task) - - [sign](#sign) + - [send](#send) - [Options](#options-17) - - [-a, --account ](#-a---account-) - [-f, --file ](#-f---file--1) - [-t, --transaction ](#-t---transaction-) + - [-n, --network ](#-n---network-) + - [sign](#sign) + - [Options](#options-18) + - [-a, --account ](#-a---account-) + - [-f, --file ](#-f---file--2) + - [-t, --transaction ](#-t---transaction--1) - [-o, --output ](#-o---output--2) - [--force](#--force-1) - [transfer](#transfer) - - [Options](#options-18) + - [Options](#options-19) - [-s, --sender ](#-s---sender-) - [-r, --receiver ](#-r---receiver--1) - [--asset, --id ](#--asset---id-) - [-a, --amount ](#-a---amount--1) - [--whole-units](#--whole-units-2) - - [-n, --network ](#-n---network-) + - [-n, --network ](#-n---network--1) - [vanity-address](#vanity-address) - - [Options](#options-19) + - [Options](#options-20) - [-m, --match ](#-m---match-) - [-o, --output ](#-o---output--3) - [-a, --alias ](#-a---alias-) @@ -125,7 +130,7 @@ - [Arguments](#arguments-5) - [KEYWORD](#keyword) - [wallet](#wallet) - - [Options](#options-20) + - [Options](#options-21) - [-a, --address ](#-a---address-) - [-m, --mnemonic](#-m---mnemonic) - [-f, --force](#-f---force-1) @@ -133,11 +138,11 @@ - [ALIAS_NAME](#alias_name) - [Arguments](#arguments-7) - [ALIAS](#alias) - - [Options](#options-21) + - [Options](#options-22) - [-f, --force](#-f---force-2) - [Arguments](#arguments-8) - [ALIAS](#alias-1) - - [Options](#options-22) + - [Options](#options-23) - [-f, --force](#-f---force-3) # algokit @@ -688,6 +693,34 @@ Collection of useful tasks to help you develop on Algorand. algokit task [OPTIONS] COMMAND [ARGS]... ``` +### send + +Send a signed transaction to the given network. + +```shell +algokit task send [OPTIONS] +``` + +### Options + + +### -f, --file +Single or multiple message pack encoded signed transactions from binary file to send. Option is mutually exclusive with transaction. + + +### -t, --transaction +Base64 encoded signed transaction to send. Option is mutually exclusive with file. + + +### -n, --network +Network to use. Refers to localnet by default. + + +* **Options** + + localnet | testnet | mainnet + + ### sign Sign goal clerk compatible Algorand transaction(s). diff --git a/docs/features/tasks.md b/docs/features/tasks.md index 4c26420c..cae6d418 100644 --- a/docs/features/tasks.md +++ b/docs/features/tasks.md @@ -9,7 +9,7 @@ AlgoKit Tasks are a collection of handy tasks that can be used to perform variou - [Transfer Assets or Algos](./tasks/transfer.md) - Transfer Algos or Assets from one account to another with the AlgoKit Transfer feature. This feature allows you to transfer Algos or Assets from one account to another on Algorand blockchain. - Opt-in or opt-out of Algorand Assets - Coming soon! - [Signing transactions](./tasks/sign.md) - Sign goal clerk compatible Algorand transactions. -- Sending transactions - Coming soon! +- [Sending transactions](./tasks/send.md) - Send signed goal clerk compatible Algorand transactions. - NFD lookups - Coming soon! - IPFS uploads - Coming soon! - ARC19 asset minting - Coming soon! diff --git a/docs/features/tasks/send.md b/docs/features/tasks/send.md new file mode 100644 index 00000000..4ec34d4f --- /dev/null +++ b/docs/features/tasks/send.md @@ -0,0 +1,56 @@ +# AlgoKit Task Send + +The AlgoKit Send feature allows you to send signed Algorand transaction(s) to a specified network using the AlgoKit CLI. This feature supports sending single or multiple transactions, either provided directly as a base64 encoded string or from a binary file. + +## Usage + +Available commands and possible usage as follows: + +```bash +$ ~ algokit task send +Usage: algokit task send [OPTIONS] + + Send a signed transaction to the given network. + +Options: + -f, --file FILE Single or multiple message pack encoded signed transactions from binary file to + send. Option is mutually exclusive with transaction. + -t, --transaction TEXT Base64 encoded signed transaction to send. Option is mutually exclusive with file. + -n, --network [localnet|testnet|mainnet] + Network to use. Refers to `localnet` by default. + -h, --help Show this message and exit. +``` + +## Options + +- `--file, -f PATH`: Specifies the path to a binary file containing single or multiple message pack encoded signed transactions to send. Mutually exclusive with `--transaction` option. +- `--transaction, -t TEXT`: Specifies a single base64 encoded signed transaction to send. Mutually exclusive with `--file` option. +- `--network, -n [localnet|testnet|mainnet]`: Specifies the network to which the transactions will be sent. Refers to `localnet` by default. + +> Please note, `--transaction` flag only supports sending a single transaction. If you want to send multiple transactions, you can use the `--file` flag to specify a binary file containing multiple transactions. + +## Example + +To send a transaction, you can use the `send` command as follows: + +```bash +$ algokit task send --file {PATH_TO_BINARY_FILE_CONTAINING_SIGNED_TRANSACTIONS} +``` + +This will send the transactions to the default `localnet` network. If you want to send the transactions to a different network, you can use the `--network` flag: + +```bash +$ algokit task send --transaction {YOUR_BASE64_ENCODED_SIGNED_TRANSACTION} --network testnet +``` + +You can also pipe the `stdout` of `algokit sign` command: + +```bash +$ algokit task sign --account {YOUR_ACCOUNT_ALIAS OR YOUR_ADDRESS} --file {PATH_TO_BINARY_FILE_CONTAINING_TRANSACTIONS} --force | algokit task send --network {network_name} +``` + +If the transaction is successfully sent, the transaction ID (txid) will be output to the console. You can check the transaction status at the provided transaction explorer URL. + +## Goal Compatibility + +Please note, at the moment this feature only supports [`goal clerk`](https://developer.algorand.org/docs/clis/goal/clerk/clerk/) compatible transaction objects. diff --git a/docs/features/tasks/sign.md b/docs/features/tasks/sign.md index 57a56a15..b36144fe 100644 --- a/docs/features/tasks/sign.md +++ b/docs/features/tasks/sign.md @@ -61,7 +61,7 @@ When `--output` option is not specified, the signed transaction(s) will be outpu ``` [ - {txn_id: "TRANSACTION_ID", content: "BASE64_ENCODED_SIGNED_TRANSACTION"}, + {transaction_id: "TRANSACTION_ID", content: "BASE64_ENCODED_SIGNED_TRANSACTION"}, ] ``` diff --git a/src/algokit/cli/task.py b/src/algokit/cli/task.py index bd1ab8b2..2d57fde5 100644 --- a/src/algokit/cli/task.py +++ b/src/algokit/cli/task.py @@ -2,6 +2,7 @@ import click +from algokit.cli.tasks.send_transaction import send from algokit.cli.tasks.sign_transaction import sign from algokit.cli.tasks.transfer import transfer from algokit.cli.tasks.vanity_address import vanity_address @@ -19,3 +20,4 @@ def task_group() -> None: task_group.add_command(vanity_address) task_group.add_command(transfer) task_group.add_command(sign) +task_group.add_command(send) diff --git a/src/algokit/cli/tasks/send_transaction.py b/src/algokit/cli/tasks/send_transaction.py new file mode 100644 index 00000000..11e37b34 --- /dev/null +++ b/src/algokit/cli/tasks/send_transaction.py @@ -0,0 +1,166 @@ +import json +import logging +from io import TextIOWrapper +from pathlib import Path +from typing import cast + +import click +from algosdk import encoding, error +from algosdk.transaction import SignedTransaction, retrieve_from_file + +from algokit.cli.common.utils import MutuallyExclusiveOption +from algokit.cli.tasks.utils import get_transaction_explorer_url, load_algod_client, stdin_has_content + +logger = logging.getLogger(__name__) + + +def _is_sign_task_output_txn(item: dict) -> bool: + """ + Checks if a given item is a dictionary and contains the keys "transaction_id" and "content". + + Args: + item (dict): A dictionary object to be checked. + + Returns: + bool: True if the input item is a dictionary with the keys "transaction_id" and "content", False otherwise. + """ + + return isinstance(item, dict) and all(key in item for key in ["transaction_id", "content"]) + + +def _load_from_stdin() -> list[SignedTransaction]: + """ + Load transaction data from standard input and convert it into a list of SignedTransaction objects. + + Returns: + A list of SignedTransaction objects representing the loaded transactions from the standard input. + + Raises: + click.ClickException: If the piped transaction content is invalid. + """ + # Read the raw file content from the standard input + + raw_file_content = cast(TextIOWrapper, click.get_text_stream("stdin")).read() + + try: + # Parse the raw file content as JSON + file_content = json.loads(raw_file_content) + except json.JSONDecodeError as ex: + raise click.ClickException("Invalid piped transaction content!") from ex + + # Check if the content is a list of dicts with the required fields + if not isinstance(file_content, list) or not all(_is_sign_task_output_txn(item) for item in file_content): + raise click.ClickException("Invalid piped transaction content!") + + # Convert the content into SignedTransaction objects + return [encoding.msgpack_decode(item["content"]) for item in file_content] # type: ignore[no-untyped-call] + + +def _get_signed_transactions(file: Path | None = None, transaction: str | None = None) -> list[SignedTransaction]: + """ + Retrieves a list of signed transactions. + + Args: + file (Optional[Path]): A `Path` object representing the file path from which to retrieve the transactions. + transaction (Optional[str]): A base64 encoded string representing a single signed transaction. + + Returns: + list[SignedTransaction]: A list of `SignedTransaction` objects representing the retrieved signed transactions. + + Raises: + click.ClickException: If the supplied transaction is not of type `SignedTransaction`. + click.ClickException: If there is an error decoding the transaction. + + """ + try: + if file: + txns = retrieve_from_file(str(file)) # type: ignore[no-untyped-call] + elif transaction: + txns = [encoding.msgpack_decode(transaction)] # type: ignore[no-untyped-call] + else: + txns = _load_from_stdin() + + for txn in txns: + if not isinstance(txn, SignedTransaction): + raise click.ClickException("Supplied transaction is not signed!") + + return cast(list[SignedTransaction], txns) + + except Exception as ex: + logger.debug(ex, exc_info=True) + raise click.ClickException( + "Failed to decode transaction! If you are intending to send multiple transactions use `--file` instead." + ) from ex + + +def _send_transactions(network: str, txns: list[SignedTransaction]) -> None: + """ + Sends a list of signed transactions to the Algorand blockchain network using the AlgodClient. + + Args: + network (str): The network to which the transactions will be sent. + txns (list[SignedTransaction]): A list of signed transactions to be sent. + + Returns: + None: The function does not return any value. + """ + algod_client = load_algod_client(network) + + if any(txn.transaction.group for txn in txns): + txid = algod_client.send_transactions(txns) + click.echo(f"Transaction group successfully sent with txid: {txid}") + click.echo(f"Check transaction group status at: {get_transaction_explorer_url(txid, network)}") + else: + for index, txn in enumerate(txns, start=1): + click.echo(f"\nSending transaction {index}/{len(txns)}") + txid = algod_client.send_transaction(txn) + click.echo(f"Transaction successfully sent with txid: {txid}") + click.echo(f"Check transaction status at: {get_transaction_explorer_url(txid, network)}") + + +@click.command(name="send", help="Send a signed transaction to the given network.") +@click.option( + "--file", + "-f", + type=click.Path(exists=True, dir_okay=False, file_okay=True, resolve_path=True, path_type=Path), + help="Single or multiple message pack encoded signed transactions from binary file to send.", + cls=MutuallyExclusiveOption, + not_required_if=["transaction"], + required=False, +) +@click.option( + "--transaction", + "-t", + type=click.STRING, + help="Base64 encoded signed transaction to send.", + cls=MutuallyExclusiveOption, + not_required_if=["file"], + required=False, +) +@click.option( + "-n", + "--network", + type=click.Choice(["localnet", "testnet", "mainnet"]), + default="localnet", + required=False, + help="Network to use. Refers to `localnet` by default.", +) +def send(*, file: Path | None, transaction: str | None, network: str) -> None: + if not file and not transaction and not stdin_has_content(): + raise click.ClickException( + "Please provide a file path via `--file` or a base64 encoded signed transaction via `--transaction`. " + "Alternatively, you can also pipe the output of `algokit task sign` to this command." + ) + + txns = _get_signed_transactions(file, transaction) + + if not txns: + raise click.ClickException("No valid transactions found!") + + try: + _send_transactions(network, txns) + except error.AlgodHTTPError as ex: + raise click.ClickException(str(ex)) from ex + except Exception as ex: + logger.debug(ex, exc_info=True) + raise click.ClickException("Failed to send transaction!") from ex diff --git a/src/algokit/cli/tasks/sign_transaction.py b/src/algokit/cli/tasks/sign_transaction.py index 6120c797..e770207c 100644 --- a/src/algokit/cli/tasks/sign_transaction.py +++ b/src/algokit/cli/tasks/sign_transaction.py @@ -2,7 +2,7 @@ import json import logging from pathlib import Path -from typing import cast +from typing import Any, cast import click from algosdk import encoding @@ -14,26 +14,23 @@ logger = logging.getLogger(__name__) -def _format_transaction_for_stdout(txn: Transaction) -> dict[str, int | str | bytes | None]: - raw_txn = txn.__dict__.copy() - - # Group isn't decoded via dictify by default, hence the need to decode it manually for stdout confirmation message. - if raw_txn.get("group"): - raw_txn["group"] = base64.b64encode(raw_txn["group"]).decode() - - return raw_txn +class TransactionBytesEncoder(json.JSONEncoder): + def default(self, obj: Any) -> Any: # noqa: ANN401 + if isinstance(obj, bytes | bytearray): + return base64.b64encode(obj).decode() + return super().default(obj) def _validate_for_signed_txns(txns: list[Transaction]) -> None: signed_txns = [txn for txn in txns if isinstance(txn, SignedTransaction)] if signed_txns: - txn_ids = ", ".join([txn.get_txid() for txn in signed_txns]) # type: ignore[no-untyped-call] - message = f"Supplied transactions {txn_ids} are already signed!" + transaction_ids = ", ".join([txn.get_txid() for txn in signed_txns]) # type: ignore[no-untyped-call] + message = f"Supplied transactions {transaction_ids} are already signed!" raise click.ClickException(message) -def get_transactions(file: Path | None, transaction: str | None) -> list[Transaction]: +def _get_transactions(file: Path | None, transaction: str | None) -> list[Transaction]: try: if file: txns: list[Transaction] = retrieve_from_file(str(file)) # type: ignore[no-untyped-call] @@ -43,20 +40,21 @@ def get_transactions(file: Path | None, transaction: str | None) -> list[Transac except Exception as ex: logger.debug(ex, exc_info=True) raise click.ClickException( - "Failed to decode transaction! If you are intending to sign multiple transactions use --file instead." + "Failed to decode transaction! If you are intending to sign multiple transactions use `--file` instead." ) from ex -def confirm_transaction(txns: list[Transaction]) -> bool: +def _confirm_transaction(txns: list[Transaction]) -> bool: click.echo( json.dumps( [ { - "txn_id": txn.get_txid(), # type: ignore[no-untyped-call] - "content": _format_transaction_for_stdout(txn), + "transaction_id": txn.get_txid(), # type: ignore[no-untyped-call] + "content": txn.dictify(), # type: ignore[no-untyped-call] } for txn in txns ], + cls=TransactionBytesEncoder, indent=2, ), ) @@ -66,7 +64,7 @@ def confirm_transaction(txns: list[Transaction]) -> bool: return bool(response == "y") -def sign_and_output_transaction(txns: list[Transaction], private_key: str, output: Path | None) -> None: +def _sign_and_output_transaction(txns: list[Transaction], private_key: str, output: Path | None) -> None: signed_txns = [txn.sign(private_key) for txn in txns] # type: ignore[no-untyped-call] if output: @@ -74,7 +72,7 @@ def sign_and_output_transaction(txns: list[Transaction], private_key: str, outpu click.echo(f"Signed transaction written to {output}") else: encoded_signed_txns = [ - {"txn_id": txn.get_txid(), "content": encoding.msgpack_encode(txn)} # type: ignore[no-untyped-call] + {"transaction_id": txn.get_txid(), "content": encoding.msgpack_encode(txn)} # type: ignore[no-untyped-call] for txn in signed_txns ] click.echo(json.dumps(encoded_signed_txns, indent=2)) @@ -114,14 +112,14 @@ def sign(*, account: str, file: Path | None, transaction: str | None, output: Pa signer_account = get_account_with_private_key(account) - txns = get_transactions(file, transaction) + txns = _get_transactions(file, transaction) if not txns: raise click.ClickException("No valid transactions found!") _validate_for_signed_txns(txns) - if not force and not confirm_transaction(txns): + if not force and not _confirm_transaction(txns): return - sign_and_output_transaction(txns, signer_account.private_key, output) + _sign_and_output_transaction(txns, signer_account.private_key, output) diff --git a/src/algokit/cli/tasks/utils.py b/src/algokit/cli/tasks/utils.py index 8b631a98..2c79f185 100644 --- a/src/algokit/cli/tasks/utils.py +++ b/src/algokit/cli/tasks/utils.py @@ -1,4 +1,7 @@ import logging +import os +import stat +import sys import algosdk import algosdk.encoding @@ -236,3 +239,39 @@ def get_address(address: str) -> str: raise click.ClickException(f"Alias `{parsed_address}` alias does not exist.") from ex return alias_data.address + + +def get_transaction_explorer_url(transaction_id: str, network: str) -> str: + """ + Returns a URL for exploring a transaction on the specified network. + + Args: + transaction_id (str): The ID of the transaction. + network (str): The name of the network (e.g., "localnet", "testnet", "mainnet"). + + Returns: + str: The URL for exploring the transaction on the specified network. + + Raises: + ValueError: If the network is invalid. + """ + if network == "localnet": + return f"https://app.dappflow.org/setnetwork?name=sandbox&redirect=explorer/transaction/{transaction_id}/" + elif network == "testnet": + return f"https://testnet.algoexplorer.io/tx/{transaction_id}" + elif network == "mainnet": + return f"https://algoexplorer.io/tx/{transaction_id}" + else: + raise ValueError(f"Invalid network: {network}") + + +def stdin_has_content() -> bool: + """ + Checks if there is content in the standard input. + + Returns: + bool: True if there is content in the standard input, False otherwise. + """ + + mode = os.fstat(sys.stdin.fileno()).st_mode + return stat.S_ISFIFO(mode) or stat.S_ISREG(mode) diff --git a/tests/tasks/conftest.py b/tests/tasks/conftest.py new file mode 100644 index 00000000..876505a4 --- /dev/null +++ b/tests/tasks/conftest.py @@ -0,0 +1,17 @@ +from algokit_utils import Account +from algosdk import transaction + +DUMMY_SUGGESTED_PARAMS = transaction.SuggestedParams( # type: ignore[no-untyped-call] + fee=0, + first=33652328, + last=33653328, + gen="testnet-v1.0", + gh="SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=", + min_fee=1000, + flat_fee=True, + consensus_version="https://github.com/algorandfoundation/specs/tree/abd3d4823c6f77349fc04c3af7b1e99fe4df699f", +) +DUMMY_ACCOUNT = Account( + private_key="iLsfFiRDwi0ijFdvdyO1PGkYxooOanbJSgpJ4pPKjKZluk70pvuPX4dYD1Jir85uZP+AImM/8SBmdPRpBSTFAg==", + address="MW5E55FG7OHV7B2YB5JGFL6ONZSP7ABCMM77CIDGOT2GSBJEYUBOF3UYKA", +) diff --git a/tests/tasks/test_send_transaction.py b/tests/tasks/test_send_transaction.py new file mode 100644 index 00000000..6dc5bf92 --- /dev/null +++ b/tests/tasks/test_send_transaction.py @@ -0,0 +1,162 @@ +import json + +import click +import pytest +from algosdk import encoding, transaction +from pytest_mock import MockerFixture + +from tests.tasks.conftest import DUMMY_ACCOUNT, DUMMY_SUGGESTED_PARAMS +from tests.utils.approvals import verify +from tests.utils.click_invoker import invoke + + +def _generate_dummy_signed_txn(*, amount: int = 1, encode: bool = False) -> transaction.SignedTransaction | str: + unsigned_txn = transaction.PaymentTxn( # type: ignore[no-untyped-call] + DUMMY_ACCOUNT.address, DUMMY_SUGGESTED_PARAMS, DUMMY_ACCOUNT.address, amt=amount + ) + txn = unsigned_txn.sign(DUMMY_ACCOUNT.private_key) # type: ignore[no-untyped-call] + + if encode: + return str(encoding.msgpack_encode(txn)) # type: ignore[no-untyped-call] + + return txn # type: ignore[no-any-return] + + +def _generate_dummy_signed_txn_group() -> list[transaction.SignedTransaction]: + txns = [ + transaction.PaymentTxn( + DUMMY_ACCOUNT.address, DUMMY_SUGGESTED_PARAMS, DUMMY_ACCOUNT.address, amt=i + ) # type: ignore[no-untyped-call] + for i in range(3) + ] + txns[0].fee = 3000 + + gid = transaction.calculate_group_id(txns) # type: ignore[no-untyped-call] + signed_txns = [] + for txn in txns: + txn.group = gid + signed_txns.append(txn.sign(DUMMY_ACCOUNT.private_key)) # type: ignore[no-untyped-call] + + return signed_txns + + +def test_send_atomic_txn_group_successful(tmp_path_factory: pytest.TempPathFactory, mocker: MockerFixture) -> None: + # Arrange + cwd = tmp_path_factory.mktemp("cwd") + txns = _generate_dummy_signed_txn_group() + transaction.write_to_file(txns, str(cwd / "dummy.txns")) # type: ignore[no-untyped-call] + + algod_mock = mocker.MagicMock() + algod_mock.send_transactions.return_value = "dummy_tx_id" + mocker.patch("algokit.cli.tasks.send_transaction.load_algod_client", return_value=algod_mock) + + # Act + result = invoke("task send --file dummy.txns", input="y", cwd=cwd) + + # Assert + assert result.exit_code == 0 + verify(result.output) + + +def test_send_from_transaction_successful(mocker: MockerFixture) -> None: + # Arrange + algod_mock = mocker.MagicMock() + algod_mock.send_transaction.return_value = "dummy_tx_id" + mocker.patch("algokit.cli.tasks.send_transaction.load_algod_client", return_value=algod_mock) + + # Act + result = invoke(f"task send --transaction {_generate_dummy_signed_txn( encode=True)}") + + # Assert + assert result.exit_code == 0 + verify(result.output) + + +def test_send_from_file_successful( + mocker: MockerFixture, + tmp_path_factory: pytest.TempPathFactory, +) -> None: + # Arrange + cwd = tmp_path_factory.mktemp("cwd") + + transaction.write_to_file( # type: ignore[no-untyped-call] + [_generate_dummy_signed_txn(amount=i) for i in range(20)], + str(cwd / "dummy.txns"), + ) + + algod_mock = mocker.MagicMock() + algod_mock.send_transaction.side_effect = [f"dummy_tx_id_{i}" for i in range(20)] + mocker.patch("algokit.cli.tasks.send_transaction.load_algod_client", return_value=algod_mock) + + # Act + result = invoke("task send --file dummy.txns", cwd=cwd) + + # Assert + assert result.exit_code == 0 + verify(result.output) + + +def test_send_from_piped_input_successful( + mocker: MockerFixture, + tmp_path_factory: pytest.TempPathFactory, +) -> None: + # Arrange + tmp_path_factory.mktemp("cwd") + + ## Below simulates stdout from algokit sign transaction + txns = [{"content": _generate_dummy_signed_txn(amount=i, encode=True), "transaction_id": str(i)} for i in range(20)] + + algod_mock = mocker.MagicMock() + algod_mock.send_transaction.side_effect = [f"dummy_tx_id_{i}" for i in range(20)] + mocker.patch("algokit.cli.tasks.send_transaction.load_algod_client", return_value=algod_mock) + mocker.patch("algokit.cli.tasks.send_transaction.stdin_has_content", return_value=True) + + # Act + result = invoke("task send ", input=json.dumps(txns)) + + # Assert + assert result.exit_code == 0 + verify(result.output) + + +def test_mutually_exclusive_options() -> None: + # Act + result = invoke( + "task send --file dummy.txns --transaction dummy.txn", + ) + + # Assert + assert result.exit_code == click.exceptions.UsageError.exit_code + verify(result.output) + + +def test_file_decoding_no_txn_error(tmp_path_factory: pytest.TempPathFactory) -> None: + # Arrange + cwd = tmp_path_factory.mktemp("cwd") + (cwd / "dummy.txns").touch() + + # Act + result = invoke( + "task send --file dummy.txns", + cwd=cwd, + ) + + # Assert + assert result.exit_code == 1 + verify(result.output) + + +def test_decoding_error(tmp_path_factory: pytest.TempPathFactory) -> None: + # Arrange + cwd = tmp_path_factory.mktemp("cwd") + (cwd / "dummy.txns").write_text("dummy") + + # Act + result = invoke( + "task send --file dummy.txns", + cwd=cwd, + ) + + # Assert + assert result.exit_code == 1 + verify(result.output) diff --git a/tests/tasks/test_send_transaction.test_decoding_error.approved.txt b/tests/tasks/test_send_transaction.test_decoding_error.approved.txt new file mode 100644 index 00000000..f8521869 --- /dev/null +++ b/tests/tasks/test_send_transaction.test_decoding_error.approved.txt @@ -0,0 +1,2 @@ +DEBUG: argument of type 'int' is not iterable +Error: Failed to decode transaction! If you are intending to send multiple transactions use `--file` instead. diff --git a/tests/tasks/test_send_transaction.test_file_decoding_no_txn_error.approved.txt b/tests/tasks/test_send_transaction.test_file_decoding_no_txn_error.approved.txt new file mode 100644 index 00000000..ec447b1c --- /dev/null +++ b/tests/tasks/test_send_transaction.test_file_decoding_no_txn_error.approved.txt @@ -0,0 +1 @@ +Error: No valid transactions found! diff --git a/tests/tasks/test_send_transaction.test_mutually_exclusive_options.approved.txt b/tests/tasks/test_send_transaction.test_mutually_exclusive_options.approved.txt new file mode 100644 index 00000000..ae82c389 --- /dev/null +++ b/tests/tasks/test_send_transaction.test_mutually_exclusive_options.approved.txt @@ -0,0 +1 @@ +Error: Illegal usage: 'file' is mutually exclusive with transaction. diff --git a/tests/tasks/test_send_transaction.test_send_atomic_txn_group_successful.approved.txt b/tests/tasks/test_send_transaction.test_send_atomic_txn_group_successful.approved.txt new file mode 100644 index 00000000..ea45cae8 --- /dev/null +++ b/tests/tasks/test_send_transaction.test_send_atomic_txn_group_successful.approved.txt @@ -0,0 +1,2 @@ +Transaction group successfully sent with txid: dummy_tx_id +Check transaction group status at: https://app.dappflow.org/setnetwork?name=sandbox&redirect=explorer/transaction/dummy_tx_id/ diff --git a/tests/tasks/test_send_transaction.test_send_from_file_successful.approved.txt b/tests/tasks/test_send_transaction.test_send_from_file_successful.approved.txt new file mode 100644 index 00000000..13eb4593 --- /dev/null +++ b/tests/tasks/test_send_transaction.test_send_from_file_successful.approved.txt @@ -0,0 +1,80 @@ + +Sending transaction 1/20 +Transaction successfully sent with txid: dummy_tx_id_0 +Check transaction status at: https://app.dappflow.org/setnetwork?name=sandbox&redirect=explorer/transaction/dummy_tx_id_0/ + +Sending transaction 2/20 +Transaction successfully sent with txid: dummy_tx_id_1 +Check transaction status at: https://app.dappflow.org/setnetwork?name=sandbox&redirect=explorer/transaction/dummy_tx_id_1/ + +Sending transaction 3/20 +Transaction successfully sent with txid: dummy_tx_id_2 +Check transaction status at: https://app.dappflow.org/setnetwork?name=sandbox&redirect=explorer/transaction/dummy_tx_id_2/ + +Sending transaction 4/20 +Transaction successfully sent with txid: dummy_tx_id_3 +Check transaction status at: https://app.dappflow.org/setnetwork?name=sandbox&redirect=explorer/transaction/dummy_tx_id_3/ + +Sending transaction 5/20 +Transaction successfully sent with txid: dummy_tx_id_4 +Check transaction status at: https://app.dappflow.org/setnetwork?name=sandbox&redirect=explorer/transaction/dummy_tx_id_4/ + +Sending transaction 6/20 +Transaction successfully sent with txid: dummy_tx_id_5 +Check transaction status at: https://app.dappflow.org/setnetwork?name=sandbox&redirect=explorer/transaction/dummy_tx_id_5/ + +Sending transaction 7/20 +Transaction successfully sent with txid: dummy_tx_id_6 +Check transaction status at: https://app.dappflow.org/setnetwork?name=sandbox&redirect=explorer/transaction/dummy_tx_id_6/ + +Sending transaction 8/20 +Transaction successfully sent with txid: dummy_tx_id_7 +Check transaction status at: https://app.dappflow.org/setnetwork?name=sandbox&redirect=explorer/transaction/dummy_tx_id_7/ + +Sending transaction 9/20 +Transaction successfully sent with txid: dummy_tx_id_8 +Check transaction status at: https://app.dappflow.org/setnetwork?name=sandbox&redirect=explorer/transaction/dummy_tx_id_8/ + +Sending transaction 10/20 +Transaction successfully sent with txid: dummy_tx_id_9 +Check transaction status at: https://app.dappflow.org/setnetwork?name=sandbox&redirect=explorer/transaction/dummy_tx_id_9/ + +Sending transaction 11/20 +Transaction successfully sent with txid: dummy_tx_id_10 +Check transaction status at: https://app.dappflow.org/setnetwork?name=sandbox&redirect=explorer/transaction/dummy_tx_id_10/ + +Sending transaction 12/20 +Transaction successfully sent with txid: dummy_tx_id_11 +Check transaction status at: https://app.dappflow.org/setnetwork?name=sandbox&redirect=explorer/transaction/dummy_tx_id_11/ + +Sending transaction 13/20 +Transaction successfully sent with txid: dummy_tx_id_12 +Check transaction status at: https://app.dappflow.org/setnetwork?name=sandbox&redirect=explorer/transaction/dummy_tx_id_12/ + +Sending transaction 14/20 +Transaction successfully sent with txid: dummy_tx_id_13 +Check transaction status at: https://app.dappflow.org/setnetwork?name=sandbox&redirect=explorer/transaction/dummy_tx_id_13/ + +Sending transaction 15/20 +Transaction successfully sent with txid: dummy_tx_id_14 +Check transaction status at: https://app.dappflow.org/setnetwork?name=sandbox&redirect=explorer/transaction/dummy_tx_id_14/ + +Sending transaction 16/20 +Transaction successfully sent with txid: dummy_tx_id_15 +Check transaction status at: https://app.dappflow.org/setnetwork?name=sandbox&redirect=explorer/transaction/dummy_tx_id_15/ + +Sending transaction 17/20 +Transaction successfully sent with txid: dummy_tx_id_16 +Check transaction status at: https://app.dappflow.org/setnetwork?name=sandbox&redirect=explorer/transaction/dummy_tx_id_16/ + +Sending transaction 18/20 +Transaction successfully sent with txid: dummy_tx_id_17 +Check transaction status at: https://app.dappflow.org/setnetwork?name=sandbox&redirect=explorer/transaction/dummy_tx_id_17/ + +Sending transaction 19/20 +Transaction successfully sent with txid: dummy_tx_id_18 +Check transaction status at: https://app.dappflow.org/setnetwork?name=sandbox&redirect=explorer/transaction/dummy_tx_id_18/ + +Sending transaction 20/20 +Transaction successfully sent with txid: dummy_tx_id_19 +Check transaction status at: https://app.dappflow.org/setnetwork?name=sandbox&redirect=explorer/transaction/dummy_tx_id_19/ diff --git a/tests/tasks/test_send_transaction.test_send_from_piped_input_successful.approved.txt b/tests/tasks/test_send_transaction.test_send_from_piped_input_successful.approved.txt new file mode 100644 index 00000000..13eb4593 --- /dev/null +++ b/tests/tasks/test_send_transaction.test_send_from_piped_input_successful.approved.txt @@ -0,0 +1,80 @@ + +Sending transaction 1/20 +Transaction successfully sent with txid: dummy_tx_id_0 +Check transaction status at: https://app.dappflow.org/setnetwork?name=sandbox&redirect=explorer/transaction/dummy_tx_id_0/ + +Sending transaction 2/20 +Transaction successfully sent with txid: dummy_tx_id_1 +Check transaction status at: https://app.dappflow.org/setnetwork?name=sandbox&redirect=explorer/transaction/dummy_tx_id_1/ + +Sending transaction 3/20 +Transaction successfully sent with txid: dummy_tx_id_2 +Check transaction status at: https://app.dappflow.org/setnetwork?name=sandbox&redirect=explorer/transaction/dummy_tx_id_2/ + +Sending transaction 4/20 +Transaction successfully sent with txid: dummy_tx_id_3 +Check transaction status at: https://app.dappflow.org/setnetwork?name=sandbox&redirect=explorer/transaction/dummy_tx_id_3/ + +Sending transaction 5/20 +Transaction successfully sent with txid: dummy_tx_id_4 +Check transaction status at: https://app.dappflow.org/setnetwork?name=sandbox&redirect=explorer/transaction/dummy_tx_id_4/ + +Sending transaction 6/20 +Transaction successfully sent with txid: dummy_tx_id_5 +Check transaction status at: https://app.dappflow.org/setnetwork?name=sandbox&redirect=explorer/transaction/dummy_tx_id_5/ + +Sending transaction 7/20 +Transaction successfully sent with txid: dummy_tx_id_6 +Check transaction status at: https://app.dappflow.org/setnetwork?name=sandbox&redirect=explorer/transaction/dummy_tx_id_6/ + +Sending transaction 8/20 +Transaction successfully sent with txid: dummy_tx_id_7 +Check transaction status at: https://app.dappflow.org/setnetwork?name=sandbox&redirect=explorer/transaction/dummy_tx_id_7/ + +Sending transaction 9/20 +Transaction successfully sent with txid: dummy_tx_id_8 +Check transaction status at: https://app.dappflow.org/setnetwork?name=sandbox&redirect=explorer/transaction/dummy_tx_id_8/ + +Sending transaction 10/20 +Transaction successfully sent with txid: dummy_tx_id_9 +Check transaction status at: https://app.dappflow.org/setnetwork?name=sandbox&redirect=explorer/transaction/dummy_tx_id_9/ + +Sending transaction 11/20 +Transaction successfully sent with txid: dummy_tx_id_10 +Check transaction status at: https://app.dappflow.org/setnetwork?name=sandbox&redirect=explorer/transaction/dummy_tx_id_10/ + +Sending transaction 12/20 +Transaction successfully sent with txid: dummy_tx_id_11 +Check transaction status at: https://app.dappflow.org/setnetwork?name=sandbox&redirect=explorer/transaction/dummy_tx_id_11/ + +Sending transaction 13/20 +Transaction successfully sent with txid: dummy_tx_id_12 +Check transaction status at: https://app.dappflow.org/setnetwork?name=sandbox&redirect=explorer/transaction/dummy_tx_id_12/ + +Sending transaction 14/20 +Transaction successfully sent with txid: dummy_tx_id_13 +Check transaction status at: https://app.dappflow.org/setnetwork?name=sandbox&redirect=explorer/transaction/dummy_tx_id_13/ + +Sending transaction 15/20 +Transaction successfully sent with txid: dummy_tx_id_14 +Check transaction status at: https://app.dappflow.org/setnetwork?name=sandbox&redirect=explorer/transaction/dummy_tx_id_14/ + +Sending transaction 16/20 +Transaction successfully sent with txid: dummy_tx_id_15 +Check transaction status at: https://app.dappflow.org/setnetwork?name=sandbox&redirect=explorer/transaction/dummy_tx_id_15/ + +Sending transaction 17/20 +Transaction successfully sent with txid: dummy_tx_id_16 +Check transaction status at: https://app.dappflow.org/setnetwork?name=sandbox&redirect=explorer/transaction/dummy_tx_id_16/ + +Sending transaction 18/20 +Transaction successfully sent with txid: dummy_tx_id_17 +Check transaction status at: https://app.dappflow.org/setnetwork?name=sandbox&redirect=explorer/transaction/dummy_tx_id_17/ + +Sending transaction 19/20 +Transaction successfully sent with txid: dummy_tx_id_18 +Check transaction status at: https://app.dappflow.org/setnetwork?name=sandbox&redirect=explorer/transaction/dummy_tx_id_18/ + +Sending transaction 20/20 +Transaction successfully sent with txid: dummy_tx_id_19 +Check transaction status at: https://app.dappflow.org/setnetwork?name=sandbox&redirect=explorer/transaction/dummy_tx_id_19/ diff --git a/tests/tasks/test_send_transaction.test_send_from_transaction_successful.approved.txt b/tests/tasks/test_send_transaction.test_send_from_transaction_successful.approved.txt new file mode 100644 index 00000000..215e6e74 --- /dev/null +++ b/tests/tasks/test_send_transaction.test_send_from_transaction_successful.approved.txt @@ -0,0 +1,4 @@ + +Sending transaction 1/1 +Transaction successfully sent with txid: dummy_tx_id +Check transaction status at: https://app.dappflow.org/setnetwork?name=sandbox&redirect=explorer/transaction/dummy_tx_id/ diff --git a/tests/tasks/test_sign_transaction.py b/tests/tasks/test_sign_transaction.py index c34863b2..17160665 100644 --- a/tests/tasks/test_sign_transaction.py +++ b/tests/tasks/test_sign_transaction.py @@ -3,27 +3,12 @@ import click import pytest from algokit.core.tasks.wallet import WALLET_ALIASES_KEYRING_USERNAME -from algokit_utils import Account from algosdk import encoding, mnemonic, transaction +from tests.tasks.conftest import DUMMY_ACCOUNT, DUMMY_SUGGESTED_PARAMS from tests.utils.approvals import verify from tests.utils.click_invoker import invoke -DUMMY_SUGGESTED_PARAMS = transaction.SuggestedParams( # type: ignore[no-untyped-call] - fee=0, - first=33652328, - last=33653328, - gen="testnet-v1.0", - gh="SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=", - min_fee=1000, - flat_fee=True, - consensus_version="https://github.com/algorandfoundation/specs/tree/abd3d4823c6f77349fc04c3af7b1e99fe4df699f", -) -DUMMY_ACCOUNT = Account( - private_key="iLsfFiRDwi0ijFdvdyO1PGkYxooOanbJSgpJ4pPKjKZluk70pvuPX4dYD1Jir85uZP+AImM/8SBmdPRpBSTFAg==", - address="MW5E55FG7OHV7B2YB5JGFL6ONZSP7ABCMM77CIDGOT2GSBJEYUBOF3UYKA", -) - def _generate_dummy_txn(sender: str, amount: int = 1) -> transaction.PaymentTxn: return transaction.PaymentTxn(sender, DUMMY_SUGGESTED_PARAMS, sender, amt=amount) # type: ignore[no-untyped-call] diff --git a/tests/tasks/test_sign_transaction.test_sign_atomic_txn_group_successful.approved.txt b/tests/tasks/test_sign_transaction.test_sign_atomic_txn_group_successful.approved.txt index bac10e89..33a9a5a6 100644 --- a/tests/tasks/test_sign_transaction.test_sign_atomic_txn_group_successful.approved.txt +++ b/tests/tasks/test_sign_transaction.test_sign_atomic_txn_group_successful.approved.txt @@ -1,51 +1,41 @@ [ { - "txn_id": "RUU4DMAYJ5TLIEZ3ZT3PWTDPUOHCGOFY26JAXMOQXE52J2W6D7UA", + "transaction_id": "RUU4DMAYJ5TLIEZ3ZT3PWTDPUOHCGOFY26JAXMOQXE52J2W6D7UA", "content": { - "sender": "MW5E55FG7OHV7B2YB5JGFL6ONZSP7ABCMM77CIDGOT2GSBJEYUBOF3UYKA", - "fee": 0, - "first_valid_round": 33652328, - "last_valid_round": 33653328, - "note": null, - "genesis_id": "testnet-v1.0", - "genesis_hash": "SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=", - "group": "Vyhyt7HcQWkkBxcIR4zP+GkJLUAmN2H2D5Hx03fAVvo=", - "lease": null, - "type": "pay", - "rekey_to": null, - "receiver": "MW5E55FG7OHV7B2YB5JGFL6ONZSP7ABCMM77CIDGOT2GSBJEYUBOF3UYKA", "amt": 1, - "close_remainder_to": null + "fv": 33652328, + "gen": "testnet-v1.0", + "gh": "SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=", + "grp": "Vyhyt7HcQWkkBxcIR4zP+GkJLUAmN2H2D5Hx03fAVvo=", + "lv": 33653328, + "rcv": "ZbpO9Kb7j1+HWA9SYq/ObmT/gCJjP/EgZnT0aQUkxQI=", + "snd": "ZbpO9Kb7j1+HWA9SYq/ObmT/gCJjP/EgZnT0aQUkxQI=", + "type": "pay" } }, { - "txn_id": "RUU4DMAYJ5TLIEZ3ZT3PWTDPUOHCGOFY26JAXMOQXE52J2W6D7UA", + "transaction_id": "RUU4DMAYJ5TLIEZ3ZT3PWTDPUOHCGOFY26JAXMOQXE52J2W6D7UA", "content": { - "sender": "MW5E55FG7OHV7B2YB5JGFL6ONZSP7ABCMM77CIDGOT2GSBJEYUBOF3UYKA", - "fee": 0, - "first_valid_round": 33652328, - "last_valid_round": 33653328, - "note": null, - "genesis_id": "testnet-v1.0", - "genesis_hash": "SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=", - "group": "Vyhyt7HcQWkkBxcIR4zP+GkJLUAmN2H2D5Hx03fAVvo=", - "lease": null, - "type": "pay", - "rekey_to": null, - "receiver": "MW5E55FG7OHV7B2YB5JGFL6ONZSP7ABCMM77CIDGOT2GSBJEYUBOF3UYKA", "amt": 1, - "close_remainder_to": null + "fv": 33652328, + "gen": "testnet-v1.0", + "gh": "SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=", + "grp": "Vyhyt7HcQWkkBxcIR4zP+GkJLUAmN2H2D5Hx03fAVvo=", + "lv": 33653328, + "rcv": "ZbpO9Kb7j1+HWA9SYq/ObmT/gCJjP/EgZnT0aQUkxQI=", + "snd": "ZbpO9Kb7j1+HWA9SYq/ObmT/gCJjP/EgZnT0aQUkxQI=", + "type": "pay" } } ] Would you like to proceed with signing the above? (y, n) [n]: y [ { - "txn_id": "RUU4DMAYJ5TLIEZ3ZT3PWTDPUOHCGOFY26JAXMOQXE52J2W6D7UA", + "transaction_id": "RUU4DMAYJ5TLIEZ3ZT3PWTDPUOHCGOFY26JAXMOQXE52J2W6D7UA", "content": "gqNzaWfEQMSdIjL5dbUk+/lVCi0AWs84ikMH0gEErgYxuNHpY/dYkKIvY9XlhiszYNMhk2XlkvTCkrrWGOWmblX3Af43tg+jdHhuiaNhbXQBomZ2zgIBfmijZ2VurHRlc3RuZXQtdjEuMKJnaMQgSGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiKjZ3JwxCBXKHK3sdxBaSQHFwhHjM/4aQktQCY3YfYPkfHTd8BW+qJsds4CAYJQo3JjdsQgZbpO9Kb7j1+HWA9SYq/ObmT/gCJjP/EgZnT0aQUkxQKjc25kxCBluk70pvuPX4dYD1Jir85uZP+AImM/8SBmdPRpBSTFAqR0eXBlo3BheQ==" }, { - "txn_id": "RUU4DMAYJ5TLIEZ3ZT3PWTDPUOHCGOFY26JAXMOQXE52J2W6D7UA", + "transaction_id": "RUU4DMAYJ5TLIEZ3ZT3PWTDPUOHCGOFY26JAXMOQXE52J2W6D7UA", "content": "gqNzaWfEQMSdIjL5dbUk+/lVCi0AWs84ikMH0gEErgYxuNHpY/dYkKIvY9XlhiszYNMhk2XlkvTCkrrWGOWmblX3Af43tg+jdHhuiaNhbXQBomZ2zgIBfmijZ2VurHRlc3RuZXQtdjEuMKJnaMQgSGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiKjZ3JwxCBXKHK3sdxBaSQHFwhHjM/4aQktQCY3YfYPkfHTd8BW+qJsds4CAYJQo3JjdsQgZbpO9Kb7j1+HWA9SYq/ObmT/gCJjP/EgZnT0aQUkxQKjc25kxCBluk70pvuPX4dYD1Jir85uZP+AImM/8SBmdPRpBSTFAqR0eXBlo3BheQ==" } ] diff --git a/tests/tasks/test_sign_transaction.test_sign_from_stdin_with_address_successful.approved.txt b/tests/tasks/test_sign_transaction.test_sign_from_stdin_with_address_successful.approved.txt index 4e0a7fe7..b34b6014 100644 --- a/tests/tasks/test_sign_transaction.test_sign_from_stdin_with_address_successful.approved.txt +++ b/tests/tasks/test_sign_transaction.test_sign_from_stdin_with_address_successful.approved.txt @@ -1,29 +1,23 @@ Enter the mnemonic phrase (25 words separated by whitespace): [ { - "txn_id": "YGSOMX5QSASQALR5V4MH47L4GEHFJZVGSUETJAWUFM2I6PJAQL4Q", + "transaction_id": "YGSOMX5QSASQALR5V4MH47L4GEHFJZVGSUETJAWUFM2I6PJAQL4Q", "content": { - "sender": "MW5E55FG7OHV7B2YB5JGFL6ONZSP7ABCMM77CIDGOT2GSBJEYUBOF3UYKA", - "fee": 0, - "first_valid_round": 33652328, - "last_valid_round": 33653328, - "note": null, - "genesis_id": "testnet-v1.0", - "genesis_hash": "SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=", - "group": null, - "lease": null, - "type": "pay", - "rekey_to": null, - "receiver": "MW5E55FG7OHV7B2YB5JGFL6ONZSP7ABCMM77CIDGOT2GSBJEYUBOF3UYKA", "amt": 1, - "close_remainder_to": null + "fv": 33652328, + "gen": "testnet-v1.0", + "gh": "SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=", + "lv": 33653328, + "rcv": "ZbpO9Kb7j1+HWA9SYq/ObmT/gCJjP/EgZnT0aQUkxQI=", + "snd": "ZbpO9Kb7j1+HWA9SYq/ObmT/gCJjP/EgZnT0aQUkxQI=", + "type": "pay" } } ] Would you like to proceed with signing the above? (y, n) [n]: y [ { - "txn_id": "YGSOMX5QSASQALR5V4MH47L4GEHFJZVGSUETJAWUFM2I6PJAQL4Q", + "transaction_id": "YGSOMX5QSASQALR5V4MH47L4GEHFJZVGSUETJAWUFM2I6PJAQL4Q", "content": "gqNzaWfEQHHuPbhkAADyq8eKU/NiDCuJ+cnW9MrHT3iAAEPm+okmoio/rmgctA7QUpqqd4eF5aYlUcgz9EbU+uUXS1rFOg2jdHhuiKNhbXQBomZ2zgIBfmijZ2VurHRlc3RuZXQtdjEuMKJnaMQgSGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiKibHbOAgGCUKNyY3bEIGW6TvSm+49fh1gPUmKvzm5k/4AiYz/xIGZ09GkFJMUCo3NuZMQgZbpO9Kb7j1+HWA9SYq/ObmT/gCJjP/EgZnT0aQUkxQKkdHlwZaNwYXk=" } ] diff --git a/tests/tasks/test_sign_transaction.test_sign_from_stdin_with_alias_successful.approved.txt b/tests/tasks/test_sign_transaction.test_sign_from_stdin_with_alias_successful.approved.txt index 5e30a483..c4e9e2b3 100644 --- a/tests/tasks/test_sign_transaction.test_sign_from_stdin_with_alias_successful.approved.txt +++ b/tests/tasks/test_sign_transaction.test_sign_from_stdin_with_alias_successful.approved.txt @@ -1,28 +1,22 @@ [ { - "txn_id": "YGSOMX5QSASQALR5V4MH47L4GEHFJZVGSUETJAWUFM2I6PJAQL4Q", + "transaction_id": "YGSOMX5QSASQALR5V4MH47L4GEHFJZVGSUETJAWUFM2I6PJAQL4Q", "content": { - "sender": "MW5E55FG7OHV7B2YB5JGFL6ONZSP7ABCMM77CIDGOT2GSBJEYUBOF3UYKA", - "fee": 0, - "first_valid_round": 33652328, - "last_valid_round": 33653328, - "note": null, - "genesis_id": "testnet-v1.0", - "genesis_hash": "SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=", - "group": null, - "lease": null, - "type": "pay", - "rekey_to": null, - "receiver": "MW5E55FG7OHV7B2YB5JGFL6ONZSP7ABCMM77CIDGOT2GSBJEYUBOF3UYKA", "amt": 1, - "close_remainder_to": null + "fv": 33652328, + "gen": "testnet-v1.0", + "gh": "SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=", + "lv": 33653328, + "rcv": "ZbpO9Kb7j1+HWA9SYq/ObmT/gCJjP/EgZnT0aQUkxQI=", + "snd": "ZbpO9Kb7j1+HWA9SYq/ObmT/gCJjP/EgZnT0aQUkxQI=", + "type": "pay" } } ] Would you like to proceed with signing the above? (y, n) [n]: y [ { - "txn_id": "YGSOMX5QSASQALR5V4MH47L4GEHFJZVGSUETJAWUFM2I6PJAQL4Q", + "transaction_id": "YGSOMX5QSASQALR5V4MH47L4GEHFJZVGSUETJAWUFM2I6PJAQL4Q", "content": "gqNzaWfEQHHuPbhkAADyq8eKU/NiDCuJ+cnW9MrHT3iAAEPm+okmoio/rmgctA7QUpqqd4eF5aYlUcgz9EbU+uUXS1rFOg2jdHhuiKNhbXQBomZ2zgIBfmijZ2VurHRlc3RuZXQtdjEuMKJnaMQgSGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiKibHbOAgGCUKNyY3bEIGW6TvSm+49fh1gPUmKvzm5k/4AiYz/xIGZ09GkFJMUCo3NuZMQgZbpO9Kb7j1+HWA9SYq/ObmT/gCJjP/EgZnT0aQUkxQKkdHlwZaNwYXk=" } ] diff --git a/tests/tasks/test_sign_transaction.test_sign_many_from_file_with_address_successful.approved.txt b/tests/tasks/test_sign_transaction.test_sign_many_from_file_with_address_successful.approved.txt index b961b42f..906bb31b 100644 --- a/tests/tasks/test_sign_transaction.test_sign_many_from_file_with_address_successful.approved.txt +++ b/tests/tasks/test_sign_transaction.test_sign_many_from_file_with_address_successful.approved.txt @@ -1,75 +1,57 @@ Enter the mnemonic phrase (25 words separated by whitespace): [ { - "txn_id": "YGSOMX5QSASQALR5V4MH47L4GEHFJZVGSUETJAWUFM2I6PJAQL4Q", + "transaction_id": "YGSOMX5QSASQALR5V4MH47L4GEHFJZVGSUETJAWUFM2I6PJAQL4Q", "content": { - "sender": "MW5E55FG7OHV7B2YB5JGFL6ONZSP7ABCMM77CIDGOT2GSBJEYUBOF3UYKA", - "fee": 0, - "first_valid_round": 33652328, - "last_valid_round": 33653328, - "note": null, - "genesis_id": "testnet-v1.0", - "genesis_hash": "SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=", - "group": null, - "lease": null, - "type": "pay", - "rekey_to": null, - "receiver": "MW5E55FG7OHV7B2YB5JGFL6ONZSP7ABCMM77CIDGOT2GSBJEYUBOF3UYKA", "amt": 1, - "close_remainder_to": null + "fv": 33652328, + "gen": "testnet-v1.0", + "gh": "SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=", + "lv": 33653328, + "rcv": "ZbpO9Kb7j1+HWA9SYq/ObmT/gCJjP/EgZnT0aQUkxQI=", + "snd": "ZbpO9Kb7j1+HWA9SYq/ObmT/gCJjP/EgZnT0aQUkxQI=", + "type": "pay" } }, { - "txn_id": "MLS7IUJQVK7GABTCOLTG5QE7NWITX74D6YC3QZLN6Y5SZF23WZBQ", + "transaction_id": "MLS7IUJQVK7GABTCOLTG5QE7NWITX74D6YC3QZLN6Y5SZF23WZBQ", "content": { - "sender": "MW5E55FG7OHV7B2YB5JGFL6ONZSP7ABCMM77CIDGOT2GSBJEYUBOF3UYKA", - "fee": 0, - "first_valid_round": 33652328, - "last_valid_round": 33653328, - "note": null, - "genesis_id": "testnet-v1.0", - "genesis_hash": "SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=", - "group": null, - "lease": null, - "type": "pay", - "rekey_to": null, - "receiver": "MW5E55FG7OHV7B2YB5JGFL6ONZSP7ABCMM77CIDGOT2GSBJEYUBOF3UYKA", "amt": 2, - "close_remainder_to": null + "fv": 33652328, + "gen": "testnet-v1.0", + "gh": "SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=", + "lv": 33653328, + "rcv": "ZbpO9Kb7j1+HWA9SYq/ObmT/gCJjP/EgZnT0aQUkxQI=", + "snd": "ZbpO9Kb7j1+HWA9SYq/ObmT/gCJjP/EgZnT0aQUkxQI=", + "type": "pay" } }, { - "txn_id": "NEI3KU7ALQ6PFOSTYEN3LSJX2CUDEESDH2NH2XUJGYHNR3UVB3OQ", + "transaction_id": "NEI3KU7ALQ6PFOSTYEN3LSJX2CUDEESDH2NH2XUJGYHNR3UVB3OQ", "content": { - "sender": "MW5E55FG7OHV7B2YB5JGFL6ONZSP7ABCMM77CIDGOT2GSBJEYUBOF3UYKA", - "fee": 0, - "first_valid_round": 33652328, - "last_valid_round": 33653328, - "note": null, - "genesis_id": "testnet-v1.0", - "genesis_hash": "SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=", - "group": null, - "lease": null, - "type": "pay", - "rekey_to": null, - "receiver": "MW5E55FG7OHV7B2YB5JGFL6ONZSP7ABCMM77CIDGOT2GSBJEYUBOF3UYKA", "amt": 3, - "close_remainder_to": null + "fv": 33652328, + "gen": "testnet-v1.0", + "gh": "SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=", + "lv": 33653328, + "rcv": "ZbpO9Kb7j1+HWA9SYq/ObmT/gCJjP/EgZnT0aQUkxQI=", + "snd": "ZbpO9Kb7j1+HWA9SYq/ObmT/gCJjP/EgZnT0aQUkxQI=", + "type": "pay" } } ] Would you like to proceed with signing the above? (y, n) [n]: y [ { - "txn_id": "YGSOMX5QSASQALR5V4MH47L4GEHFJZVGSUETJAWUFM2I6PJAQL4Q", + "transaction_id": "YGSOMX5QSASQALR5V4MH47L4GEHFJZVGSUETJAWUFM2I6PJAQL4Q", "content": "gqNzaWfEQHHuPbhkAADyq8eKU/NiDCuJ+cnW9MrHT3iAAEPm+okmoio/rmgctA7QUpqqd4eF5aYlUcgz9EbU+uUXS1rFOg2jdHhuiKNhbXQBomZ2zgIBfmijZ2VurHRlc3RuZXQtdjEuMKJnaMQgSGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiKibHbOAgGCUKNyY3bEIGW6TvSm+49fh1gPUmKvzm5k/4AiYz/xIGZ09GkFJMUCo3NuZMQgZbpO9Kb7j1+HWA9SYq/ObmT/gCJjP/EgZnT0aQUkxQKkdHlwZaNwYXk=" }, { - "txn_id": "MLS7IUJQVK7GABTCOLTG5QE7NWITX74D6YC3QZLN6Y5SZF23WZBQ", + "transaction_id": "MLS7IUJQVK7GABTCOLTG5QE7NWITX74D6YC3QZLN6Y5SZF23WZBQ", "content": "gqNzaWfEQMDbNg81bX8hvW+MHye1pJhtDxWvk/3Oec8lC8QZ5i0pn7LNnXTEniYzhECzjwxL11ENlTh6w66M2jl1f4YeZwijdHhuiKNhbXQComZ2zgIBfmijZ2VurHRlc3RuZXQtdjEuMKJnaMQgSGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiKibHbOAgGCUKNyY3bEIGW6TvSm+49fh1gPUmKvzm5k/4AiYz/xIGZ09GkFJMUCo3NuZMQgZbpO9Kb7j1+HWA9SYq/ObmT/gCJjP/EgZnT0aQUkxQKkdHlwZaNwYXk=" }, { - "txn_id": "NEI3KU7ALQ6PFOSTYEN3LSJX2CUDEESDH2NH2XUJGYHNR3UVB3OQ", + "transaction_id": "NEI3KU7ALQ6PFOSTYEN3LSJX2CUDEESDH2NH2XUJGYHNR3UVB3OQ", "content": "gqNzaWfEQJPaDlnXKBkMacMv6SNJmDlRf2dBzVBZwhOelc/Q+uriONVXzP0pqmZqqCua/fo3DxH9tbKyCDcSYVmn9MsaLgqjdHhuiKNhbXQDomZ2zgIBfmijZ2VurHRlc3RuZXQtdjEuMKJnaMQgSGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiKibHbOAgGCUKNyY3bEIGW6TvSm+49fh1gPUmKvzm5k/4AiYz/xIGZ09GkFJMUCo3NuZMQgZbpO9Kb7j1+HWA9SYq/ObmT/gCJjP/EgZnT0aQUkxQKkdHlwZaNwYXk=" } ] diff --git a/tests/tasks/test_sign_transaction.test_sign_many_from_file_with_alias_successful.approved.txt b/tests/tasks/test_sign_transaction.test_sign_many_from_file_with_alias_successful.approved.txt index 5e131639..ecd96945 100644 --- a/tests/tasks/test_sign_transaction.test_sign_many_from_file_with_alias_successful.approved.txt +++ b/tests/tasks/test_sign_transaction.test_sign_many_from_file_with_alias_successful.approved.txt @@ -1,74 +1,56 @@ [ { - "txn_id": "YGSOMX5QSASQALR5V4MH47L4GEHFJZVGSUETJAWUFM2I6PJAQL4Q", + "transaction_id": "YGSOMX5QSASQALR5V4MH47L4GEHFJZVGSUETJAWUFM2I6PJAQL4Q", "content": { - "sender": "MW5E55FG7OHV7B2YB5JGFL6ONZSP7ABCMM77CIDGOT2GSBJEYUBOF3UYKA", - "fee": 0, - "first_valid_round": 33652328, - "last_valid_round": 33653328, - "note": null, - "genesis_id": "testnet-v1.0", - "genesis_hash": "SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=", - "group": null, - "lease": null, - "type": "pay", - "rekey_to": null, - "receiver": "MW5E55FG7OHV7B2YB5JGFL6ONZSP7ABCMM77CIDGOT2GSBJEYUBOF3UYKA", "amt": 1, - "close_remainder_to": null + "fv": 33652328, + "gen": "testnet-v1.0", + "gh": "SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=", + "lv": 33653328, + "rcv": "ZbpO9Kb7j1+HWA9SYq/ObmT/gCJjP/EgZnT0aQUkxQI=", + "snd": "ZbpO9Kb7j1+HWA9SYq/ObmT/gCJjP/EgZnT0aQUkxQI=", + "type": "pay" } }, { - "txn_id": "MLS7IUJQVK7GABTCOLTG5QE7NWITX74D6YC3QZLN6Y5SZF23WZBQ", + "transaction_id": "MLS7IUJQVK7GABTCOLTG5QE7NWITX74D6YC3QZLN6Y5SZF23WZBQ", "content": { - "sender": "MW5E55FG7OHV7B2YB5JGFL6ONZSP7ABCMM77CIDGOT2GSBJEYUBOF3UYKA", - "fee": 0, - "first_valid_round": 33652328, - "last_valid_round": 33653328, - "note": null, - "genesis_id": "testnet-v1.0", - "genesis_hash": "SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=", - "group": null, - "lease": null, - "type": "pay", - "rekey_to": null, - "receiver": "MW5E55FG7OHV7B2YB5JGFL6ONZSP7ABCMM77CIDGOT2GSBJEYUBOF3UYKA", "amt": 2, - "close_remainder_to": null + "fv": 33652328, + "gen": "testnet-v1.0", + "gh": "SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=", + "lv": 33653328, + "rcv": "ZbpO9Kb7j1+HWA9SYq/ObmT/gCJjP/EgZnT0aQUkxQI=", + "snd": "ZbpO9Kb7j1+HWA9SYq/ObmT/gCJjP/EgZnT0aQUkxQI=", + "type": "pay" } }, { - "txn_id": "NEI3KU7ALQ6PFOSTYEN3LSJX2CUDEESDH2NH2XUJGYHNR3UVB3OQ", + "transaction_id": "NEI3KU7ALQ6PFOSTYEN3LSJX2CUDEESDH2NH2XUJGYHNR3UVB3OQ", "content": { - "sender": "MW5E55FG7OHV7B2YB5JGFL6ONZSP7ABCMM77CIDGOT2GSBJEYUBOF3UYKA", - "fee": 0, - "first_valid_round": 33652328, - "last_valid_round": 33653328, - "note": null, - "genesis_id": "testnet-v1.0", - "genesis_hash": "SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=", - "group": null, - "lease": null, - "type": "pay", - "rekey_to": null, - "receiver": "MW5E55FG7OHV7B2YB5JGFL6ONZSP7ABCMM77CIDGOT2GSBJEYUBOF3UYKA", "amt": 3, - "close_remainder_to": null + "fv": 33652328, + "gen": "testnet-v1.0", + "gh": "SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=", + "lv": 33653328, + "rcv": "ZbpO9Kb7j1+HWA9SYq/ObmT/gCJjP/EgZnT0aQUkxQI=", + "snd": "ZbpO9Kb7j1+HWA9SYq/ObmT/gCJjP/EgZnT0aQUkxQI=", + "type": "pay" } } ] Would you like to proceed with signing the above? (y, n) [n]: y [ { - "txn_id": "YGSOMX5QSASQALR5V4MH47L4GEHFJZVGSUETJAWUFM2I6PJAQL4Q", + "transaction_id": "YGSOMX5QSASQALR5V4MH47L4GEHFJZVGSUETJAWUFM2I6PJAQL4Q", "content": "gqNzaWfEQHHuPbhkAADyq8eKU/NiDCuJ+cnW9MrHT3iAAEPm+okmoio/rmgctA7QUpqqd4eF5aYlUcgz9EbU+uUXS1rFOg2jdHhuiKNhbXQBomZ2zgIBfmijZ2VurHRlc3RuZXQtdjEuMKJnaMQgSGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiKibHbOAgGCUKNyY3bEIGW6TvSm+49fh1gPUmKvzm5k/4AiYz/xIGZ09GkFJMUCo3NuZMQgZbpO9Kb7j1+HWA9SYq/ObmT/gCJjP/EgZnT0aQUkxQKkdHlwZaNwYXk=" }, { - "txn_id": "MLS7IUJQVK7GABTCOLTG5QE7NWITX74D6YC3QZLN6Y5SZF23WZBQ", + "transaction_id": "MLS7IUJQVK7GABTCOLTG5QE7NWITX74D6YC3QZLN6Y5SZF23WZBQ", "content": "gqNzaWfEQMDbNg81bX8hvW+MHye1pJhtDxWvk/3Oec8lC8QZ5i0pn7LNnXTEniYzhECzjwxL11ENlTh6w66M2jl1f4YeZwijdHhuiKNhbXQComZ2zgIBfmijZ2VurHRlc3RuZXQtdjEuMKJnaMQgSGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiKibHbOAgGCUKNyY3bEIGW6TvSm+49fh1gPUmKvzm5k/4AiYz/xIGZ09GkFJMUCo3NuZMQgZbpO9Kb7j1+HWA9SYq/ObmT/gCJjP/EgZnT0aQUkxQKkdHlwZaNwYXk=" }, { - "txn_id": "NEI3KU7ALQ6PFOSTYEN3LSJX2CUDEESDH2NH2XUJGYHNR3UVB3OQ", + "transaction_id": "NEI3KU7ALQ6PFOSTYEN3LSJX2CUDEESDH2NH2XUJGYHNR3UVB3OQ", "content": "gqNzaWfEQJPaDlnXKBkMacMv6SNJmDlRf2dBzVBZwhOelc/Q+uriONVXzP0pqmZqqCua/fo3DxH9tbKyCDcSYVmn9MsaLgqjdHhuiKNhbXQDomZ2zgIBfmijZ2VurHRlc3RuZXQtdjEuMKJnaMQgSGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiKibHbOAgGCUKNyY3bEIGW6TvSm+49fh1gPUmKvzm5k/4AiYz/xIGZ09GkFJMUCo3NuZMQgZbpO9Kb7j1+HWA9SYq/ObmT/gCJjP/EgZnT0aQUkxQKkdHlwZaNwYXk=" } ] diff --git a/tests/tasks/test_sign_transaction.test_transaction_decoding_errors.approved.txt b/tests/tasks/test_sign_transaction.test_transaction_decoding_errors.approved.txt index fbc60fc0..9e6ffdeb 100644 --- a/tests/tasks/test_sign_transaction.test_transaction_decoding_errors.approved.txt +++ b/tests/tasks/test_sign_transaction.test_transaction_decoding_errors.approved.txt @@ -1,3 +1,3 @@ Enter the mnemonic phrase (25 words separated by whitespace): DEBUG: Invalid base64-encoded string: number of data characters (5) cannot be 1 more than a multiple of 4 -Error: Failed to decode transaction! If you are intending to sign multiple transactions use --file instead. +Error: Failed to decode transaction! If you are intending to sign multiple transactions use `--file` instead.