From 2fe9067595ce14cc7f65ce5f7d2fd6643df50f16 Mon Sep 17 00:00:00 2001 From: Marcin Sobczyk Date: Fri, 24 Oct 2025 10:44:17 +0000 Subject: [PATCH 01/15] Add GetTransaction to schemas used in type checking --- clive/__private/models/schemas.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/clive/__private/models/schemas.py b/clive/__private/models/schemas.py index f13068943b..d3f562b2a1 100644 --- a/clive/__private/models/schemas.py +++ b/clive/__private/models/schemas.py @@ -44,6 +44,7 @@ __all__ = [ # noqa: RUF022 "FindWitnesses", # get API responses (have unnecessary nested property which stores actual model) "GetAccountHistory", + "GetTransaction", # get API responses (have no unnecessary nested properties, just the model itself) "Config", "DynamicGlobalProperties", @@ -159,7 +160,7 @@ __all__ = [ # noqa: RUF022 if TYPE_CHECKING: from schemas._preconfigured_base_model import PreconfiguredBaseModel - from schemas.apis.account_history_api import GetAccountHistory + from schemas.apis.account_history_api import GetAccountHistory, GetTransaction from schemas.apis.database_api import ( FindAccounts, FindProposals, @@ -470,6 +471,7 @@ __getattr__ = lazy_module_factory( ("schemas.apis.transaction_status_api", "FindTransaction", "TransactionStatus"), *aggregate_same_import( "GetAccountHistory", + "GetTransaction", module="schemas.apis.account_history_api", ), ( -- GitLab From b3c9ea759785cb9cd696351bdcdaf119752e0ffe Mon Sep 17 00:00:00 2001 From: Marcin Sobczyk Date: Fri, 24 Oct 2025 09:41:07 +0000 Subject: [PATCH 02/15] Simplify tests for update-witness --- .../checkers/blockchain_checkers.py | 60 +++- .../process/test_process_update_witness.py | 276 ++++-------------- 2 files changed, 116 insertions(+), 220 deletions(-) diff --git a/tests/clive-local-tools/clive_local_tools/checkers/blockchain_checkers.py b/tests/clive-local-tools/clive_local_tools/checkers/blockchain_checkers.py index 0cf2766918..ffae0668ed 100644 --- a/tests/clive-local-tools/clive_local_tools/checkers/blockchain_checkers.py +++ b/tests/clive-local-tools/clive_local_tools/checkers/blockchain_checkers.py @@ -1,17 +1,24 @@ from __future__ import annotations +from decimal import Decimal from typing import TYPE_CHECKING import beekeepy.exceptions as bke import pytest from click.testing import Result +from clive.__private.core.percent_conversions import percent_to_hive_percent +from clive.__private.models.asset import Asset +from clive.__private.models.schemas import HbdExchangeRate from clive_local_tools.helpers import get_transaction_id_from_output if TYPE_CHECKING: + from collections.abc import Callable + from typing import Any + import test_tools as tt - from clive.__private.models.schemas import GetTransaction, OperationBase, OperationUnion + from clive.__private.models.schemas import GetTransaction, OperationBase, OperationUnion, Witness def _ensure_transaction_id(trx_id_or_result: Result | str) -> str: @@ -88,3 +95,54 @@ def assert_operation_type_in_blockchain( f"{transaction}." ) assert not types_to_check, message + + +def assert_witness_property( + property_name: str, property_value: str | int | Decimal | Asset.LiquidT | HbdExchangeRate, witness: Witness +) -> None: + """Assert that given property has expected value in the witness object obtained by api call to blockchain.""" + props = witness.props + + def check(actual: Any, expected: Any) -> None: # noqa: ANN401 + message = ( + f"Witness property '{property_name}' does not have expected value. Expected: {expected}, actual: {actual}" + ) + assert actual == expected, message + + # Define specialized check functions + def check_account_creation_fee() -> None: + assert props.account_creation_fee is not None, "account_creation_fee is None (see issue #46)" + check(props.account_creation_fee, property_value) + + def check_hbd_exchange_rate() -> None: + if isinstance(property_value, HbdExchangeRate): + hbd_exchange_rate = property_value + elif isinstance(property_value, Asset.LiquidT): + hbd_exchange_rate = HbdExchangeRate(base=property_value, quote=Asset.hive(1)) + else: + pytest.fail(f"Unknown property_value type: {type(property_value)}") + check(witness.hbd_exchange_rate.base, hbd_exchange_rate.base) + check(witness.hbd_exchange_rate.quote, hbd_exchange_rate.quote) + + def check_hbd_interest_rate() -> None: + assert isinstance(property_value, Decimal), "For hbd_interest_rate, property_value must be Decimal." + expected = percent_to_hive_percent(property_value) + check(props.hbd_interest_rate, expected) + + # Map of property_name → checker function + check_map: dict[str, Callable[[], None]] = { + "account_creation_fee": check_account_creation_fee, + "account_subsidy_budget": lambda: check(props.account_subsidy_budget, property_value), + "account_subsidy_decay": lambda: check(props.account_subsidy_decay, property_value), + "hbd_exchange_rate": check_hbd_exchange_rate, + "hbd_interest_rate": check_hbd_interest_rate, + "maximum_block_size": lambda: check(props.maximum_block_size, property_value), + "new_signing_key": lambda: check(witness.signing_key, property_value), + "url": lambda: check(witness.url, property_value), + } + + # Dispatch or fail if unknown property + if property_name not in check_map: + pytest.fail(f"Unknown property: '{property_name}'.") + + check_map[property_name]() diff --git a/tests/functional/cli/process/test_process_update_witness.py b/tests/functional/cli/process/test_process_update_witness.py index 195fdd5a2e..72b40c7cf1 100644 --- a/tests/functional/cli/process/test_process_update_witness.py +++ b/tests/functional/cli/process/test_process_update_witness.py @@ -1,243 +1,74 @@ from __future__ import annotations from decimal import Decimal -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Final import pytest -import test_tools as tt from clive.__private.cli.commands.process.process_update_witness import RequiresWitnessSetPropertiesOperationError -from clive.__private.core.keys.keys import PrivateKey +from clive.__private.core.formatters.case import dasherize +from clive.__private.core.keys.keys import PrivateKey, PublicKey from clive.__private.core.percent_conversions import percent_to_hive_percent +from clive.__private.models.asset import Asset from clive.__private.models.schemas import ( FeedPublishOperation, OperationBase, WitnessSetPropertiesOperation, WitnessUpdateOperation, ) -from clive_local_tools.checkers.blockchain_checkers import assert_operation_type_in_blockchain +from clive_local_tools.checkers.blockchain_checkers import assert_operation_type_in_blockchain, assert_witness_property from clive_local_tools.cli.exceptions import CLITestCommandError from clive_local_tools.helpers import get_formatted_error_message if TYPE_CHECKING: - from clive_local_tools.cli.cli_tester import CLITester - - -@pytest.mark.parametrize("use_witness_key", [True, False]) -async def test_account_creation_fee( - node: tt.RawNode, - cli_tester_unlocked_with_witness_profile: CLITester, - use_witness_key: bool, # noqa: FBT001 -) -> None: - # ARRANGE - operations: list[type[OperationBase]] = ( - [WitnessUpdateOperation] if not use_witness_key else [WitnessSetPropertiesOperation] - ) - amount = tt.Asset.Hive(3.456) - owner = cli_tester_unlocked_with_witness_profile.world.profile.accounts.working.name - - # ACT - result = cli_tester_unlocked_with_witness_profile.process_update_witness( - use_witness_key=use_witness_key, account_creation_fee=amount - ) - - # ASSERT - assert_operation_type_in_blockchain(node, result, *operations) - witness = ( - await cli_tester_unlocked_with_witness_profile.world.commands.find_witness(witness_name=owner) - ).result_or_raise - assert witness.props.account_creation_fee is not None, "Account creation fee should be set during witness creation" - assert amount == witness.props.account_creation_fee, ( - f"Witness '{owner}' account creation fee should change after command witness-update," - f" expected: `{amount.pretty_amount()}`, actual: `{witness.props.account_creation_fee.pretty_amount()}`" - ) - - -@pytest.mark.parametrize("use_witness_key", [True, False]) -async def test_maximum_block_size( - node: tt.RawNode, - cli_tester_unlocked_with_witness_profile: CLITester, - use_witness_key: bool, # noqa: FBT001 -) -> None: - # ARRANGE - operations: list[type[OperationBase]] = ( - [WitnessUpdateOperation] if not use_witness_key else [WitnessSetPropertiesOperation] - ) - maximum_block_size = 1_048_576 - owner = cli_tester_unlocked_with_witness_profile.world.profile.accounts.working.name - - # ACT - result = cli_tester_unlocked_with_witness_profile.process_update_witness( - use_witness_key=use_witness_key, maximum_block_size=maximum_block_size - ) - - # ASSERT - assert_operation_type_in_blockchain(node, result, *operations) - witness = ( - await cli_tester_unlocked_with_witness_profile.world.commands.find_witness(witness_name=owner) - ).result_or_raise - assert maximum_block_size == witness.props.maximum_block_size, ( - f"Witness '{owner}' maximum block size should change after command witness-update," - f" expected: `{maximum_block_size}`, actual: `{witness.props.maximum_block_size}`" - ) - - -@pytest.mark.parametrize("use_witness_key", [True, False]) -async def test_hbd_interest_rate( - node: tt.RawNode, - cli_tester_unlocked_with_witness_profile: CLITester, - use_witness_key: bool, # noqa: FBT001 -) -> None: - # ARRANGE - operations: list[type[OperationBase]] = ( - [WitnessUpdateOperation] if not use_witness_key else [WitnessSetPropertiesOperation] - ) - hbd_interest_rate = Decimal("54.32") - owner = cli_tester_unlocked_with_witness_profile.world.profile.accounts.working.name - - # ACT - result = cli_tester_unlocked_with_witness_profile.process_update_witness( - use_witness_key=use_witness_key, hbd_interest_rate=hbd_interest_rate - ) - - # ASSERT - assert_operation_type_in_blockchain(node, result, *operations) - witness = ( - await cli_tester_unlocked_with_witness_profile.world.commands.find_witness(witness_name=owner) - ).result_or_raise - assert percent_to_hive_percent(hbd_interest_rate) == witness.props.hbd_interest_rate, ( - f"Witness '{owner}' hbd interest rate should change after command witness-update," - f" expected: `{percent_to_hive_percent(hbd_interest_rate)}`, actual: `{witness.props.hbd_interest_rate}`" - ) - - -@pytest.mark.parametrize("use_witness_key", [True, False]) -async def test_new_signing_key( - node: tt.RawNode, - cli_tester_unlocked_with_witness_profile: CLITester, - use_witness_key: bool, # noqa: FBT001 -) -> None: - # ARRANGE - operations: list[type[OperationBase]] = ( - [WitnessUpdateOperation] if not use_witness_key else [WitnessSetPropertiesOperation] - ) - new_signing_key = PrivateKey.create().calculate_public_key().value - owner = cli_tester_unlocked_with_witness_profile.world.profile.accounts.working.name - - # ACT - result = cli_tester_unlocked_with_witness_profile.process_update_witness( - use_witness_key=use_witness_key, new_signing_key=new_signing_key - ) - - # ASSERT - assert_operation_type_in_blockchain(node, result, *operations) - witness = ( - await cli_tester_unlocked_with_witness_profile.world.commands.find_witness(witness_name=owner) - ).result_or_raise - assert new_signing_key == witness.signing_key, ( - f"Witness '{owner}' signing key should change after command witness-update," - f" expected: `{new_signing_key}`, actual: `{witness.signing_key}`" - ) - - -@pytest.mark.parametrize("use_witness_key", [True, False]) -async def test_hbd_exchange_rate( - node: tt.RawNode, - cli_tester_unlocked_with_witness_profile: CLITester, - use_witness_key: bool, # noqa: FBT001 -) -> None: - # ARRANGE - operations: list[type[OperationBase]] = ( - [FeedPublishOperation] if not use_witness_key else [WitnessSetPropertiesOperation] - ) - hbd_exchange_rate = tt.Asset.Hbd(0.234) - owner = cli_tester_unlocked_with_witness_profile.world.profile.accounts.working.name - - # ACT - result = cli_tester_unlocked_with_witness_profile.process_update_witness( - use_witness_key=use_witness_key, hbd_exchange_rate=hbd_exchange_rate - ) - - # ASSERT - assert_operation_type_in_blockchain(node, result, *operations) - witness = ( - await cli_tester_unlocked_with_witness_profile.world.commands.find_witness(witness_name=owner) - ).result_or_raise - assert witness.hbd_exchange_rate.base == hbd_exchange_rate, ( - f"Witness '{owner}' hbd exchange rate should change after command witness-update," - f" expected: `{hbd_exchange_rate}`, actual: `{witness.hbd_exchange_rate.base}`" - ) - assert witness.hbd_exchange_rate.quote == tt.Asset.Hive(1), "hbd exchange rate should be given as price of 1 hive" + import test_tools as tt + from clive_local_tools.cli.cli_tester import CLITester -@pytest.mark.parametrize("use_witness_key", [True, False]) -async def test_url( +NEW_SIGNING_KEY: Final[PublicKey] = PublicKey( + value="STM6aacAN2UoV723iH3Ko1tJZVbNjtPeJoP7AxDu9XPczafp4UQWc" +) # random key + + +@pytest.mark.parametrize( + ("use_witness_key", "property_name", "property_value", "operation_type"), + [ + (True, "account_creation_fee", Asset.hive("3.456"), WitnessSetPropertiesOperation), + (False, "account_creation_fee", Asset.hive("3.456"), WitnessUpdateOperation), + (True, "maximum_block_size", 1_048_576, WitnessSetPropertiesOperation), + (False, "maximum_block_size", 1_048_576, WitnessUpdateOperation), + (True, "hbd_interest_rate", Decimal("54.32"), WitnessSetPropertiesOperation), + (False, "hbd_interest_rate", Decimal("54.32"), WitnessUpdateOperation), + (True, "new_signing_key", NEW_SIGNING_KEY.value, WitnessSetPropertiesOperation), + (False, "new_signing_key", NEW_SIGNING_KEY.value, WitnessUpdateOperation), + (True, "hbd_exchange_rate", Asset.hbd("0.234"), WitnessSetPropertiesOperation), + (False, "hbd_exchange_rate", Asset.hbd("0.234"), FeedPublishOperation), + (True, "url", "example.com", WitnessSetPropertiesOperation), + (False, "url", "example.com", WitnessUpdateOperation), + (True, "account_subsidy_budget", 1234, WitnessSetPropertiesOperation), + (True, "account_subsidy_decay", 5678, WitnessSetPropertiesOperation), + ], +) +async def test_setting_witness_property( # noqa: PLR0913 node: tt.RawNode, cli_tester_unlocked_with_witness_profile: CLITester, use_witness_key: bool, # noqa: FBT001 + property_name: str, + property_value: str | int | Decimal | Asset.LiquidT, + operation_type: type[OperationBase], ) -> None: # ARRANGE - operations: list[type[OperationBase]] = ( - [WitnessUpdateOperation] if not use_witness_key else [WitnessSetPropertiesOperation] - ) - url = "example.com" - owner = cli_tester_unlocked_with_witness_profile.world.profile.accounts.working.name - - # ACT - result = cli_tester_unlocked_with_witness_profile.process_update_witness(use_witness_key=use_witness_key, url=url) - - # ASSERT - assert_operation_type_in_blockchain(node, result, *operations) - witness = ( - await cli_tester_unlocked_with_witness_profile.world.commands.find_witness(witness_name=owner) - ).result_or_raise - assert url == witness.url, ( - f"Witness '{owner}' url change after command witness-update, expected: `{url}`, actual: `{witness.url}`" - ) - - -async def test_account_subsidy_budget(node: tt.RawNode, cli_tester_unlocked_with_witness_profile: CLITester) -> None: - # ARRANGE - operations: list[type[OperationBase]] = [WitnessSetPropertiesOperation] - account_subsidy_budget = 1234 - owner = cli_tester_unlocked_with_witness_profile.world.profile.accounts.working.name - - # ACT - result = cli_tester_unlocked_with_witness_profile.process_update_witness( - account_subsidy_budget=account_subsidy_budget - ) - - # ASSERT - assert_operation_type_in_blockchain(node, result, *operations) - witness = ( - await cli_tester_unlocked_with_witness_profile.world.commands.find_witness(witness_name=owner) - ).result_or_raise - assert account_subsidy_budget == witness.props.account_subsidy_budget, ( - f"Witness '{owner}' account subsidy budget change after command witness-update," - f" expected: `{account_subsidy_budget}`, actual: `{witness.props.account_subsidy_budget}`" - ) - - -async def test_account_subsidy_decay(node: tt.RawNode, cli_tester_unlocked_with_witness_profile: CLITester) -> None: - # ARRANGE - operations: list[type[OperationBase]] = [WitnessSetPropertiesOperation] - account_subsidy_decay = 5678 - owner = cli_tester_unlocked_with_witness_profile.world.profile.accounts.working.name + cli_tester = cli_tester_unlocked_with_witness_profile + owner = cli_tester.world.profile.accounts.working.name + properties = {property_name: property_value} # ACT - result = cli_tester_unlocked_with_witness_profile.process_update_witness( - account_subsidy_decay=account_subsidy_decay - ) + result = cli_tester.process_update_witness(use_witness_key=use_witness_key, **properties) # type: ignore[arg-type] # ASSERT - assert_operation_type_in_blockchain(node, result, *operations) - witness = ( - await cli_tester_unlocked_with_witness_profile.world.commands.find_witness(witness_name=owner) - ).result_or_raise - assert account_subsidy_decay == witness.props.account_subsidy_decay, ( - f"Witness '{owner}' account subsidy decay should change after command witness-update," - f" expected: `{account_subsidy_decay}`, actual: `{witness.props.account_subsidy_decay}`" - ) + assert_operation_type_in_blockchain(node, result, operation_type) + witness = (await cli_tester.world.commands.find_witness(witness_name=owner)).result_or_raise + assert_witness_property(property_name, property_value, witness) async def test_two_operations_in_transaction( @@ -245,7 +76,7 @@ async def test_two_operations_in_transaction( ) -> None: # ARRANGE operations: list[type[OperationBase]] = [WitnessUpdateOperation, FeedPublishOperation] - hbd_exchange_rate = tt.Asset.Hbd(0.3456) + hbd_exchange_rate = Asset.hbd("0.3456") hbd_interest_rate = Decimal("65.43") owner = cli_tester_unlocked_with_witness_profile.world.profile.accounts.working.name @@ -263,7 +94,7 @@ async def test_two_operations_in_transaction( f"Witness '{owner}' hbd exchange rate should change after command witness-update," f" expected: `{hbd_exchange_rate}`, actual: `{witness.hbd_exchange_rate.base}`" ) - assert witness.hbd_exchange_rate.quote == tt.Asset.Hive(1), "hbd exchange rate should be given as price of 1 hive" + assert witness.hbd_exchange_rate.quote == Asset.hive(1), "hbd exchange rate should be given as price of 1 hive" assert percent_to_hive_percent(hbd_interest_rate) == witness.props.hbd_interest_rate, ( f"Witness '{owner}' hbd interest rate should change after command witness-update," f" expected: `{percent_to_hive_percent(hbd_interest_rate)}`, actual: `{witness.props.hbd_interest_rate}`" @@ -298,17 +129,24 @@ async def test_using_updated_witness_key(node: tt.RawNode, cli_tester_unlocked_w ) +@pytest.mark.parametrize( + ("property_name", "property_value"), + [ + ("account_subsidy_budget", 1234), + ("account_subsidy_decay", 5678), + ], +) async def test_negative_account_subsidy_with_active_authority( - cli_tester_unlocked_with_witness_profile: CLITester, + cli_tester_unlocked_with_witness_profile: CLITester, property_name: str, property_value: int ) -> None: + """Test exception is thrown when attempting to modify account-subsidy properties and sign it with active key.""" # ARRANGE - account_subsidy_budget = 2345 + cli_tester = cli_tester_unlocked_with_witness_profile + properties = {property_name: property_value} # ACT & ASSERT with pytest.raises( CLITestCommandError, - match=get_formatted_error_message(RequiresWitnessSetPropertiesOperationError("account-subsidy-budget")), + match=get_formatted_error_message(RequiresWitnessSetPropertiesOperationError(dasherize(property_name))), ): - cli_tester_unlocked_with_witness_profile.process_update_witness( - use_witness_key=False, account_subsidy_budget=account_subsidy_budget - ) + cli_tester.process_update_witness(use_witness_key=False, **properties) # type: ignore[arg-type] -- GitLab From 3edf51d59096731561760eccfff641f50f988a2f Mon Sep 17 00:00:00 2001 From: Marcin Sobczyk Date: Mon, 27 Oct 2025 12:43:38 +0000 Subject: [PATCH 03/15] Fix generate block_log script after wax asset interface change --- .../testnet_block_log/generate_block_log.py | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/tests/clive-local-tools/clive_local_tools/testnet_block_log/generate_block_log.py b/tests/clive-local-tools/clive_local_tools/testnet_block_log/generate_block_log.py index 93637cb782..fc76c6aa88 100644 --- a/tests/clive-local-tools/clive_local_tools/testnet_block_log/generate_block_log.py +++ b/tests/clive-local-tools/clive_local_tools/testnet_block_log/generate_block_log.py @@ -62,9 +62,9 @@ def create_witnesses(wallet: tt.Wallet) -> None: with wallet.in_single_transaction(): for witness in WITNESSES: - wallet.api.transfer("initminer", witness.name, tt.Asset.Test(10_000).as_nai(), "memo") - wallet.api.transfer_to_vesting("initminer", witness.name, tt.Asset.Test(10_000).as_nai()) - wallet.api.transfer("initminer", witness.name, tt.Asset.Tbd(10_000).as_nai(), memo="memo") + wallet.api.transfer("initminer", witness.name, tt.Asset.Test(10_000), "memo") + wallet.api.transfer_to_vesting("initminer", witness.name, tt.Asset.Test(10_000)) + wallet.api.transfer("initminer", witness.name, tt.Asset.Tbd(10_000), memo="memo") with wallet.in_single_transaction(): for witness in WITNESSES: @@ -73,7 +73,7 @@ def create_witnesses(wallet: tt.Wallet) -> None: "https://" + witness.name, witness.public_key, { - "account_creation_fee": tt.Asset.Test(3).as_nai(), + "account_creation_fee": tt.Asset.Test(3), "maximum_block_size": 65536, "hbd_interest_rate": 0, }, @@ -100,7 +100,7 @@ def create_proposals(wallet: tt.Wallet) -> None: witness.name, tt.Time.from_now(months=start_month, minutes=1), tt.Time.from_now(months=end_month, minutes=1), - tt.Asset.Tbd(daily_pay).as_nai(), + tt.Asset.Tbd(daily_pay), permlink, f"{permlink}", ) @@ -110,21 +110,21 @@ def create_working_accounts(wallet: tt.Wallet) -> None: tt.logger.info("Creating working accounts...") wallet.create_account( WORKING_ACCOUNT_DATA.account.name, - hives=WORKING_ACCOUNT_DATA.hives_liquid.as_nai(), # type: ignore[arg-type] # test-tools dooesn't convert to hf26 - vests=WORKING_ACCOUNT_DATA.vests.as_nai(), # type: ignore[arg-type] # test-tools dooesn't convert to hf26 - hbds=WORKING_ACCOUNT_DATA.hbds_liquid.as_nai(), # type: ignore[arg-type] # test-tools dooesn't convert to hf26 + hives=WORKING_ACCOUNT_DATA.hives_liquid, + vests=WORKING_ACCOUNT_DATA.vests, + hbds=WORKING_ACCOUNT_DATA.hbds_liquid, ) wallet.create_account( ALT_WORKING_ACCOUNT1_DATA.account.name, - hives=ALT_WORKING_ACCOUNT1_DATA.hives_liquid.as_nai(), # type: ignore[arg-type] # test-tools dooesn't convert to hf26 - vests=ALT_WORKING_ACCOUNT1_DATA.vests.as_nai(), # type: ignore[arg-type] # test-tools dooesn't convert to hf26 - hbds=ALT_WORKING_ACCOUNT1_DATA.hbds_liquid.as_nai(), # type: ignore[arg-type] # test-tools dooesn't convert to hf26 + hives=ALT_WORKING_ACCOUNT1_DATA.hives_liquid, + vests=ALT_WORKING_ACCOUNT1_DATA.vests, + hbds=ALT_WORKING_ACCOUNT1_DATA.hbds_liquid, ) wallet.create_account( ALT_WORKING_ACCOUNT2_DATA.account.name, - hives=ALT_WORKING_ACCOUNT2_DATA.hives_liquid.as_nai(), # type: ignore[arg-type] # test-tools dooesn't convert to hf26 - vests=ALT_WORKING_ACCOUNT2_DATA.vests.as_nai(), # type: ignore[arg-type] # test-tools dooesn't convert to hf26 - hbds=ALT_WORKING_ACCOUNT2_DATA.hbds_liquid.as_nai(), # type: ignore[arg-type] # test-tools dooesn't convert to hf26 + hives=ALT_WORKING_ACCOUNT2_DATA.hives_liquid, + vests=ALT_WORKING_ACCOUNT2_DATA.vests, + hbds=ALT_WORKING_ACCOUNT2_DATA.hbds_liquid, ) @@ -136,14 +136,14 @@ def prepare_savings(wallet: tt.Wallet) -> None: wallet.api.transfer_to_savings( CREATOR_ACCOUNT.name, data.account.name, - data.hives_savings.as_nai(), + data.hives_savings, "Supplying HIVE savings", ) if data.hbds_savings > 0: wallet.api.transfer_to_savings( CREATOR_ACCOUNT.name, data.account.name, - data.hbds_savings.as_nai(), + data.hbds_savings, "Supplying HBD savings", ) if data.hives_savings_withdrawal > 0: @@ -151,7 +151,7 @@ def prepare_savings(wallet: tt.Wallet) -> None: data.account.name, 0, CREATOR_ACCOUNT.name, - data.hives_savings_withdrawal.as_nai(), + data.hives_savings_withdrawal, "Withdrawing HIVE savings", ) if data.hbds_savings_withdrawal > 0: @@ -159,7 +159,7 @@ def prepare_savings(wallet: tt.Wallet) -> None: data.account.name, 0, CREATOR_ACCOUNT.name, - data.hbds_savings_withdrawal.as_nai(), + data.hbds_savings_withdrawal, "Withdrawing HBD savings", ) @@ -169,9 +169,9 @@ def create_watched_accounts(wallet: tt.Wallet) -> None: for data in WATCHED_ACCOUNTS_DATA: wallet.create_account( data.account.name, - hives=data.hives_liquid.as_nai(), # type: ignore[arg-type] # test-tools dooesn't convert to hf26 - vests=data.vests.as_nai(), # type: ignore[arg-type] # test-tools dooesn't convert to hf26 - hbds=data.hbds_liquid.as_nai(), # type: ignore[arg-type] # test-tools dooesn't convert to hf26 + hives=data.hives_liquid, + vests=data.vests, + hbds=data.hbds_liquid, ) -- GitLab From 475edf21223224cf76378f5350608c7384194088 Mon Sep 17 00:00:00 2001 From: Marcin Sobczyk Date: Mon, 27 Oct 2025 13:25:02 +0000 Subject: [PATCH 04/15] Allow for str | PublicKey in WitnessSetProperties wrapper --- clive/__private/core/iwax.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/clive/__private/core/iwax.py b/clive/__private/core/iwax.py index f007319414..e44096b295 100644 --- a/clive/__private/core/iwax.py +++ b/clive/__private/core/iwax.py @@ -282,8 +282,8 @@ class WitnessSetPropertiesWrapper: def create( # noqa: PLR0913 cls, owner: str, - key: PublicKey, - new_signing_key: PublicKey | None = None, + key: str | PublicKey, + new_signing_key: str | PublicKey | None = None, account_creation_fee: Asset.Hive | None = None, url: str | None = None, hbd_exchange_rate: Asset.Hbd | None = None, @@ -292,18 +292,22 @@ class WitnessSetPropertiesWrapper: account_subsidy_budget: int | None = None, account_subsidy_decay: int | None = None, ) -> Self: + from clive.__private.core.keys import PublicKey # noqa: PLC0415 from clive.__private.models.asset import Asset # noqa: PLC0415 from wax.complex_operations.witness_set_properties import ( # noqa: PLC0415 WitnessSetProperties, WitnessSetPropertiesData, ) + def key_string(input_key: str | PublicKey) -> str: + return input_key.value if isinstance(input_key, PublicKey) else input_key + return cls( WitnessSetProperties( data=WitnessSetPropertiesData( owner=AccountName(owner), - witness_signing_key=key.value, - new_signing_key=new_signing_key.value if new_signing_key is not None else None, + witness_signing_key=key_string(key), + new_signing_key=key_string(new_signing_key) if new_signing_key is not None else None, account_creation_fee=account_creation_fee.as_serialized_nai() if account_creation_fee is not None else None, -- GitLab From 15d0acd6a1a5280c47ec92127deb9a546fc923bb Mon Sep 17 00:00:00 2001 From: Marcin Sobczyk Date: Mon, 27 Oct 2025 13:50:17 +0000 Subject: [PATCH 05/15] Refactor check for witness set properties and witness update operation requirement cli command update-witness --- .../cli/commands/abc/external_cli_command.py | 4 ++ .../process/process_update_witness.py | 54 ++++++++++--------- .../process/test_process_update_witness.py | 4 +- 3 files changed, 34 insertions(+), 28 deletions(-) diff --git a/clive/__private/cli/commands/abc/external_cli_command.py b/clive/__private/cli/commands/abc/external_cli_command.py index 98f0a8d6a5..023d1fcaf3 100644 --- a/clive/__private/cli/commands/abc/external_cli_command.py +++ b/clive/__private/cli/commands/abc/external_cli_command.py @@ -72,6 +72,10 @@ class ExternalCLICommand(ABC): sanitized = {k: v for k, v in kwargs.items() if k in inspect.signature(cls).parameters} return cls(**sanitized) + @classmethod + def is_option_given(cls, value: object) -> bool: + return value is not None + def read_interactive(self, prompt: str, *, hide_input: bool = True) -> str: """ Read input in interactive mode. diff --git a/clive/__private/cli/commands/process/process_update_witness.py b/clive/__private/cli/commands/process/process_update_witness.py index a6d1ea3d7f..6096565a5e 100644 --- a/clive/__private/cli/commands/process/process_update_witness.py +++ b/clive/__private/cli/commands/process/process_update_witness.py @@ -26,7 +26,7 @@ if TYPE_CHECKING: from clive.__private.cli.types import ComposeTransaction -class RequiresWitnessSetPropertiesOperationError(CLIPrettyError): +class CLIRequiresWitnessSetPropertiesOperationError(CLIPrettyError): """ Raised when operation must be signed with active authority but signing with witness key is requested. @@ -103,42 +103,44 @@ class ProcessUpdateWitness(OperationCommand): ) def _validate_requirements_for_witness_set_propertues_operation(self) -> None: - if self.use_active_authority and self.account_subsidy_budget is not None: - raise RequiresWitnessSetPropertiesOperationError("account-subsidy-budget") - if self.use_active_authority and self.account_subsidy_decay is not None: - raise RequiresWitnessSetPropertiesOperationError("account-subsidy-decay") + if self.use_active_authority and self.is_option_given(self.account_subsidy_budget): + raise CLIRequiresWitnessSetPropertiesOperationError("account-subsidy-budget") + if self.use_active_authority and self.is_option_given(self.account_subsidy_decay): + raise CLIRequiresWitnessSetPropertiesOperationError("account-subsidy-decay") @property def _needs_feed_publish_operation(self) -> bool: - return self.use_active_authority and self.hbd_exchange_rate is not None + return self.use_active_authority and self.is_option_given(self.hbd_exchange_rate) @property def _needs_witness_set_properties_operation(self) -> bool: - are_witness_set_properties_options_required: bool = ( - self.account_creation_fee is not None - or self.maximum_block_size is not None - or self.hbd_interest_rate is not None - or self.new_signing_key is not None - or self.url is not None - or self.hbd_exchange_rate is not None - or self.account_subsidy_budget is not None - or self.account_subsidy_decay is not None - ) - return self.use_witness_key and are_witness_set_properties_options_required + properties = [ + self.account_creation_fee, + self.maximum_block_size, + self.hbd_interest_rate, + self.new_signing_key, + self.url, + self.hbd_exchange_rate, + self.account_subsidy_budget, + self.account_subsidy_decay, + ] + is_witness_set_properties_option_given = any(self.is_option_given(prop) for prop in properties) + return self.use_witness_key and is_witness_set_properties_option_given @property def _needs_witness_update_operation(self) -> bool: - are_witness_update_options_required: bool = ( - self.account_creation_fee is not None - or self.maximum_block_size is not None - or self.hbd_interest_rate is not None - or self.new_signing_key is not None - or self.url is not None - ) - return self.use_active_authority and are_witness_update_options_required + properties = [ + self.account_creation_fee, + self.maximum_block_size, + self.hbd_interest_rate, + self.new_signing_key, + self.url, + ] + is_witness_update_option_given = any(self.is_option_given(prop) for prop in properties) + return self.use_active_authority and is_witness_update_option_given def _create_feed_publish_operation(self) -> FeedPublishOperation: - assert self.hbd_exchange_rate is not None, ( + assert self.hbd_exchange_rate is not None, ( # we already checked this option was given "Feed publish should be created only if command requires changing hbd_exchange_rate" ) return FeedPublishOperation( diff --git a/tests/functional/cli/process/test_process_update_witness.py b/tests/functional/cli/process/test_process_update_witness.py index 72b40c7cf1..c16ce91fca 100644 --- a/tests/functional/cli/process/test_process_update_witness.py +++ b/tests/functional/cli/process/test_process_update_witness.py @@ -5,7 +5,7 @@ from typing import TYPE_CHECKING, Final import pytest -from clive.__private.cli.commands.process.process_update_witness import RequiresWitnessSetPropertiesOperationError +from clive.__private.cli.commands.process.process_update_witness import CLIRequiresWitnessSetPropertiesOperationError from clive.__private.core.formatters.case import dasherize from clive.__private.core.keys.keys import PrivateKey, PublicKey from clive.__private.core.percent_conversions import percent_to_hive_percent @@ -147,6 +147,6 @@ async def test_negative_account_subsidy_with_active_authority( # ACT & ASSERT with pytest.raises( CLITestCommandError, - match=get_formatted_error_message(RequiresWitnessSetPropertiesOperationError(dasherize(property_name))), + match=get_formatted_error_message(CLIRequiresWitnessSetPropertiesOperationError(dasherize(property_name))), ): cli_tester.process_update_witness(use_witness_key=False, **properties) # type: ignore[arg-type] -- GitLab From 58e76b4650515e47d2537142785719d6d8ca8d62 Mon Sep 17 00:00:00 2001 From: Marcin Sobczyk Date: Wed, 29 Oct 2025 13:31:04 +0000 Subject: [PATCH 06/15] Pass single operation to assert_operation_type_in_blockchain --- tests/functional/cli/process/test_process_update_witness.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/functional/cli/process/test_process_update_witness.py b/tests/functional/cli/process/test_process_update_witness.py index c16ce91fca..6cc79f9661 100644 --- a/tests/functional/cli/process/test_process_update_witness.py +++ b/tests/functional/cli/process/test_process_update_witness.py @@ -103,7 +103,7 @@ async def test_two_operations_in_transaction( async def test_using_updated_witness_key(node: tt.RawNode, cli_tester_unlocked_with_witness_profile: CLITester) -> None: # ARRANGE - operations: list[type[OperationBase]] = [WitnessSetPropertiesOperation] + operation = WitnessSetPropertiesOperation account_subsidy_decay = 6543 alias = "updated_signing_key" new_private_signing_key = PrivateKey.create() @@ -119,7 +119,7 @@ async def test_using_updated_witness_key(node: tt.RawNode, cli_tester_unlocked_w ) # ASSERT - assert_operation_type_in_blockchain(node, result, *operations) + assert_operation_type_in_blockchain(node, result, operation) witness = ( await cli_tester_unlocked_with_witness_profile.world.commands.find_witness(witness_name=owner) ).result_or_raise -- GitLab From 689e80fbe96b7fc6f4e8965bd0d7b41be37f435c Mon Sep 17 00:00:00 2001 From: Marcin Sobczyk Date: Wed, 29 Oct 2025 13:33:33 +0000 Subject: [PATCH 07/15] Simplify fixture name in tests for update witness --- .../process/test_process_update_witness.py | 36 ++++++++----------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/tests/functional/cli/process/test_process_update_witness.py b/tests/functional/cli/process/test_process_update_witness.py index 6cc79f9661..960dfd7ca7 100644 --- a/tests/functional/cli/process/test_process_update_witness.py +++ b/tests/functional/cli/process/test_process_update_witness.py @@ -59,7 +59,7 @@ async def test_setting_witness_property( # noqa: PLR0913 ) -> None: # ARRANGE cli_tester = cli_tester_unlocked_with_witness_profile - owner = cli_tester.world.profile.accounts.working.name + witness_name = cli_tester.world.profile.accounts.working.name properties = {property_name: property_value} # ACT @@ -67,7 +67,7 @@ async def test_setting_witness_property( # noqa: PLR0913 # ASSERT assert_operation_type_in_blockchain(node, result, operation_type) - witness = (await cli_tester.world.commands.find_witness(witness_name=owner)).result_or_raise + witness = (await cli_tester.world.commands.find_witness(witness_name=witness_name)).result_or_raise assert_witness_property(property_name, property_value, witness) @@ -75,56 +75,50 @@ async def test_two_operations_in_transaction( node: tt.RawNode, cli_tester_unlocked_with_witness_profile: CLITester ) -> None: # ARRANGE + cli_tester = cli_tester_unlocked_with_witness_profile operations: list[type[OperationBase]] = [WitnessUpdateOperation, FeedPublishOperation] hbd_exchange_rate = Asset.hbd("0.3456") hbd_interest_rate = Decimal("65.43") - owner = cli_tester_unlocked_with_witness_profile.world.profile.accounts.working.name + witness_name = cli_tester.world.profile.accounts.working.name # ACT - result = cli_tester_unlocked_with_witness_profile.process_update_witness( + result = cli_tester.process_update_witness( use_witness_key=False, hbd_exchange_rate=hbd_exchange_rate, hbd_interest_rate=hbd_interest_rate ) # ASSERT assert_operation_type_in_blockchain(node, result, *operations) - witness = ( - await cli_tester_unlocked_with_witness_profile.world.commands.find_witness(witness_name=owner) - ).result_or_raise + witness = (await cli_tester.world.commands.find_witness(witness_name=witness_name)).result_or_raise assert witness.hbd_exchange_rate.base == hbd_exchange_rate, ( - f"Witness '{owner}' hbd exchange rate should change after command witness-update," + f"Witness '{witness_name}' hbd exchange rate should change after command witness-update," f" expected: `{hbd_exchange_rate}`, actual: `{witness.hbd_exchange_rate.base}`" ) assert witness.hbd_exchange_rate.quote == Asset.hive(1), "hbd exchange rate should be given as price of 1 hive" assert percent_to_hive_percent(hbd_interest_rate) == witness.props.hbd_interest_rate, ( - f"Witness '{owner}' hbd interest rate should change after command witness-update," + f"Witness '{witness_name}' hbd interest rate should change after command witness-update," f" expected: `{percent_to_hive_percent(hbd_interest_rate)}`, actual: `{witness.props.hbd_interest_rate}`" ) async def test_using_updated_witness_key(node: tt.RawNode, cli_tester_unlocked_with_witness_profile: CLITester) -> None: # ARRANGE + cli_tester = cli_tester_unlocked_with_witness_profile operation = WitnessSetPropertiesOperation account_subsidy_decay = 6543 alias = "updated_signing_key" new_private_signing_key = PrivateKey.create() - cli_tester_unlocked_with_witness_profile.process_update_witness( - new_signing_key=new_private_signing_key.calculate_public_key().value - ) - cli_tester_unlocked_with_witness_profile.configure_key_add(key=new_private_signing_key.value, alias=alias) - owner = cli_tester_unlocked_with_witness_profile.world.profile.accounts.working.name + cli_tester.process_update_witness(new_signing_key=new_private_signing_key.calculate_public_key().value) + cli_tester.configure_key_add(key=new_private_signing_key.value, alias=alias) + witness_name = cli_tester.world.profile.accounts.working.name # ACT - result = cli_tester_unlocked_with_witness_profile.process_update_witness( - account_subsidy_decay=account_subsidy_decay, sign_with=alias - ) + result = cli_tester.process_update_witness(account_subsidy_decay=account_subsidy_decay, sign_with=alias) # ASSERT assert_operation_type_in_blockchain(node, result, operation) - witness = ( - await cli_tester_unlocked_with_witness_profile.world.commands.find_witness(witness_name=owner) - ).result_or_raise + witness = (await cli_tester.world.commands.find_witness(witness_name=witness_name)).result_or_raise assert account_subsidy_decay == witness.props.account_subsidy_decay, ( - f"Witness '{owner}' account subsidy decay should change after command witness-update," + f"Witness '{witness_name}' account subsidy decay should change after command witness-update," f" expected: `{account_subsidy_decay}`, actual: `{witness.props.account_subsidy_decay}`" ) -- GitLab From 52006274af7d9f41e538072470bde20a19e2c71e Mon Sep 17 00:00:00 2001 From: Marcin Sobczyk Date: Wed, 12 Nov 2025 14:55:37 +0000 Subject: [PATCH 08/15] Validate all operations with wax when transaction is built --- clive/__private/core/commands/build_transaction.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clive/__private/core/commands/build_transaction.py b/clive/__private/core/commands/build_transaction.py index 7141db45c5..c71d95cfc1 100644 --- a/clive/__private/core/commands/build_transaction.py +++ b/clive/__private/core/commands/build_transaction.py @@ -3,6 +3,7 @@ from __future__ import annotations from dataclasses import dataclass from typing import TYPE_CHECKING +from clive.__private.core import iwax from clive.__private.core.commands.abc.command_with_result import CommandWithResult from clive.__private.core.commands.update_transaction_metadata import UpdateTransactionMetadata from clive.__private.core.ensure_transaction import TransactionConvertibleType, ensure_transaction @@ -21,6 +22,7 @@ class BuildTransaction(CommandWithResult[Transaction]): async def _execute(self) -> None: transaction = ensure_transaction(self.content) + iwax.validate_transaction(transaction) if not transaction.is_tapos_set or self.force_update_metadata: assert self.node is not None, "node is required so that transaction metadata can be updated" -- GitLab From d6fe1e2ce10b7d61eb7e7f284c118e948dfed599 Mon Sep 17 00:00:00 2001 From: Marcin Sobczyk Date: Wed, 12 Nov 2025 16:00:38 +0000 Subject: [PATCH 09/15] Perform schemas lazy import only if not type checking without this we hide errors when import is missing while type checking also we need alias for lazy_getattr because otherwise lazy_getattr is not type-checked --- clive/__private/models/schemas.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/clive/__private/models/schemas.py b/clive/__private/models/schemas.py index d3f562b2a1..f95ea2481b 100644 --- a/clive/__private/models/schemas.py +++ b/clive/__private/models/schemas.py @@ -322,7 +322,7 @@ if TYPE_CHECKING: WithdrawRoute = WithdrawVestingRoutesFundament Witness = WitnessesFundament -__getattr__ = lazy_module_factory( +lazy_getattr = lazy_module_factory( globals(), *aggregate_same_import( "FindAccounts", @@ -497,3 +497,6 @@ __getattr__ = lazy_module_factory( ), ("schemas.transaction", "Transaction"), ) + +if not TYPE_CHECKING: + __getattr__ = lazy_getattr -- GitLab From a32d82cf3d871c0b4fc325eeb2d0f74a172527ac Mon Sep 17 00:00:00 2001 From: Marcin Sobczyk Date: Thu, 13 Nov 2025 11:53:49 +0000 Subject: [PATCH 10/15] Add docstrings for tests for cli command update-witness --- tests/functional/cli/process/test_process_update_witness.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/functional/cli/process/test_process_update_witness.py b/tests/functional/cli/process/test_process_update_witness.py index 960dfd7ca7..5b08121d62 100644 --- a/tests/functional/cli/process/test_process_update_witness.py +++ b/tests/functional/cli/process/test_process_update_witness.py @@ -57,6 +57,7 @@ async def test_setting_witness_property( # noqa: PLR0913 property_value: str | int | Decimal | Asset.LiquidT, operation_type: type[OperationBase], ) -> None: + """Test setting witness properties with witness-key or active-key.""" # ARRANGE cli_tester = cli_tester_unlocked_with_witness_profile witness_name = cli_tester.world.profile.accounts.working.name @@ -74,6 +75,7 @@ async def test_setting_witness_property( # noqa: PLR0913 async def test_two_operations_in_transaction( node: tt.RawNode, cli_tester_unlocked_with_witness_profile: CLITester ) -> None: + """Test sending two operations (WitnessUpdateOperation and FeedPublishOperation) in one transaction.""" # ARRANGE cli_tester = cli_tester_unlocked_with_witness_profile operations: list[type[OperationBase]] = [WitnessUpdateOperation, FeedPublishOperation] @@ -101,6 +103,7 @@ async def test_two_operations_in_transaction( async def test_using_updated_witness_key(node: tt.RawNode, cli_tester_unlocked_with_witness_profile: CLITester) -> None: + """Test modification of witness key and using it to sign new transaction.""" # ARRANGE cli_tester = cli_tester_unlocked_with_witness_profile operation = WitnessSetPropertiesOperation -- GitLab From 3b35feb6cdc7ab03f28cd53e3e45bfd35e2fabf8 Mon Sep 17 00:00:00 2001 From: Marcin Sobczyk Date: Thu, 13 Nov 2025 13:28:30 +0000 Subject: [PATCH 11/15] Raise custom exception when in cli attempting to send empty transaction --- .../process/process_account_update.py | 20 +++---------------- .../process/process_update_witness.py | 6 ++---- clive/__private/cli/exceptions.py | 14 +++++++++++++ ...test_process_update_authority_threshold.py | 4 ++-- 4 files changed, 21 insertions(+), 23 deletions(-) diff --git a/clive/__private/cli/commands/process/process_account_update.py b/clive/__private/cli/commands/process/process_account_update.py index c15851c936..cc9a0cd7ad 100644 --- a/clive/__private/cli/commands/process/process_account_update.py +++ b/clive/__private/cli/commands/process/process_account_update.py @@ -2,10 +2,10 @@ from __future__ import annotations from copy import deepcopy from dataclasses import dataclass, field -from typing import TYPE_CHECKING, Final, cast, override +from typing import TYPE_CHECKING, cast, override from clive.__private.cli.commands.abc.operation_command import OperationCommand -from clive.__private.cli.exceptions import CLIPrettyError +from clive.__private.cli.exceptions import CLINoChangesTransactionError, CLIPrettyError from clive.__private.models.schemas import AccountName, AccountUpdate2Operation, Authority, HiveInt, PublicKey if TYPE_CHECKING: @@ -14,20 +14,6 @@ if TYPE_CHECKING: from clive.__private.models.schemas import Account -class NoChangesTransactionError(CLIPrettyError): - """ - Raised when trying to create a transaction with no changes to authority. - - Attributes: - MESSAGE: A message displayed to user when this error occurs. - """ - - MESSAGE: Final[str] = "Transaction with no changes to authority cannot be created." - - def __init__(self) -> None: - super().__init__(self.MESSAGE) - - @dataclass(kw_only=True) class ProcessAccountUpdate(OperationCommand): account_name: str @@ -42,7 +28,7 @@ class ProcessAccountUpdate(OperationCommand): @override async def validate(self) -> None: if len(self._callbacks) == 0: - raise NoChangesTransactionError + raise CLINoChangesTransactionError await super().validate() async def _create_operations(self) -> ComposeTransaction: diff --git a/clive/__private/cli/commands/process/process_update_witness.py b/clive/__private/cli/commands/process/process_update_witness.py index 6096565a5e..2098fc7025 100644 --- a/clive/__private/cli/commands/process/process_update_witness.py +++ b/clive/__private/cli/commands/process/process_update_witness.py @@ -4,7 +4,7 @@ from dataclasses import dataclass, field from typing import TYPE_CHECKING, override from clive.__private.cli.commands.abc.operation_command import OperationCommand -from clive.__private.cli.exceptions import CLIPrettyError, CLIWitnessNotFoundError +from clive.__private.cli.exceptions import CLINoChangesTransactionError, CLIPrettyError, CLIWitnessNotFoundError from clive.__private.core import iwax from clive.__private.core.commands.find_witness import WitnessNotFoundError from clive.__private.core.keys.keys import PublicKey @@ -98,9 +98,7 @@ class ProcessUpdateWitness(OperationCommand): ] ) if not is_operation_required: - raise CLIPrettyError( - "Transaction with no changes to witness cannot be created. Use '--help' flag to display help." - ) + raise CLINoChangesTransactionError def _validate_requirements_for_witness_set_propertues_operation(self) -> None: if self.use_active_authority and self.is_option_given(self.account_subsidy_budget): diff --git a/clive/__private/cli/exceptions.py b/clive/__private/cli/exceptions.py index 46f36a8813..99e6d4c9b4 100644 --- a/clive/__private/cli/exceptions.py +++ b/clive/__private/cli/exceptions.py @@ -517,3 +517,17 @@ class CLIWitnessNotFoundError(CLIPrettyError): def __init__(self, name: str) -> None: message = f"Witness `{name}` was not found." super().__init__(message, errno.EINVAL) + + +class CLINoChangesTransactionError(CLIPrettyError): + """ + Raised when trying to create a transaction with no operations. + + Attributes: + MESSAGE: A message displayed to user when this error occurs. + """ + + MESSAGE: Final[str] = "Transaction with no operations cannot be created. Use '--help' flag to display help." + + def __init__(self) -> None: + super().__init__(self.MESSAGE) diff --git a/tests/functional/cli/process/test_process_update_authority_threshold.py b/tests/functional/cli/process/test_process_update_authority_threshold.py index 4989738f3d..82711bc013 100644 --- a/tests/functional/cli/process/test_process_update_authority_threshold.py +++ b/tests/functional/cli/process/test_process_update_authority_threshold.py @@ -4,7 +4,7 @@ from typing import TYPE_CHECKING import pytest -from clive.__private.cli.commands.process.process_account_update import NoChangesTransactionError +from clive.__private.cli.exceptions import CLINoChangesTransactionError from clive.__private.core.constants.authority import AUTHORITY_LEVELS_REGULAR from clive_local_tools.cli.checkers import assert_weight_threshold from clive_local_tools.cli.exceptions import CLITestCommandError @@ -32,5 +32,5 @@ async def test_set_threshold(cli_tester: CLITester, authority: AuthorityLevelReg @pytest.mark.parametrize("authority", AUTHORITY_LEVELS_REGULAR) async def test_negative_do_nothing_command(cli_tester: CLITester, authority: AuthorityLevelRegular) -> None: # ACT & ASSERT - with pytest.raises(CLITestCommandError, match=NoChangesTransactionError.MESSAGE): + with pytest.raises(CLITestCommandError, match=CLINoChangesTransactionError.MESSAGE): cli_tester.process_update_authority(authority, sign_with=WORKING_ACCOUNT_KEY_ALIAS).fire() -- GitLab From 1198b507685a63503379017186fe9583cf9e0cd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20=C5=BBebrak?= Date: Fri, 14 Nov 2025 09:33:12 +0100 Subject: [PATCH 12/15] Properly handle wax transaction validation error --- .../__private/core/commands/build_transaction.py | 16 +++++++++++++++- .../error_handlers/general_error_notificator.py | 13 ++++++++++--- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/clive/__private/core/commands/build_transaction.py b/clive/__private/core/commands/build_transaction.py index c71d95cfc1..48d9c74e0c 100644 --- a/clive/__private/core/commands/build_transaction.py +++ b/clive/__private/core/commands/build_transaction.py @@ -4,15 +4,26 @@ from dataclasses import dataclass from typing import TYPE_CHECKING from clive.__private.core import iwax +from clive.__private.core.commands.abc.command import CommandError from clive.__private.core.commands.abc.command_with_result import CommandWithResult from clive.__private.core.commands.update_transaction_metadata import UpdateTransactionMetadata from clive.__private.core.ensure_transaction import TransactionConvertibleType, ensure_transaction +from clive.__private.core.iwax import WaxOperationFailedError from clive.__private.models.transaction import Transaction if TYPE_CHECKING: from clive.__private.core.node import Node +class TransactionWaxValidationError(CommandError): + def __init__(self, command: BuildTransaction, transaction: Transaction, wax_error_details: str) -> None: + self.transaction = transaction + self.wax_error_details = wax_error_details + + reason = f"Transaction validation failed.\nWax error details:\n{wax_error_details}" + super().__init__(command, reason) + + @dataclass(kw_only=True) class BuildTransaction(CommandWithResult[Transaction]): content: TransactionConvertibleType @@ -22,7 +33,10 @@ class BuildTransaction(CommandWithResult[Transaction]): async def _execute(self) -> None: transaction = ensure_transaction(self.content) - iwax.validate_transaction(transaction) + try: + iwax.validate_transaction(transaction) + except WaxOperationFailedError as error: + raise TransactionWaxValidationError(self, transaction, str(error)) from error if not transaction.is_tapos_set or self.force_update_metadata: assert self.node is not None, "node is required so that transaction metadata can be updated" diff --git a/clive/__private/core/error_handlers/general_error_notificator.py b/clive/__private/core/error_handlers/general_error_notificator.py index be93dc8f6d..094a144dfc 100644 --- a/clive/__private/core/error_handlers/general_error_notificator.py +++ b/clive/__private/core/error_handlers/general_error_notificator.py @@ -1,14 +1,18 @@ from __future__ import annotations -from typing import Final, TypeGuard +from typing import TYPE_CHECKING, Final, TypeGuard import beekeepy.exceptions as bke +from clive.__private.core.commands.build_transaction import TransactionWaxValidationError from clive.__private.core.commands.recover_wallets import CannotRecoverWalletsError from clive.__private.core.commands.save_profile import ProfileSavingFailedError from clive.__private.core.error_handlers.abc.error_notificator import ErrorNotificator from clive.__private.storage.service.exceptions import ProfileEncryptionError +if TYPE_CHECKING: + from collections.abc import Callable + INVALID_PASSWORD_MESSAGE: Final[str] = "The password you entered is incorrect. Please try again." # noqa: S105 @@ -20,13 +24,16 @@ class GeneralErrorNotificator(ErrorNotificator[Exception]): SEARCHED_AND_PRINTED_MESSAGES: A mapping of exception types to messages that should be printed. """ - SEARCHED_AND_PRINTED_MESSAGES: Final[dict[type[Exception], str]] = { + SEARCHED_AND_PRINTED_MESSAGES: Final[dict[type[Exception], str | Callable[[Exception], str]]] = { bke.InvalidPasswordError: INVALID_PASSWORD_MESSAGE, bke.NoWalletWithSuchNameError: "Wallet with this name was not found on the beekeeper. Please try again.", bke.NotExistingKeyError: "Key does not exist in the wallet.", ProfileEncryptionError: "Profile encryption failed which means profile cannot be saved or loaded.", CannotRecoverWalletsError: CannotRecoverWalletsError.MESSAGE, ProfileSavingFailedError: ProfileSavingFailedError.MESSAGE, + TransactionWaxValidationError: lambda error: ( + f"Transaction validation failed. Wax error details:\n{error.wax_error_details}" # type: ignore[attr-defined] + ), } def __init__(self) -> None: @@ -36,7 +43,7 @@ class GeneralErrorNotificator(ErrorNotificator[Exception]): def _is_exception_to_catch(self, error: Exception) -> TypeGuard[Exception]: for searched, printed in self.SEARCHED_AND_PRINTED_MESSAGES.items(): if type(error) is searched: - self._message_to_print = printed + self._message_to_print = printed if isinstance(printed, str) else printed(error) return True return False -- GitLab From 6ed24c7c4325671156d574b3243297214cefd467 Mon Sep 17 00:00:00 2001 From: Marcin Sobczyk Date: Fri, 14 Nov 2025 12:17:05 +0000 Subject: [PATCH 13/15] Expose wax to schemas operation conversion --- .../process/process_update_witness.py | 6 +- clive/__private/core/iwax.py | 78 +------------- clive/__private/core/wax_operation_wrapper.py | 101 ++++++++++++++++++ clive/exceptions.py | 18 +++- 4 files changed, 124 insertions(+), 79 deletions(-) create mode 100644 clive/__private/core/wax_operation_wrapper.py diff --git a/clive/__private/cli/commands/process/process_update_witness.py b/clive/__private/cli/commands/process/process_update_witness.py index 2098fc7025..39ece8ecbb 100644 --- a/clive/__private/cli/commands/process/process_update_witness.py +++ b/clive/__private/cli/commands/process/process_update_witness.py @@ -5,10 +5,10 @@ from typing import TYPE_CHECKING, override from clive.__private.cli.commands.abc.operation_command import OperationCommand from clive.__private.cli.exceptions import CLINoChangesTransactionError, CLIPrettyError, CLIWitnessNotFoundError -from clive.__private.core import iwax from clive.__private.core.commands.find_witness import WitnessNotFoundError from clive.__private.core.keys.keys import PublicKey from clive.__private.core.percent_conversions import percent_to_hive_percent +from clive.__private.core.wax_operation_wrapper import WaxOperationWrapper from clive.__private.models.asset import Asset from clive.__private.models.schemas import ( AccountName, @@ -169,7 +169,7 @@ class ProcessUpdateWitness(OperationCommand): ) def _create_witness_set_properties_operation(self) -> WitnessSetPropertiesOperation: - wax_operation_wrapper = iwax.WitnessSetPropertiesWrapper.create( + wrapper = WaxOperationWrapper.create_witness_set_properties( owner=self.owner, key=PublicKey(value=self.witness_ensure.signing_key), new_signing_key=self.new_signing_key, @@ -181,4 +181,4 @@ class ProcessUpdateWitness(OperationCommand): account_subsidy_budget=self.account_subsidy_budget, account_subsidy_decay=self.account_subsidy_decay, ) - return wax_operation_wrapper.to_schemas(self.world.wax_interface) + return wrapper.to_schemas(self.world.wax_interface, WitnessSetPropertiesOperation) diff --git a/clive/__private/core/iwax.py b/clive/__private/core/iwax.py index e44096b295..5827909d28 100644 --- a/clive/__private/core/iwax.py +++ b/clive/__private/core/iwax.py @@ -3,14 +3,12 @@ from __future__ import annotations import datetime from collections.abc import Callable from functools import wraps -from typing import TYPE_CHECKING, Any, Protocol, Self, cast, get_args +from typing import TYPE_CHECKING, Any, Protocol, cast import wax from clive.__private.core.constants.precision import HIVE_PERCENT_PRECISION_DOT_PLACES from clive.__private.core.decimal_conventer import DecimalConverter -from clive.__private.core.percent_conversions import hive_percent_to_percent, percent_to_hive_percent -from clive.__private.models.schemas import AccountName, WitnessPropsSerializedKey -from clive.__private.models.schemas import WitnessSetPropertiesOperation as SchemasWitnessSetPropertiesOperation +from clive.__private.core.percent_conversions import hive_percent_to_percent from clive.exceptions import CliveError if TYPE_CHECKING: @@ -18,9 +16,8 @@ if TYPE_CHECKING: from clive.__private.core.keys import PrivateKey, PublicKey from clive.__private.models.asset import Asset - from clive.__private.models.schemas import Hex, OperationUnion, PriceFeed + from clive.__private.models.schemas import OperationUnion, PriceFeed from clive.__private.models.transaction import Transaction - from wax.complex_operations.witness_set_properties import WitnessSetProperties as WaxWitnessSetProperties def cast_hiveint_args[F: Callable[..., Any]](func: F) -> F: @@ -272,72 +269,3 @@ def generate_password_based_private_key( def suggest_brain_key() -> str: result = wax.suggest_brain_key() return result.brain_key.decode() - - -class WitnessSetPropertiesWrapper: - def __init__(self, operation: WaxWitnessSetProperties) -> None: - self._operation = operation - - @classmethod - def create( # noqa: PLR0913 - cls, - owner: str, - key: str | PublicKey, - new_signing_key: str | PublicKey | None = None, - account_creation_fee: Asset.Hive | None = None, - url: str | None = None, - hbd_exchange_rate: Asset.Hbd | None = None, - maximum_block_size: int | None = None, - hbd_interest_rate: Decimal | None = None, - account_subsidy_budget: int | None = None, - account_subsidy_decay: int | None = None, - ) -> Self: - from clive.__private.core.keys import PublicKey # noqa: PLC0415 - from clive.__private.models.asset import Asset # noqa: PLC0415 - from wax.complex_operations.witness_set_properties import ( # noqa: PLC0415 - WitnessSetProperties, - WitnessSetPropertiesData, - ) - - def key_string(input_key: str | PublicKey) -> str: - return input_key.value if isinstance(input_key, PublicKey) else input_key - - return cls( - WitnessSetProperties( - data=WitnessSetPropertiesData( - owner=AccountName(owner), - witness_signing_key=key_string(key), - new_signing_key=key_string(new_signing_key) if new_signing_key is not None else None, - account_creation_fee=account_creation_fee.as_serialized_nai() - if account_creation_fee is not None - else None, - url=url, - hbd_exchange_rate=wax.complex_operations.witness_set_properties.HbdExchangeRate( - base=hbd_exchange_rate.as_serialized_nai(), - quote=Asset.hive(1).as_serialized_nai(), - ) - if hbd_exchange_rate - else None, - maximum_block_size=maximum_block_size, - hbd_interest_rate=percent_to_hive_percent(hbd_interest_rate) if hbd_interest_rate else None, - account_subsidy_budget=account_subsidy_budget, - account_subsidy_decay=account_subsidy_decay, - ) - ) - ) - - def to_schemas(self, wax_interface: wax.IWaxBaseInterface) -> SchemasWitnessSetPropertiesOperation: - first = next(iter(self._operation.finalize(wax_interface))) - props = self._props_to_schemas(first.props) - return SchemasWitnessSetPropertiesOperation(owner=self._operation.owner, props=props) - - def _props_to_schemas(self, wax_props: dict[str, str]) -> list[tuple[WitnessPropsSerializedKey, Hex]]: - schemas_properties_model: list[tuple[WitnessPropsSerializedKey, Hex]] = [] - - def append_optional_property(name: WitnessPropsSerializedKey) -> None: - if property_value := wax_props.get(name): - schemas_properties_model.append((name, property_value)) - - for property_name in get_args(WitnessPropsSerializedKey): - append_optional_property(property_name) - return schemas_properties_model diff --git a/clive/__private/core/wax_operation_wrapper.py b/clive/__private/core/wax_operation_wrapper.py new file mode 100644 index 0000000000..96fad9b8a0 --- /dev/null +++ b/clive/__private/core/wax_operation_wrapper.py @@ -0,0 +1,101 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Self, TypeVar, overload + +from clive.__private.core.percent_conversions import percent_to_hive_percent +from clive.__private.models.schemas import AccountName, OperationUnion +from clive.exceptions import WrongTypeError + +if TYPE_CHECKING: + from decimal import Decimal + + import wax + from clive.__private.core.keys import PublicKey + from clive.__private.models.asset import Asset + from wax._private.operation_base import OperationBase as WaxOperationBase + + +OperationExpectType = TypeVar("OperationExpectType", bound=OperationUnion) + + +class WaxOperationWrapper: + def __init__(self, wax_operation: WaxOperationBase) -> None: + self._wax_operation = wax_operation + + @classmethod + def create_witness_set_properties( # noqa: PLR0913 + cls, + *, + owner: str, + key: str | PublicKey, + new_signing_key: str | PublicKey | None = None, + account_creation_fee: Asset.Hive | None = None, + url: str | None = None, + hbd_exchange_rate: Asset.Hbd | None = None, + maximum_block_size: int | None = None, + hbd_interest_rate: Decimal | None = None, + account_subsidy_budget: int | None = None, + account_subsidy_decay: int | None = None, + ) -> Self: + from clive.__private.core.keys import PublicKey # noqa: PLC0415 + from clive.__private.models.asset import Asset # noqa: PLC0415 + from wax.complex_operations.witness_set_properties import ( # noqa: PLC0415 + HbdExchangeRate, + WitnessSetProperties, + WitnessSetPropertiesData, + ) + + def key_string(input_key: str | PublicKey) -> str: + return PublicKey(value=input_key).value if isinstance(input_key, str) else input_key.value + + return cls( + WitnessSetProperties( + data=WitnessSetPropertiesData( + owner=AccountName(owner), + witness_signing_key=key_string(key), + new_signing_key=key_string(new_signing_key) if new_signing_key is not None else None, + account_creation_fee=account_creation_fee.as_serialized_nai() + if account_creation_fee is not None + else None, + url=url, + hbd_exchange_rate=HbdExchangeRate( + base=hbd_exchange_rate.as_serialized_nai(), + quote=Asset.hive(1).as_serialized_nai(), + ) + if hbd_exchange_rate + else None, + maximum_block_size=maximum_block_size, + hbd_interest_rate=percent_to_hive_percent(hbd_interest_rate) if hbd_interest_rate else None, + account_subsidy_budget=account_subsidy_budget, + account_subsidy_decay=account_subsidy_decay, + ) + ) + ) + + @overload + def to_schemas( + self, wax_interface: wax.IHiveChainInterface, expect_type: type[OperationExpectType] + ) -> OperationExpectType: ... + + @overload + def to_schemas(self, wax_interface: wax.IHiveChainInterface, expect_type: None = None) -> OperationUnion: ... + + def to_schemas( + self, wax_interface: wax.IHiveChainInterface, expect_type: type[OperationExpectType] | None = None + ) -> OperationExpectType: + from clive.__private.models.transaction import Transaction # noqa: PLC0415 + + # We must specify tapos now, this will be changed with resolving issue https://gitlab.syncad.com/hive/wax/-/issues/128 + wax_transaction = wax_interface.create_transaction_with_tapos("0") + + proto_operations = list(self._wax_operation.finalize(wax_interface)) + assert len(proto_operations) == 1, "A single proto operation was expected when finalizing" + wax_transaction.push_operation(proto_operations[0]) + + schemas_transaction = Transaction.parse_raw(wax_transaction.to_api_json()) + schemas_operation = schemas_transaction.operations_models[0] + + if expect_type and not isinstance(schemas_operation, expect_type): + raise WrongTypeError(expect_type, type(schemas_operation)) + + return schemas_operation # type: ignore[return-value] diff --git a/clive/exceptions.py b/clive/exceptions.py index ff2768aec5..dfa75029e9 100644 --- a/clive/exceptions.py +++ b/clive/exceptions.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Final +from typing import Any, Final class CliveError(Exception): @@ -43,3 +43,19 @@ class RequestIdError(CliveError): class ProfileNotLoadedError(CliveError): """Raise when profile is requested and was not loaded.""" + + +class WrongTypeError(CliveError): + """ + Raised when the type of the value is not the expected one. + + Args: + expected_type: Expected type. + actual_type: Actual type. + """ + + def __init__(self, expected_type: type[Any], actual_type: type[Any]) -> None: + self.expected_type = expected_type + self.actual_type = actual_type + self.message = f"Expected type {expected_type}, got {actual_type}" + super().__init__(self.message) -- GitLab From 5e84d7d0b07aa931a8c6dc1832f119fe17580d85 Mon Sep 17 00:00:00 2001 From: Marcin Sobczyk Date: Fri, 14 Nov 2025 13:39:57 +0000 Subject: [PATCH 14/15] Allow to create PublicKey from str or clone other PublicKey --- clive/__private/core/keys/keys.py | 18 ++++++++++++++++++ clive/__private/core/wax_operation_wrapper.py | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/clive/__private/core/keys/keys.py b/clive/__private/core/keys/keys.py index e5782066fd..12f9e49c4c 100644 --- a/clive/__private/core/keys/keys.py +++ b/clive/__private/core/keys/keys.py @@ -1,6 +1,7 @@ from __future__ import annotations from abc import ABC, abstractmethod +from copy import deepcopy from dataclasses import dataclass from typing import TYPE_CHECKING, Literal, overload @@ -124,6 +125,23 @@ class PublicKey(Key): def __hash__(self) -> int: return super().__hash__() + @staticmethod + @overload + def create(value: str) -> PublicKey: ... + + @staticmethod + @overload + def create[T: PublicKey](value: T) -> T: ... + + @staticmethod + @overload + def create(value: str | PublicKey, with_alias: str) -> PublicKeyAliased: ... + + @staticmethod + def create(value: str | PublicKey, with_alias: str = "") -> PublicKey | PublicKeyAliased: + public_key = PublicKey(value=value) if isinstance(value, str) else deepcopy(value) + return public_key.with_alias(with_alias) if with_alias else public_key + @staticmethod def validate(key: str) -> None: """ diff --git a/clive/__private/core/wax_operation_wrapper.py b/clive/__private/core/wax_operation_wrapper.py index 96fad9b8a0..2c611ffbba 100644 --- a/clive/__private/core/wax_operation_wrapper.py +++ b/clive/__private/core/wax_operation_wrapper.py @@ -46,7 +46,7 @@ class WaxOperationWrapper: ) def key_string(input_key: str | PublicKey) -> str: - return PublicKey(value=input_key).value if isinstance(input_key, str) else input_key.value + return PublicKey.create(input_key).value return cls( WitnessSetProperties( -- GitLab From e1f4f813fc0de481b64c28abacac694dfae07a81 Mon Sep 17 00:00:00 2001 From: Marcin Sobczyk Date: Fri, 14 Nov 2025 14:04:55 +0000 Subject: [PATCH 15/15] Rename PrivateKey.create* to PrivateKey.generate_* --- .../cli/commands/generate/generate_key_from_seed.py | 2 +- .../cli/commands/generate/generate_random_key.py | 2 +- .../core/commands/create_encryption_wallet.py | 2 +- clive/__private/core/keys/keys.py | 12 ++++++------ .../clive_local_tools/data/models.py | 2 +- .../cli/autosign/test_autosign_on_operations.py | 2 +- .../cli/autosign/test_autosign_on_transaction.py | 2 +- tests/functional/cli/configure/test_configure_key.py | 6 +++--- .../cli/process/test_process_account_creation.py | 2 +- .../cli/process/test_process_transaction.py | 2 +- .../cli/process/test_process_update_witness.py | 2 +- tests/unit/keys/test_key_manager.py | 12 ++++++------ tests/unit/keys/test_keys_equal.py | 2 +- 13 files changed, 25 insertions(+), 25 deletions(-) diff --git a/clive/__private/cli/commands/generate/generate_key_from_seed.py b/clive/__private/cli/commands/generate/generate_key_from_seed.py index a1b4d0d4f6..f0133c51d5 100644 --- a/clive/__private/cli/commands/generate/generate_key_from_seed.py +++ b/clive/__private/cli/commands/generate/generate_key_from_seed.py @@ -27,7 +27,7 @@ class GenerateKeyFromSeed(ExternalCLICommand): self.read_interactive("Enter seed (like as secret phrase)") if self.is_interactive else self.read_piped() ) - private_key = PrivateKey.create_from_seed(password, self.account_name, role=self.role) + private_key = PrivateKey.generate_from_seed(password, self.account_name, role=self.role) if self.only_public_key: print_cli(private_key.calculate_public_key().value) elif self.only_private_key: diff --git a/clive/__private/cli/commands/generate/generate_random_key.py b/clive/__private/cli/commands/generate/generate_random_key.py index 0a40893ebd..e00a180136 100644 --- a/clive/__private/cli/commands/generate/generate_random_key.py +++ b/clive/__private/cli/commands/generate/generate_random_key.py @@ -13,6 +13,6 @@ class GenerateRandomKey(ExternalCLICommand): async def _run(self) -> None: for _ in range(self.key_pairs): - private_key = PrivateKey.create() + private_key = PrivateKey.generate() print_cli(private_key.value) print_cli(private_key.calculate_public_key().value) diff --git a/clive/__private/core/commands/create_encryption_wallet.py b/clive/__private/core/commands/create_encryption_wallet.py index 1bee7ae008..2f645780c8 100644 --- a/clive/__private/core/commands/create_encryption_wallet.py +++ b/clive/__private/core/commands/create_encryption_wallet.py @@ -24,6 +24,6 @@ class CreateEncryptionWallet(CommandWithResult[bk.AsyncUnlockedWallet]): name=EncryptionService.get_encryption_wallet_name(self.profile_name), password=self.password ) await unlocked_encryption_wallet.import_key( - private_key=PrivateKey.create_from_seed(self.password, account_name=self.profile_name).value + private_key=PrivateKey.generate_from_seed(self.password, account_name=self.profile_name).value ) self._result = unlocked_encryption_wallet diff --git a/clive/__private/core/keys/keys.py b/clive/__private/core/keys/keys.py index 12f9e49c4c..bfd0cae14b 100644 --- a/clive/__private/core/keys/keys.py +++ b/clive/__private/core/keys/keys.py @@ -209,29 +209,29 @@ class PrivateKey(Key): @staticmethod @overload - def create() -> PrivateKey: ... + def generate() -> PrivateKey: ... @staticmethod @overload - def create(*, with_alias: str) -> PrivateKeyAliased: ... + def generate(*, with_alias: str) -> PrivateKeyAliased: ... @staticmethod - def create(*, with_alias: str = "") -> PrivateKey | PrivateKeyAliased: + def generate(*, with_alias: str = "") -> PrivateKey | PrivateKeyAliased: private_key = iwax.generate_private_key() return private_key.with_alias(with_alias) if with_alias else private_key @staticmethod @overload - def create_from_seed(seed: str, account_name: str, *, role: AuthorityLevel = "memo") -> PrivateKey: ... + def generate_from_seed(seed: str, account_name: str, *, role: AuthorityLevel = "memo") -> PrivateKey: ... @staticmethod @overload - def create_from_seed( + def generate_from_seed( seed: str, account_name: str, *, role: AuthorityLevel = "memo", with_alias: str = "" ) -> PrivateKeyAliased: ... @staticmethod - def create_from_seed( + def generate_from_seed( seed: str, account_name: str, *, role: AuthorityLevel = "memo", with_alias: str = "" ) -> PrivateKey | PrivateKeyAliased: private_key = iwax.generate_password_based_private_key(seed, role, account_name) diff --git a/tests/clive-local-tools/clive_local_tools/data/models.py b/tests/clive-local-tools/clive_local_tools/data/models.py index 47bdff0430..9e17f217bd 100644 --- a/tests/clive-local-tools/clive_local_tools/data/models.py +++ b/tests/clive-local-tools/clive_local_tools/data/models.py @@ -35,7 +35,7 @@ class Keys: def generate_key_pair(self, *, alias: str | None = None) -> KeysPair: alias = f"key-{len(self.pairs)}" if alias is None else alias - private_key = PrivateKeyAliased.create(with_alias=alias) + private_key = PrivateKeyAliased.generate(with_alias=alias) public_key = private_key.calculate_public_key() return Keys.KeysPair(public_key, private_key) diff --git a/tests/functional/cli/autosign/test_autosign_on_operations.py b/tests/functional/cli/autosign/test_autosign_on_operations.py index de74266d54..4e97f13546 100644 --- a/tests/functional/cli/autosign/test_autosign_on_operations.py +++ b/tests/functional/cli/autosign/test_autosign_on_operations.py @@ -35,7 +35,7 @@ import test_tools as tt RECEIVER: Final[str] = WATCHED_ACCOUNTS_DATA[0].account.name AMOUNT: Final[tt.Asset.HiveT] = tt.Asset.Hive(1) MEMO: Final[str] = "test-process-transfer-autosign-single-key" -ADDITIONAL_KEY_VALUE: str = PrivateKey.create().value +ADDITIONAL_KEY_VALUE: str = PrivateKey.generate().value ADDITIONAL_KEY_ALIAS_NAME: Final[str] = f"{WORKING_ACCOUNT_KEY_ALIAS}_2" diff --git a/tests/functional/cli/autosign/test_autosign_on_transaction.py b/tests/functional/cli/autosign/test_autosign_on_transaction.py index 48ca27e6e9..44701c5693 100644 --- a/tests/functional/cli/autosign/test_autosign_on_transaction.py +++ b/tests/functional/cli/autosign/test_autosign_on_transaction.py @@ -41,7 +41,7 @@ if TYPE_CHECKING: AMOUNT: Final[tt.Asset.HiveT] = tt.Asset.Hive(10) TO_ACCOUNT: Final[str] = WATCHED_ACCOUNTS_NAMES[0] AUTO_SIGN_SKIPPED_WARNING_MESSAGE: Final[str] = "Your transaction is already signed. Autosign will be skipped" -ADDITIONAL_KEY_VALUE: str = PrivateKey.create().value +ADDITIONAL_KEY_VALUE: str = PrivateKey.generate().value ADDITIONAL_KEY_ALIAS_NAME: Final[str] = f"{WORKING_ACCOUNT_KEY_ALIAS}_2" WORKING_ACCOUNT_KEY_VALUE: str = WORKING_ACCOUNT_DATA.account.private_key diff --git a/tests/functional/cli/configure/test_configure_key.py b/tests/functional/cli/configure/test_configure_key.py index e3da5b5eb5..ecacd89200 100644 --- a/tests/functional/cli/configure/test_configure_key.py +++ b/tests/functional/cli/configure/test_configure_key.py @@ -26,7 +26,7 @@ async def assert_key_exists(wallet: AsyncUnlockedWallet, private_key: PrivateKey async def test_configure_key_add(cli_tester: CLITester) -> None: """Check clive configure key add command.""" # ARRANGE - pk = PrivateKey.create() + pk = PrivateKey.generate() unlocked_wallet = cli_tester.world.beekeeper_manager.user_wallet await assert_key_exists(unlocked_wallet, pk, should_exists=False) @@ -42,7 +42,7 @@ async def test_negative_configure_key_add_in_locked( ) -> None: """Check if clive configure add_key command throws exception when wallet is locked.""" # ARRANGE - pk = PrivateKey.create() + pk = PrivateKey.generate() message = CLINoProfileUnlockedError.MESSAGE # ACT & ASSERT @@ -54,7 +54,7 @@ async def test_negative_configure_key_add_in_locked( async def test_configure_key_remove(cli_tester: CLITester, *, from_beekeeper: bool) -> None: """Check clive configure key remove command.""" # ARRANGE - pk = PrivateKey.create() + pk = PrivateKey.generate() unlocked_wallet = cli_tester.world.beekeeper_manager.user_wallet await assert_key_exists(unlocked_wallet, pk, should_exists=False) cli_tester.configure_key_add(key=pk.value, alias="key") diff --git a/tests/functional/cli/process/test_process_account_creation.py b/tests/functional/cli/process/test_process_account_creation.py index 2d609c17d0..29971e6d32 100644 --- a/tests/functional/cli/process/test_process_account_creation.py +++ b/tests/functional/cli/process/test_process_account_creation.py @@ -33,7 +33,7 @@ WEIGHT_THRESHOLD: Final[int] = 2 def create_public_key_for_role(new_account_name: str = NEW_ACCOUNT_NAME, *, role: AuthorityLevel) -> PublicKey: - private_key = PrivateKey.create_from_seed("seed", new_account_name, role=role) + private_key = PrivateKey.generate_from_seed("seed", new_account_name, role=role) return private_key.calculate_public_key().value diff --git a/tests/functional/cli/process/test_process_transaction.py b/tests/functional/cli/process/test_process_transaction.py index e737e7fdbf..d70771483f 100644 --- a/tests/functional/cli/process/test_process_transaction.py +++ b/tests/functional/cli/process/test_process_transaction.py @@ -33,7 +33,7 @@ EXAMPLE_STRING: Final[str] = '"somestring"' EXAMPLE_NUMBER: Final[str] = "123456.789" ID: Final[str] = "test-custom-json-some-id" RECEIVER: Final[str] = WATCHED_ACCOUNTS_DATA[0].account.name -ADDITIONAL_KEY_VALUE: str = PrivateKey.create().value +ADDITIONAL_KEY_VALUE: str = PrivateKey.generate().value ADDITIONAL_KEY_ALIAS_NAME: Final[str] = f"{WORKING_ACCOUNT_KEY_ALIAS}_2" diff --git a/tests/functional/cli/process/test_process_update_witness.py b/tests/functional/cli/process/test_process_update_witness.py index 5b08121d62..3a29cdcc83 100644 --- a/tests/functional/cli/process/test_process_update_witness.py +++ b/tests/functional/cli/process/test_process_update_witness.py @@ -109,7 +109,7 @@ async def test_using_updated_witness_key(node: tt.RawNode, cli_tester_unlocked_w operation = WitnessSetPropertiesOperation account_subsidy_decay = 6543 alias = "updated_signing_key" - new_private_signing_key = PrivateKey.create() + new_private_signing_key = PrivateKey.generate() cli_tester.process_update_witness(new_signing_key=new_private_signing_key.calculate_public_key().value) cli_tester.configure_key_add(key=new_private_signing_key.value, alias=alias) witness_name = cli_tester.world.profile.accounts.working.name diff --git a/tests/unit/keys/test_key_manager.py b/tests/unit/keys/test_key_manager.py index 781e0d6a78..a2a243d92d 100644 --- a/tests/unit/keys/test_key_manager.py +++ b/tests/unit/keys/test_key_manager.py @@ -61,14 +61,14 @@ async def test_key_manager_contains( @pytest.mark.parametrize( "compare_key", [ - PrivateKey.create().value, - PrivateKey.create().calculate_public_key().value, - PrivateKey.create(), - PrivateKey.create().calculate_public_key(), + PrivateKey.generate().value, + PrivateKey.generate().calculate_public_key().value, + PrivateKey.generate(), + PrivateKey.generate().calculate_public_key(), IMPORTED_PRIVATE_KEY.with_alias("different"), IMPORTED_PUBLIC_KEY.with_alias("different"), - PrivateKey.create(with_alias=IMPORTED_KEY_ALIAS), - PrivateKey.create(with_alias=IMPORTED_KEY_ALIAS).calculate_public_key(), + PrivateKey.generate(with_alias=IMPORTED_KEY_ALIAS), + PrivateKey.generate(with_alias=IMPORTED_KEY_ALIAS).calculate_public_key(), ], ids=[ "private_key_string", diff --git a/tests/unit/keys/test_keys_equal.py b/tests/unit/keys/test_keys_equal.py index 97a7987b3b..29adca7593 100644 --- a/tests/unit/keys/test_keys_equal.py +++ b/tests/unit/keys/test_keys_equal.py @@ -79,4 +79,4 @@ def test_different_alias_private_key() -> None: def test_comparing_public_key_to_other_public_key_raw() -> None: # ACT & ASSERT - assert PrivateKey.create().calculate_public_key().value != PUBLIC_KEY + assert PrivateKey.generate().calculate_public_key().value != PUBLIC_KEY -- GitLab