From de484ec5983625b55e61dae912c75660da8c884a Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 19 Feb 2024 11:42:45 +0200 Subject: [PATCH 1/4] handle proxy error --- multiversx_sdk_cli/cli_shared.py | 14 +++++++++++--- multiversx_sdk_cli/cli_transactions.py | 11 +++++++++-- multiversx_sdk_cli/cli_wallet.py | 1 + multiversx_sdk_cli/errors.py | 10 ++++++++++ multiversx_sdk_cli/transactions.py | 15 ++++++++++++--- pyproject.toml | 2 +- 6 files changed, 44 insertions(+), 9 deletions(-) diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index 02005c06..483078b4 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -6,6 +6,7 @@ from typing import Any, Dict, List, Text, cast from multiversx_sdk_core import Address +from multiversx_sdk_network_providers import GenericError from multiversx_sdk_network_providers.proxy_network_provider import \ ProxyNetworkProvider @@ -16,7 +17,7 @@ load_password) from multiversx_sdk_cli.constants import (DEFAULT_TX_VERSION, TRANSACTION_OPTIONS_TX_GUARDED) -from multiversx_sdk_cli.errors import ArgumentsNotProvidedError +from multiversx_sdk_cli.errors import ArgumentsNotProvidedError, ProxyError from multiversx_sdk_cli.interfaces import ITransaction from multiversx_sdk_cli.ledger.ledger_functions import do_get_ledger_address from multiversx_sdk_cli.simulation import Simulator @@ -268,8 +269,15 @@ def send_or_simulate(tx: ITransaction, args: Any, dump_output: bool = True) -> C transaction_on_network = send_and_wait_for_result(tx, proxy, args.timeout) output_builder.set_awaited_transaction(transaction_on_network) elif send_only: - hash = proxy.send_transaction(tx) - output_builder.set_emitted_transaction_hash(hash) + try: + hash = proxy.send_transaction(tx) + output_builder.set_emitted_transaction_hash(hash) + except GenericError as ge: + url = ge.url + message = ge.data["error"] + data = ge.data["data"] + code = ge.data["code"] + raise ProxyError(message, url, data, code) elif simulate: simulation = Simulator(proxy).run(tx) output_builder.set_simulation_results(simulation) diff --git a/multiversx_sdk_cli/cli_transactions.py b/multiversx_sdk_cli/cli_transactions.py index 6850ac8e..139706f9 100644 --- a/multiversx_sdk_cli/cli_transactions.py +++ b/multiversx_sdk_cli/cli_transactions.py @@ -1,13 +1,14 @@ from pathlib import Path from typing import Any, List +from multiversx_sdk_network_providers import GenericError from multiversx_sdk_network_providers.proxy_network_provider import \ ProxyNetworkProvider from multiversx_sdk_cli import cli_shared, utils from multiversx_sdk_cli.cli_output import CLIOutputBuilder from multiversx_sdk_cli.cosign_transaction import cosign_transaction -from multiversx_sdk_cli.errors import NoWalletProvided +from multiversx_sdk_cli.errors import NoWalletProvided, ProxyError from multiversx_sdk_cli.transactions import (compute_relayed_v1_data, do_prepare_transaction, load_transaction_from_file) @@ -88,11 +89,17 @@ def send_transaction(args: Any): tx = load_transaction_from_file(args.infile) output = CLIOutputBuilder() + proxy = ProxyNetworkProvider(args.proxy) try: - proxy = ProxyNetworkProvider(args.proxy) tx_hash = proxy.send_transaction(tx) output.set_emitted_transaction_hash(tx_hash) + except GenericError as ge: + url = ge.url + message = ge.data["error"] + data = ge.data["data"] + code = ge.data["code"] + raise ProxyError(message, url, data, code) finally: output = output.set_emitted_transaction(tx).build() utils.dump_out_json(output, outfile=args.outfile) diff --git a/multiversx_sdk_cli/cli_wallet.py b/multiversx_sdk_cli/cli_wallet.py index 337bd0aa..de7dd7ce 100644 --- a/multiversx_sdk_cli/cli_wallet.py +++ b/multiversx_sdk_cli/cli_wallet.py @@ -112,6 +112,7 @@ def wallet_new(args: Any): mnemonic = Mnemonic.generate() print(f"Mnemonic: {mnemonic.get_text()}") + print(f"Wallet address: {mnemonic.derive_key().generate_public_key().to_address(address_hrp).to_bech32()}") if format is None: return diff --git a/multiversx_sdk_cli/errors.py b/multiversx_sdk_cli/errors.py index 139b3c26..13b06e87 100644 --- a/multiversx_sdk_cli/errors.py +++ b/multiversx_sdk_cli/errors.py @@ -198,3 +198,13 @@ def __init__(self, message: str): class ArgumentsNotProvidedError(KnownError): def __init__(self, message: str): super().__init__(message) + + +class ProxyError(KnownError): + def __init__(self, message: str, url: str, data: str, code: str): + inner = { + "url": url, + "data": data, + "code": code + } + super().__init__(message, inner) diff --git a/multiversx_sdk_cli/transactions.py b/multiversx_sdk_cli/transactions.py index 418a469d..2265ea01 100644 --- a/multiversx_sdk_cli/transactions.py +++ b/multiversx_sdk_cli/transactions.py @@ -5,13 +5,14 @@ from typing import Any, Dict, Optional, Protocol, Sequence, TextIO, Tuple from multiversx_sdk_core import Address, Transaction, TransactionPayload +from multiversx_sdk_network_providers import GenericError from multiversx_sdk_cli import errors from multiversx_sdk_cli.accounts import Account, LedgerAccount from multiversx_sdk_cli.cli_password import (load_guardian_password, load_password) from multiversx_sdk_cli.cosign_transaction import cosign_transaction -from multiversx_sdk_cli.errors import NoWalletProvided +from multiversx_sdk_cli.errors import NoWalletProvided, ProxyError from multiversx_sdk_cli.interfaces import ITransaction from multiversx_sdk_cli.ledger.ledger_functions import do_get_ledger_address @@ -30,7 +31,7 @@ class INetworkProvider(Protocol): def send_transaction(self, transaction: ITransaction) -> str: ... - def send_transactions(self, transactions: Sequence[ITransaction]) -> Tuple[int, str]: + def send_transactions(self, transactions: Sequence[ITransaction]) -> Tuple[int, Dict[str, str]]: ... def get_transaction(self, tx_hash: str, with_process_status: Optional[bool] = False) -> ITransactionOnNetwork: @@ -132,7 +133,15 @@ def send_and_wait_for_result(transaction: ITransaction, proxy: INetworkProvider, def _send_transaction_and_wait_for_result(proxy: INetworkProvider, payload: ITransaction, num_seconds_timeout: int = 100) -> ITransactionOnNetwork: AWAIT_TRANSACTION_PERIOD = 5 - tx_hash = proxy.send_transaction(payload) + try: + tx_hash = proxy.send_transaction(payload) + except GenericError as ge: + url = ge.url + message = ge.data["error"] + data = ge.data["data"] + code = ge.data["code"] + raise ProxyError(message, url, data, code) + num_periods_to_wait = int(num_seconds_timeout / AWAIT_TRANSACTION_PERIOD) for _ in range(0, num_periods_to_wait): diff --git a/pyproject.toml b/pyproject.toml index 1940090a..c3c50e86 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "multiversx-sdk-cli" -version = "9.5.0" +version = "9.5.1" authors = [ { name="MultiversX" }, ] From 303411ede7ebac2fe90030c851cbebe82c916424 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 19 Feb 2024 12:09:33 +0200 Subject: [PATCH 2/4] fix unit tests --- multiversx_sdk_cli/tests/test_cli_wallet.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/multiversx_sdk_cli/tests/test_cli_wallet.py b/multiversx_sdk_cli/tests/test_cli_wallet.py index 2e495382..5255b4d3 100644 --- a/multiversx_sdk_cli/tests/test_cli_wallet.py +++ b/multiversx_sdk_cli/tests/test_cli_wallet.py @@ -367,7 +367,8 @@ def test_sign_and_verify_message_with_multi_address_pem(capsys: Any): def _read_stdout_mnemonic(capsys: Any) -> str: - return _read_stdout(capsys).replace("Mnemonic:", "").strip() + lines = _read_stdout(capsys).split("\n") + return lines[0].replace("Mnemonic:", "").strip() def _read_stdout(capsys: Any) -> str: From 26e5848cdb2ac42f834dc9d722b7c8f7c9778ed8 Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Mon, 19 Feb 2024 18:32:11 +0200 Subject: [PATCH 3/4] fixes after review --- multiversx_sdk_cli/cli_shared.py | 17 +++------ multiversx_sdk_cli/cli_transactions.py | 28 +++++---------- multiversx_sdk_cli/custom_network_provider.py | 36 +++++++++++++++++++ multiversx_sdk_cli/transactions.py | 18 ++-------- 4 files changed, 53 insertions(+), 46 deletions(-) create mode 100644 multiversx_sdk_cli/custom_network_provider.py diff --git a/multiversx_sdk_cli/cli_shared.py b/multiversx_sdk_cli/cli_shared.py index 483078b4..1f5dc517 100644 --- a/multiversx_sdk_cli/cli_shared.py +++ b/multiversx_sdk_cli/cli_shared.py @@ -6,7 +6,6 @@ from typing import Any, Dict, List, Text, cast from multiversx_sdk_core import Address -from multiversx_sdk_network_providers import GenericError from multiversx_sdk_network_providers.proxy_network_provider import \ ProxyNetworkProvider @@ -17,7 +16,8 @@ load_password) from multiversx_sdk_cli.constants import (DEFAULT_TX_VERSION, TRANSACTION_OPTIONS_TX_GUARDED) -from multiversx_sdk_cli.errors import ArgumentsNotProvidedError, ProxyError +from multiversx_sdk_cli.custom_network_provider import CustomNetworkProvider +from multiversx_sdk_cli.errors import ArgumentsNotProvidedError from multiversx_sdk_cli.interfaces import ITransaction from multiversx_sdk_cli.ledger.ledger_functions import do_get_ledger_address from multiversx_sdk_cli.simulation import Simulator @@ -250,7 +250,7 @@ def check_options_for_guarded_tx(options: int): def send_or_simulate(tx: ITransaction, args: Any, dump_output: bool = True) -> CLIOutputBuilder: - proxy = ProxyNetworkProvider(args.proxy) + proxy = CustomNetworkProvider(args.proxy) is_set_wait_result = hasattr(args, "wait_result") and args.wait_result is_set_send = hasattr(args, "send") and args.send @@ -269,15 +269,8 @@ def send_or_simulate(tx: ITransaction, args: Any, dump_output: bool = True) -> C transaction_on_network = send_and_wait_for_result(tx, proxy, args.timeout) output_builder.set_awaited_transaction(transaction_on_network) elif send_only: - try: - hash = proxy.send_transaction(tx) - output_builder.set_emitted_transaction_hash(hash) - except GenericError as ge: - url = ge.url - message = ge.data["error"] - data = ge.data["data"] - code = ge.data["code"] - raise ProxyError(message, url, data, code) + hash = proxy.send_transaction(tx) + output_builder.set_emitted_transaction_hash(hash) elif simulate: simulation = Simulator(proxy).run(tx) output_builder.set_simulation_results(simulation) diff --git a/multiversx_sdk_cli/cli_transactions.py b/multiversx_sdk_cli/cli_transactions.py index 139706f9..5c89e389 100644 --- a/multiversx_sdk_cli/cli_transactions.py +++ b/multiversx_sdk_cli/cli_transactions.py @@ -1,14 +1,11 @@ from pathlib import Path from typing import Any, List -from multiversx_sdk_network_providers import GenericError -from multiversx_sdk_network_providers.proxy_network_provider import \ - ProxyNetworkProvider - from multiversx_sdk_cli import cli_shared, utils from multiversx_sdk_cli.cli_output import CLIOutputBuilder from multiversx_sdk_cli.cosign_transaction import cosign_transaction -from multiversx_sdk_cli.errors import NoWalletProvided, ProxyError +from multiversx_sdk_cli.custom_network_provider import CustomNetworkProvider +from multiversx_sdk_cli.errors import NoWalletProvided from multiversx_sdk_cli.transactions import (compute_relayed_v1_data, do_prepare_transaction, load_transaction_from_file) @@ -89,26 +86,19 @@ def send_transaction(args: Any): tx = load_transaction_from_file(args.infile) output = CLIOutputBuilder() - proxy = ProxyNetworkProvider(args.proxy) + proxy = CustomNetworkProvider(args.proxy) - try: - tx_hash = proxy.send_transaction(tx) - output.set_emitted_transaction_hash(tx_hash) - except GenericError as ge: - url = ge.url - message = ge.data["error"] - data = ge.data["data"] - code = ge.data["code"] - raise ProxyError(message, url, data, code) - finally: - output = output.set_emitted_transaction(tx).build() - utils.dump_out_json(output, outfile=args.outfile) + tx_hash = proxy.send_transaction(tx) + output.set_emitted_transaction_hash(tx_hash) + + output = output.set_emitted_transaction(tx).build() + utils.dump_out_json(output, outfile=args.outfile) def get_transaction(args: Any): args = utils.as_object(args) omit_fields = cli_shared.parse_omit_fields_arg(args) - proxy = ProxyNetworkProvider(args.proxy) + proxy = CustomNetworkProvider(args.proxy) transaction = proxy.get_transaction(args.hash, True) output = CLIOutputBuilder().set_transaction_on_network(transaction, omit_fields).build() diff --git a/multiversx_sdk_cli/custom_network_provider.py b/multiversx_sdk_cli/custom_network_provider.py new file mode 100644 index 00000000..3354a495 --- /dev/null +++ b/multiversx_sdk_cli/custom_network_provider.py @@ -0,0 +1,36 @@ +from typing import Any, Dict, Optional, Protocol + +from multiversx_sdk_network_providers import GenericError, ProxyNetworkProvider + +from multiversx_sdk_cli.errors import ProxyError +from multiversx_sdk_cli.interfaces import ISimulateResponse, ITransaction + + +class ITransactionOnNetwork(Protocol): + hash: str + is_completed: Optional[bool] + + def to_dictionary(self) -> Dict[str, Any]: + ... + + +class CustomNetworkProvider: + def __init__(self, url: str) -> None: + self._provider = ProxyNetworkProvider(url) + + def send_transaction(self, transaction: ITransaction) -> str: + try: + hash = self._provider.send_transaction(transaction) + return hash + except GenericError as ge: + url = ge.url + message = ge.data.get("error", "") + data = ge.data.get("data", "") + code = ge.data.get("code", "") + raise ProxyError(message, url, data, code) + + def get_transaction(self, tx_hash: str, with_process_status: Optional[bool] = False) -> ITransactionOnNetwork: + return self._provider.get_transaction(tx_hash, with_process_status) + + def simulate_transaction(self, transaction: ITransaction) -> ISimulateResponse: + return self._provider.simulate_transaction(transaction) diff --git a/multiversx_sdk_cli/transactions.py b/multiversx_sdk_cli/transactions.py index 2265ea01..c9430396 100644 --- a/multiversx_sdk_cli/transactions.py +++ b/multiversx_sdk_cli/transactions.py @@ -2,17 +2,16 @@ import json import logging import time -from typing import Any, Dict, Optional, Protocol, Sequence, TextIO, Tuple +from typing import Any, Dict, Optional, Protocol, TextIO from multiversx_sdk_core import Address, Transaction, TransactionPayload -from multiversx_sdk_network_providers import GenericError from multiversx_sdk_cli import errors from multiversx_sdk_cli.accounts import Account, LedgerAccount from multiversx_sdk_cli.cli_password import (load_guardian_password, load_password) from multiversx_sdk_cli.cosign_transaction import cosign_transaction -from multiversx_sdk_cli.errors import NoWalletProvided, ProxyError +from multiversx_sdk_cli.errors import NoWalletProvided from multiversx_sdk_cli.interfaces import ITransaction from multiversx_sdk_cli.ledger.ledger_functions import do_get_ledger_address @@ -31,9 +30,6 @@ class INetworkProvider(Protocol): def send_transaction(self, transaction: ITransaction) -> str: ... - def send_transactions(self, transactions: Sequence[ITransaction]) -> Tuple[int, Dict[str, str]]: - ... - def get_transaction(self, tx_hash: str, with_process_status: Optional[bool] = False) -> ITransactionOnNetwork: ... @@ -133,15 +129,7 @@ def send_and_wait_for_result(transaction: ITransaction, proxy: INetworkProvider, def _send_transaction_and_wait_for_result(proxy: INetworkProvider, payload: ITransaction, num_seconds_timeout: int = 100) -> ITransactionOnNetwork: AWAIT_TRANSACTION_PERIOD = 5 - try: - tx_hash = proxy.send_transaction(payload) - except GenericError as ge: - url = ge.url - message = ge.data["error"] - data = ge.data["data"] - code = ge.data["code"] - raise ProxyError(message, url, data, code) - + tx_hash = proxy.send_transaction(payload) num_periods_to_wait = int(num_seconds_timeout / AWAIT_TRANSACTION_PERIOD) for _ in range(0, num_periods_to_wait): From b84e31c5fca3748c6a2479f5ae80af97fde5bcdd Mon Sep 17 00:00:00 2001 From: Alexandru Popenta Date: Tue, 20 Feb 2024 10:56:03 +0200 Subject: [PATCH 4/4] add try-finally statement --- multiversx_sdk_cli/cli_transactions.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/multiversx_sdk_cli/cli_transactions.py b/multiversx_sdk_cli/cli_transactions.py index 5c89e389..5e02cac1 100644 --- a/multiversx_sdk_cli/cli_transactions.py +++ b/multiversx_sdk_cli/cli_transactions.py @@ -88,11 +88,12 @@ def send_transaction(args: Any): output = CLIOutputBuilder() proxy = CustomNetworkProvider(args.proxy) - tx_hash = proxy.send_transaction(tx) - output.set_emitted_transaction_hash(tx_hash) - - output = output.set_emitted_transaction(tx).build() - utils.dump_out_json(output, outfile=args.outfile) + try: + tx_hash = proxy.send_transaction(tx) + output.set_emitted_transaction_hash(tx_hash) + finally: + output = output.set_emitted_transaction(tx).build() + utils.dump_out_json(output, outfile=args.outfile) def get_transaction(args: Any):