diff --git a/chia/_tests/wallet/did_wallet/test_did.py b/chia/_tests/wallet/did_wallet/test_did.py index cf36960abb13..0fe6677d7990 100644 --- a/chia/_tests/wallet/did_wallet/test_did.py +++ b/chia/_tests/wallet/did_wallet/test_did.py @@ -2,7 +2,7 @@ import dataclasses import json -from typing import Any, Optional, Union +from typing import Any import pytest from chia_rs import AugSchemeMPL, G1Element, G2Element @@ -24,12 +24,7 @@ from chia.types.signing_mode import CHIP_0002_SIGN_MESSAGE_PREFIX from chia.util.bech32m import decode_puzzle_hash, encode_puzzle_hash from chia.wallet.did_wallet.did_wallet import DIDWallet -from chia.wallet.did_wallet.did_wallet_puzzles import ( - DID_INNERPUZ_MOD, -) from chia.wallet.singleton import ( - SINGLETON_LAUNCHER_PUZZLE_HASH, - SINGLETON_TOP_LAYER_MOD_HASH, create_singleton_puzzle, ) from chia.wallet.util.address_type import AddressType @@ -38,9 +33,8 @@ from chia.wallet.wallet import Wallet from chia.wallet.wallet_action_scope import WalletActionScope from chia.wallet.wallet_node import WalletNode -from chia.wallet.wallet_request_types import DIDFindLostDID, DIDGetCurrentCoinInfo, DIDGetInfo, DIDGetRecoveryInfo +from chia.wallet.wallet_request_types import DIDFindLostDID, DIDGetCurrentCoinInfo, DIDGetInfo from chia.wallet.wallet_rpc_api import WalletRpcApi -from chia.wallet.wallet_spend_bundle import WalletSpendBundle async def get_wallet_num(wallet_manager): @@ -56,34 +50,12 @@ async def make_did_wallet( wallet: Wallet, amount: uint64, action_scope: WalletActionScope, - recovery_list: list[bytes32] = [], metadata: dict[str, str] = {}, fee: uint64 = uint64(0), - use_alternate_recovery: bool = False, ) -> DIDWallet: - def alt_create_innerpuz( - p2_puzzle_or_hash: Union[Program, bytes32], - recovery_list: list[bytes32], - num_of_backup_ids_needed: uint64, - launcher_id: bytes32, - metadata: Program = Program.to([]), - recovery_list_hash: Optional[Program] = None, - ) -> Program: - # override the default of NIL_TREEHASH with NIL to match other wallet implementations - nil_recovery = Program.to(None) - singleton_struct = Program.to((SINGLETON_TOP_LAYER_MOD_HASH, (launcher_id, SINGLETON_LAUNCHER_PUZZLE_HASH))) - return DID_INNERPUZ_MOD.curry(p2_puzzle_or_hash, nil_recovery, 0, singleton_struct, metadata) - - if use_alternate_recovery: - with pytest.MonkeyPatch.context() as m: - m.setattr("chia.wallet.did_wallet.did_wallet_puzzles.create_innerpuz", alt_create_innerpuz) - did_wallet = await DIDWallet.create_new_did_wallet( - wallet_state_manager, wallet, uint64(101), action_scope, metadata=metadata, fee=fee - ) - else: - did_wallet = await DIDWallet.create_new_did_wallet( - wallet_state_manager, wallet, amount, action_scope, backups_ids=recovery_list, metadata=metadata, fee=fee - ) + did_wallet = await DIDWallet.create_new_did_wallet( + wallet_state_manager, wallet, amount, action_scope, metadata=metadata, fee=fee + ) return did_wallet @@ -94,16 +66,11 @@ def alt_create_innerpuz( "trusted", [True, False], ) -@pytest.mark.parametrize( - "use_alternate_recovery", - [True, False], -) @pytest.mark.anyio async def test_creation_from_coin_spend( self_hostname: str, two_nodes_two_wallets_with_same_keys: OldSimulatorsAndWallets, trusted: bool, - use_alternate_recovery: bool, ) -> None: """ Verify that DIDWallet.create_new_did_wallet_from_coin_spend() is called after Singleton creation on @@ -151,12 +118,10 @@ async def test_creation_from_coin_spend( wallet_0, uint64(101), action_scope, - use_alternate_recovery=use_alternate_recovery, ) with pytest.raises(RuntimeError): assert await did_wallet_0.get_coin() == set() - assert await did_wallet_0.get_info_for_recovery() is None await full_node_api.process_transaction_records(records=action_scope.side_effects.transactions) await full_node_api.wait_for_wallets_synced(wallet_nodes=[wallet_node_0, wallet_node_1]) @@ -187,15 +152,9 @@ async def test_creation_from_coin_spend( ], indirect=True, ) -@pytest.mark.parametrize( - "use_alternate_recovery", - [True, False], -) @pytest.mark.anyio @pytest.mark.limit_consensus_modes(reason="irrelevant") -async def test_creation_from_backup_file( - wallet_environments: WalletTestFramework, use_alternate_recovery: bool -) -> None: +async def test_creation_from_backup_file(wallet_environments: WalletTestFramework) -> None: env_0 = wallet_environments.environments[0] env_1 = wallet_environments.environments[1] env_2 = wallet_environments.environments[2] @@ -220,7 +179,6 @@ async def test_creation_from_backup_file( env_0.xch_wallet, uint64(101), action_scope, - use_alternate_recovery=use_alternate_recovery, ) await wallet_environments.process_pending_states( @@ -270,12 +228,9 @@ async def test_creation_from_backup_file( ] ) - # Wallet1 sets up DIDWallet_1 with DIDWallet_0 as backup - backup_ids = [bytes32.from_hexstr(did_wallet_0.get_my_DID())] - async with env_1.wallet_state_manager.new_action_scope(wallet_environments.tx_config, push=True) as action_scope: did_wallet_1: DIDWallet = await DIDWallet.create_new_did_wallet( - env_1.wallet_state_manager, env_1.xch_wallet, uint64(201), action_scope, backup_ids + env_1.wallet_state_manager, env_1.xch_wallet, uint64(201), action_scope ) await wallet_environments.process_pending_states( @@ -335,999 +290,149 @@ async def test_creation_from_backup_file( backup_data=backup_data, ) did_wallet_2 = env_2.wallet_state_manager.get_wallet(id=uint32(2), required_type=DIDWallet) - recovery_info = await env_2.rpc_client.did_get_recovery_info( - DIDGetRecoveryInfo(uint32(env_2.wallet_aliases["did"])) - ) - assert recovery_info.wallet_id == env_2.wallet_aliases["did"] - assert recovery_info.backup_dids == backup_ids current_coin_info_response = await env_0.rpc_client.did_get_current_coin_info( DIDGetCurrentCoinInfo(uint32(env_0.wallet_aliases["did"])) ) - # TODO: this check is kind of weak, we should research when this endpoint might actually be useful assert current_coin_info_response.wallet_id == env_0.wallet_aliases["did"] - async with env_0.wallet_state_manager.new_action_scope(wallet_environments.tx_config, push=True) as action_scope: - assert recovery_info.pubkey is not None - assert recovery_info.newpuzhash is not None - message_spend_bundle, attest_data = await did_wallet_0.create_attestment( - recovery_info.coin_name, recovery_info.newpuzhash, recovery_info.pubkey, action_scope - ) - - await wallet_environments.process_pending_states( - [ - WalletStateTransition( - pre_block_balance_updates={ - "did": { - "spendable_balance": -101, - "pending_change": 101, - "pending_coin_removal_count": 1, - "max_send_amount": -101, - } - }, - post_block_balance_updates={ - "did": { - "spendable_balance": 101, - "pending_change": -101, - "pending_coin_removal_count": -1, - "max_send_amount": 101, - } - }, - ), - WalletStateTransition( - pre_block_balance_updates={}, - post_block_balance_updates={}, - ), - WalletStateTransition( - pre_block_balance_updates={ - "did": { - "init": True, - } - }, - post_block_balance_updates={}, - ), - ] - ) - - ( - test_info_list, - test_message_spend_bundle, - ) = await did_wallet_2.load_attest_files_for_recovery_spend([attest_data]) - assert message_spend_bundle == test_message_spend_bundle - - async with env_2.wallet_state_manager.new_action_scope(wallet_environments.tx_config, push=True) as action_scope: - assert did_wallet_2.did_info.temp_coin is not None - await did_wallet_2.recovery_spend( - did_wallet_2.did_info.temp_coin, - recovery_info.newpuzhash, - test_info_list, - recovery_info.pubkey, - test_message_spend_bundle, - action_scope, - ) - - await wallet_environments.process_pending_states( - [ - WalletStateTransition( - pre_block_balance_updates={}, - post_block_balance_updates={}, - ), - WalletStateTransition( - pre_block_balance_updates={}, - post_block_balance_updates={ - "did": { - "confirmed_wallet_balance": -201, - "unconfirmed_wallet_balance": -201, - "spendable_balance": -201, - "max_send_amount": -201, - "unspent_coin_count": -1, - } - }, - ), - WalletStateTransition( - pre_block_balance_updates={ - "did": { - "unconfirmed_wallet_balance": 201, - "pending_coin_removal_count": 2, - } - }, - post_block_balance_updates={ - "did": { - "confirmed_wallet_balance": 201, - "spendable_balance": 201, - "max_send_amount": 201, - "unspent_coin_count": 1, - "pending_coin_removal_count": -2, - } - }, - ), - ] - ) for wallet in [did_wallet_0, did_wallet_1, did_wallet_2]: assert wallet.wallet_state_manager.wallets[wallet.id()] == wallet - some_ph = bytes32(32 * b"\2") - async with env_2.wallet_state_manager.new_action_scope(wallet_environments.tx_config, push=True) as action_scope: - await did_wallet_2.create_exit_spend(some_ph, action_scope) - - await wallet_environments.process_pending_states( - [ - WalletStateTransition( - pre_block_balance_updates={}, - post_block_balance_updates={}, - ), - WalletStateTransition( - pre_block_balance_updates={}, - post_block_balance_updates={}, - ), - WalletStateTransition( - pre_block_balance_updates={ - "did": { - "unconfirmed_wallet_balance": -201, - "spendable_balance": -201, - "max_send_amount": -201, - "pending_coin_removal_count": 1, - } - }, - post_block_balance_updates={ - "did": { - "confirmed_wallet_balance": -201, - "unspent_coin_count": -1, - "pending_coin_removal_count": -1, - } - }, - ), - ] - ) - - async def get_coins_with_ph() -> bool: - coins = await wallet_environments.full_node.full_node.coin_store.get_coin_records_by_puzzle_hash(True, some_ph) - return len(coins) == 1 - - await time_out_assert(15, get_coins_with_ph, True) - - for wallet in [did_wallet_0, did_wallet_1]: - assert wallet.wallet_state_manager.wallets[wallet.id()] == wallet - @pytest.mark.limit_consensus_modes(allowed=[ConsensusMode.PLAIN], reason="irrelevant") -@pytest.mark.parametrize("wallet_environments", [{"num_environments": 2, "blocks_needed": [1, 1]}], indirect=True) -@pytest.mark.parametrize( - "use_alternate_recovery", - [True, False], -) +@pytest.mark.parametrize("wallet_environments", [{"num_environments": 1, "blocks_needed": [1]}], indirect=True) @pytest.mark.anyio -async def test_did_recovery_with_multiple_backup_dids( - wallet_environments: WalletTestFramework, use_alternate_recovery: bool -) -> None: +async def test_did_find_lost_did(wallet_environments: WalletTestFramework): env_0 = wallet_environments.environments[0] - env_1 = wallet_environments.environments[1] wallet_node_0 = env_0.node - wallet_node_1 = env_1.node wallet_0 = env_0.xch_wallet - wallet_1 = env_1.xch_wallet env_0.wallet_aliases = { "xch": 1, "did": 2, } - env_1.wallet_aliases = { - "xch": 1, - "did": 2, - } async with wallet_0.wallet_state_manager.new_action_scope(wallet_environments.tx_config, push=True) as action_scope: - did_wallet: DIDWallet = await make_did_wallet( + did_wallet_0 = await make_did_wallet( wallet_node_0.wallet_state_manager, wallet_0, uint64(101), action_scope, - use_alternate_recovery=use_alternate_recovery, - ) - assert did_wallet.get_name() == "Profile 1" - recovery_list = [bytes32.from_hexstr(did_wallet.get_my_DID())] - - async with wallet_1.wallet_state_manager.new_action_scope(wallet_environments.tx_config, push=True) as action_scope: - did_wallet_2: DIDWallet = await DIDWallet.create_new_did_wallet( - wallet_node_1.wallet_state_manager, wallet_1, uint64(101), action_scope, recovery_list - ) - - await wallet_environments.process_pending_states( - [ - WalletStateTransition( - pre_block_balance_updates={ - "xch": { - "set_remainder": True, - }, - "did": { - "init": True, - "unconfirmed_wallet_balance": 101, - "pending_change": 101, - "pending_coin_removal_count": 1, - }, - }, - post_block_balance_updates={ - "xch": { - "set_remainder": True, - }, - "did": { - "confirmed_wallet_balance": 101, - "spendable_balance": 101, - "max_send_amount": 101, - "unspent_coin_count": 1, - "pending_change": -101, - "pending_coin_removal_count": -1, - }, - }, - ), - WalletStateTransition( - pre_block_balance_updates={ - "xch": { - "set_remainder": True, - }, - "did": { - "init": True, - "unconfirmed_wallet_balance": 101, - "pending_change": 101, - "pending_coin_removal_count": 1, - }, - }, - post_block_balance_updates={ - "xch": { - "set_remainder": True, - }, - "did": { - "confirmed_wallet_balance": 101, - "spendable_balance": 101, - "max_send_amount": 101, - "unspent_coin_count": 1, - "pending_change": -101, - "pending_coin_removal_count": -1, - }, - }, - ), - ] - ) - assert did_wallet_2.did_info.backup_ids == recovery_list - - recovery_list.append(bytes32.from_hexstr(did_wallet_2.get_my_DID())) - - async with wallet_1.wallet_state_manager.new_action_scope(wallet_environments.tx_config, push=True) as action_scope: - did_wallet_3: DIDWallet = await DIDWallet.create_new_did_wallet( - wallet_node_1.wallet_state_manager, wallet_1, uint64(201), action_scope, recovery_list - ) - - env_1.wallet_aliases["did_2"] = 3 - - await wallet_environments.process_pending_states( - [ - WalletStateTransition( - pre_block_balance_updates={}, - post_block_balance_updates={}, - ), - WalletStateTransition( - pre_block_balance_updates={ - "xch": { - "set_remainder": True, - }, - "did": { - "set_remainder": True, - }, - "did_2": { - "init": True, - "unconfirmed_wallet_balance": 201, - "pending_change": 201, - "pending_coin_removal_count": 1, - }, - }, - post_block_balance_updates={ - "xch": { - "set_remainder": True, - }, - "did": { - "set_remainder": True, - }, - "did_2": { - "confirmed_wallet_balance": 201, - "spendable_balance": 201, - "max_send_amount": 201, - "unspent_coin_count": 1, - "pending_change": -201, - "pending_coin_removal_count": -1, - }, - }, - ), - ] - ) - coin = await did_wallet_3.get_coin() - - backup_data = did_wallet_3.create_backup() - - async with wallet_node_0.wallet_state_manager.lock: - did_wallet_4 = await DIDWallet.create_new_did_wallet_from_recovery( - wallet_node_0.wallet_state_manager, - wallet_0, - backup_data, - ) - assert did_wallet_4.get_name() == "Profile 2" - env_0.wallet_aliases["did_2"] = 3 - - pubkey = (await did_wallet_4.wallet_state_manager.get_unused_derivation_record(did_wallet_2.wallet_info.id)).pubkey - new_ph = did_wallet_4.did_info.temp_puzhash - async with did_wallet.wallet_state_manager.new_action_scope( - wallet_environments.tx_config, push=True - ) as action_scope: - message_spend_bundle, attest1 = await did_wallet.create_attestment(coin.name(), new_ph, pubkey, action_scope) - - async with did_wallet_2.wallet_state_manager.new_action_scope( - wallet_environments.tx_config, push=True - ) as action_scope_2: - message_spend_bundle2, attest2 = await did_wallet_2.create_attestment( - coin.name(), new_ph, pubkey, action_scope_2 - ) - - message_spend_bundle = message_spend_bundle.aggregate([message_spend_bundle, message_spend_bundle2]) - - ( - test_info_list, - test_message_spend_bundle, - ) = await did_wallet_4.load_attest_files_for_recovery_spend([attest1, attest2]) - assert message_spend_bundle == test_message_spend_bundle - - await wallet_environments.process_pending_states( - [ - WalletStateTransition( - pre_block_balance_updates={ - "did": { - "spendable_balance": -101, - "pending_change": 101, - "max_send_amount": -101, - "pending_coin_removal_count": 1, - "set_remainder": True, - }, - "did_2": { - "init": True, - "unconfirmed_wallet_balance": 0, - "pending_change": 0, - "pending_coin_removal_count": 0, - }, - }, - post_block_balance_updates={ - "did": { - "spendable_balance": 101, - "pending_change": -101, - "max_send_amount": 101, - "pending_coin_removal_count": -1, - }, - "did_2": { - "confirmed_wallet_balance": 0, - "spendable_balance": 0, - "max_send_amount": 0, - "unspent_coin_count": 0, - "pending_change": 0, - "pending_coin_removal_count": 0, - }, - }, - ), - WalletStateTransition( - pre_block_balance_updates={ - "did": { - "spendable_balance": -101, - "pending_change": 101, - "max_send_amount": -101, - "pending_coin_removal_count": 1, - "set_remainder": True, - }, - }, - post_block_balance_updates={ - "did": { - "spendable_balance": 101, - "pending_change": -101, - "max_send_amount": 101, - "pending_coin_removal_count": -1, - }, - }, - ), - ] - ) - - async with did_wallet_4.wallet_state_manager.new_action_scope( - wallet_environments.tx_config, push=True - ) as action_scope: - await did_wallet_4.recovery_spend(coin, new_ph, test_info_list, pubkey, message_spend_bundle, action_scope) - - await wallet_environments.process_pending_states( - [ - WalletStateTransition( - pre_block_balance_updates={ - "did_2": { - "unconfirmed_wallet_balance": 201, - "pending_change": 0, - "pending_coin_removal_count": 3, - }, - }, - post_block_balance_updates={ - "did_2": { - "confirmed_wallet_balance": 201, - "spendable_balance": 201, - "max_send_amount": 201, - "unspent_coin_count": 1, - "pending_change": 0, - "pending_coin_removal_count": -3, - }, - }, - ), - WalletStateTransition( - pre_block_balance_updates={ - "did_2": { - "unconfirmed_wallet_balance": 0, # TODO: fix pre-block balances for recovery - "spendable_balance": 0, - "pending_change": 0, - "max_send_amount": 0, - "pending_coin_removal_count": 0, - "set_remainder": True, - }, - }, - post_block_balance_updates={ - "did_2": { - "confirmed_wallet_balance": -201, - "spendable_balance": -201, - "max_send_amount": -201, - "unspent_coin_count": -1, - "set_remainder": True, - }, - }, - ), - ] - ) - - for wallet in [did_wallet, did_wallet_2, did_wallet_3, did_wallet_4]: - assert wallet.wallet_state_manager.wallets[wallet.id()] == wallet - - -@pytest.mark.limit_consensus_modes(allowed=[ConsensusMode.PLAIN], reason="irrelevant") -@pytest.mark.parametrize("wallet_environments", [{"num_environments": 1, "blocks_needed": [1]}], indirect=True) -@pytest.mark.parametrize( - "use_alternate_recovery", - [True, False], -) -@pytest.mark.anyio -async def test_did_recovery_with_empty_set(wallet_environments: WalletTestFramework, use_alternate_recovery: bool): - env_0 = wallet_environments.environments[0] - wallet_node_0 = env_0.node - wallet_0 = env_0.xch_wallet - - env_0.wallet_aliases = { - "xch": 1, - "did": 2, - } - - async with wallet_0.wallet_state_manager.new_action_scope(wallet_environments.tx_config, push=True) as action_scope: - ph = await action_scope.get_puzzle_hash(wallet_0.wallet_state_manager) - - async with wallet_0.wallet_state_manager.new_action_scope(DEFAULT_TX_CONFIG, push=True) as action_scope: - did_wallet: DIDWallet = await make_did_wallet( - wallet_node_0.wallet_state_manager, - wallet_0, - uint64(101), - action_scope, - use_alternate_recovery=use_alternate_recovery, - ) - - await wallet_environments.process_pending_states( - [ - WalletStateTransition( - pre_block_balance_updates={ - "xch": { - "set_remainder": True, - }, - "did": { - "init": True, - "unconfirmed_wallet_balance": 101, - "pending_change": 101, - "pending_coin_removal_count": 1, - }, - }, - post_block_balance_updates={ - "xch": { - "set_remainder": True, - }, - "did": { - "confirmed_wallet_balance": 101, - "spendable_balance": 101, - "max_send_amount": 101, - "unspent_coin_count": 1, - "pending_change": -101, - "pending_coin_removal_count": -1, - }, - }, - ), - ] - ) - coin = await did_wallet.get_coin() - info: list[tuple[bytes, bytes, int]] = [] - pubkey = (await did_wallet.wallet_state_manager.get_unused_derivation_record(did_wallet.wallet_info.id)).pubkey - with pytest.raises(Exception): # We expect a CLVM 80 error for this test - async with did_wallet.wallet_state_manager.new_action_scope( - wallet_environments.tx_config, push=False - ) as action_scope: - await did_wallet.recovery_spend( - coin, - ph, - info, - pubkey, - WalletSpendBundle([], AugSchemeMPL.aggregate([])), - action_scope, - ) - - -@pytest.mark.limit_consensus_modes(allowed=[ConsensusMode.PLAIN], reason="irrelevant") -@pytest.mark.parametrize("wallet_environments", [{"num_environments": 1, "blocks_needed": [1]}], indirect=True) -@pytest.mark.parametrize( - "use_alternate_recovery", - [True, False], -) -@pytest.mark.anyio -async def test_did_find_lost_did(wallet_environments: WalletTestFramework, use_alternate_recovery: bool): - env_0 = wallet_environments.environments[0] - wallet_node_0 = env_0.node - wallet_0 = env_0.xch_wallet - - env_0.wallet_aliases = { - "xch": 1, - "did": 2, - } - - async with wallet_0.wallet_state_manager.new_action_scope(wallet_environments.tx_config, push=True) as action_scope: - did_wallet_0 = await make_did_wallet( - wallet_node_0.wallet_state_manager, - wallet_0, - uint64(101), - action_scope, - use_alternate_recovery=use_alternate_recovery, - ) - - await wallet_environments.process_pending_states( - [ - WalletStateTransition( - pre_block_balance_updates={ - "xch": { - "set_remainder": True, - }, - "did": { - "init": True, - "unconfirmed_wallet_balance": 101, - "pending_change": 101, - "pending_coin_removal_count": 1, - }, - }, - post_block_balance_updates={ - "xch": { - "set_remainder": True, - }, - "did": { - "confirmed_wallet_balance": 101, - "spendable_balance": 101, - "max_send_amount": 101, - "unspent_coin_count": 1, - "pending_change": -101, - "pending_coin_removal_count": -1, - }, - }, - ), - ] - ) - - # Delete the coin and wallet - coin = await did_wallet_0.get_coin() - await wallet_node_0.wallet_state_manager.coin_store.delete_coin_record(coin.name()) - await wallet_node_0.wallet_state_manager.delete_wallet(did_wallet_0.wallet_info.id) - wallet_node_0.wallet_state_manager.wallets.pop(did_wallet_0.wallet_info.id) - assert len(wallet_node_0.wallet_state_manager.wallets) == 1 - # Find lost DID - assert did_wallet_0.did_info.origin_coin is not None # mypy - await env_0.rpc_client.find_lost_did(DIDFindLostDID(did_wallet_0.did_info.origin_coin.name().hex())) - did_wallets = list( - filter( - lambda w: (w.type == WalletType.DECENTRALIZED_ID), - await wallet_node_0.wallet_state_manager.get_all_wallet_info_entries(), - ) - ) - did_wallet = wallet_node_0.wallet_state_manager.wallets[did_wallets[0].id] - assert isinstance(did_wallet, DIDWallet) - env_0.wallet_aliases["did_found"] = did_wallets[0].id - await env_0.change_balances( - { - "did_found": { - "init": True, - "confirmed_wallet_balance": 101, - "unconfirmed_wallet_balance": 101, - "spendable_balance": 101, - "max_send_amount": 101, - "unspent_coin_count": 1, - } - } - ) - await env_0.check_balances() - - # Spend DID - recovery_list = [bytes32.fromhex(did_wallet.get_my_DID())] - await did_wallet.update_recovery_list(recovery_list, uint64(1)) - assert did_wallet.did_info.backup_ids == recovery_list - async with did_wallet.wallet_state_manager.new_action_scope( - wallet_environments.tx_config, push=True - ) as action_scope: - await did_wallet.create_update_spend(action_scope) - - await wallet_environments.process_pending_states( - [ - WalletStateTransition( - pre_block_balance_updates={ - "did_found": { - "spendable_balance": -101, - "max_send_amount": -101, - "pending_change": 101, - "pending_coin_removal_count": 1, - }, - }, - post_block_balance_updates={ - "did_found": { - "spendable_balance": 101, - "max_send_amount": 101, - "pending_change": -101, - "pending_coin_removal_count": -1, - }, - }, - ), - ] - ) - - # Delete the coin and change inner puzzle - coin = await did_wallet.get_coin() - await wallet_node_0.wallet_state_manager.coin_store.delete_coin_record(coin.name()) - with wallet_environments.new_puzzle_hashes_allowed(): - async with did_wallet.wallet_state_manager.new_action_scope( - wallet_environments.tx_config, push=True - ) as action_scope: - new_inner_puzzle = await did_wallet.get_did_innerpuz(action_scope, override_reuse_puzhash_with=False) - did_wallet.did_info = dataclasses.replace(did_wallet.did_info, current_inner=new_inner_puzzle) - # Recovery the coin - assert did_wallet.did_info.origin_coin is not None # mypy - await env_0.rpc_client.find_lost_did(DIDFindLostDID(did_wallet.did_info.origin_coin.name().hex())) - found_coin = await did_wallet.get_coin() - assert found_coin == coin - assert did_wallet.did_info.current_inner != new_inner_puzzle - - -@pytest.mark.limit_consensus_modes(allowed=[ConsensusMode.PLAIN], reason="irrelevant") -@pytest.mark.parametrize("wallet_environments", [{"num_environments": 2, "blocks_needed": [1, 1]}], indirect=True) -@pytest.mark.parametrize( - "use_alternate_recovery", - [True, False], -) -@pytest.mark.anyio -async def test_did_attest_after_recovery(wallet_environments: WalletTestFramework, use_alternate_recovery: bool): - env_0 = wallet_environments.environments[0] - env_1 = wallet_environments.environments[1] - wallet_node_0 = env_0.node - wallet_node_1 = env_1.node - wallet_0 = env_0.xch_wallet - wallet_1 = env_1.xch_wallet - - env_0.wallet_aliases = { - "xch": 1, - "did": 2, - } - env_1.wallet_aliases = { - "xch": 1, - "did": 2, - } - - async with wallet_0.wallet_state_manager.new_action_scope(wallet_environments.tx_config, push=True) as action_scope: - did_wallet: DIDWallet = await make_did_wallet( - wallet_node_0.wallet_state_manager, - wallet_0, - uint64(101), - action_scope, - use_alternate_recovery=use_alternate_recovery, - ) - await wallet_environments.process_pending_states( - [ - WalletStateTransition( - pre_block_balance_updates={ - "xch": { - "set_remainder": True, - }, - "did": { - "init": True, - "unconfirmed_wallet_balance": 101, - "pending_change": 101, - "pending_coin_removal_count": 1, - }, - }, - post_block_balance_updates={ - "xch": { - "set_remainder": True, - }, - "did": { - "confirmed_wallet_balance": 101, - "spendable_balance": 101, - "max_send_amount": 101, - "unspent_coin_count": 1, - "pending_change": -101, - "pending_coin_removal_count": -1, - }, - }, - ), - WalletStateTransition(), - ] - ) - - await time_out_assert(15, did_wallet.get_confirmed_balance, 101) - await time_out_assert(15, did_wallet.get_unconfirmed_balance, 101) - recovery_list = [bytes32.from_hexstr(did_wallet.get_my_DID())] - - async with wallet_1.wallet_state_manager.new_action_scope(wallet_environments.tx_config, push=True) as action_scope: - did_wallet_2: DIDWallet = await DIDWallet.create_new_did_wallet( - wallet_node_1.wallet_state_manager, wallet_1, uint64(101), action_scope, recovery_list - ) - await wallet_environments.process_pending_states( - [ - WalletStateTransition(), - WalletStateTransition( - pre_block_balance_updates={ - "xch": { - "set_remainder": True, - }, - "did": { - "init": True, - "unconfirmed_wallet_balance": 101, - "pending_change": 101, - "pending_coin_removal_count": 1, - }, - }, - post_block_balance_updates={ - "xch": { - "set_remainder": True, - }, - "did": { - "confirmed_wallet_balance": 101, - "spendable_balance": 101, - "max_send_amount": 101, - "unspent_coin_count": 1, - "pending_change": -101, - "pending_coin_removal_count": -1, - }, - }, - ), - ] - ) - - assert did_wallet_2.did_info.backup_ids == recovery_list - - # Update coin with new ID info - recovery_list = [bytes32.from_hexstr(did_wallet_2.get_my_DID())] - await did_wallet.update_recovery_list(recovery_list, uint64(1)) - assert did_wallet.did_info.backup_ids == recovery_list - async with did_wallet.wallet_state_manager.new_action_scope( - wallet_environments.tx_config, push=True - ) as action_scope: - await did_wallet.create_update_spend(action_scope) - - await wallet_environments.process_pending_states( - [ - WalletStateTransition( - pre_block_balance_updates={ - "did": { - "set_remainder": True, - } - }, - post_block_balance_updates={ - "did": { - "set_remainder": True, - } - }, - ), - WalletStateTransition(), - ] - ) - - # DID Wallet 2 recovers into DID Wallet 3 with new innerpuz - backup_data = did_wallet_2.create_backup() - - async with wallet_node_0.wallet_state_manager.lock: - did_wallet_3 = await DIDWallet.create_new_did_wallet_from_recovery( - wallet_node_0.wallet_state_manager, - wallet_0, - backup_data, - ) - env_0.wallet_aliases["did_2"] = 3 - with wallet_environments.new_puzzle_hashes_allowed(): - async with did_wallet_3.wallet_state_manager.new_action_scope( - wallet_environments.tx_config, push=True - ) as action_scope: - new_ph = ( - await did_wallet_3.get_did_innerpuz(action_scope, override_reuse_puzhash_with=False) - ).get_tree_hash() - coin = await did_wallet_2.get_coin() - pubkey = (await did_wallet_3.wallet_state_manager.get_unused_derivation_record(did_wallet_3.wallet_info.id)).pubkey - - async with did_wallet.wallet_state_manager.new_action_scope( - wallet_environments.tx_config, push=True - ) as action_scope: - message_spend_bundle, attest_data = await did_wallet.create_attestment( - coin.name(), new_ph, pubkey, action_scope - ) - await wallet_environments.process_pending_states( - [ - WalletStateTransition( - pre_block_balance_updates={ - "did": { - "set_remainder": True, - }, - "did_2": { - "init": True, - "set_remainder": True, - }, - }, - post_block_balance_updates={ - "did": { - "set_remainder": True, - }, - "did_2": { - "set_remainder": True, - }, - }, - ), - WalletStateTransition(), - ] - ) - - ( - info, - message_spend_bundle, - ) = await did_wallet_3.load_attest_files_for_recovery_spend([attest_data]) - async with did_wallet_3.wallet_state_manager.new_action_scope(DEFAULT_TX_CONFIG, push=True) as action_scope: - await did_wallet_3.recovery_spend(coin, new_ph, info, pubkey, message_spend_bundle, action_scope) - await wallet_environments.process_pending_states( - [ - WalletStateTransition( - pre_block_balance_updates={ - "did_2": { - "unconfirmed_wallet_balance": 101, - "set_remainder": True, - }, - }, - post_block_balance_updates={ - "did_2": { - "confirmed_wallet_balance": 101, - "set_remainder": True, - }, - }, - ), - WalletStateTransition( - pre_block_balance_updates={ - "did": { - "unconfirmed_wallet_balance": 0, # TODO: fix pre-block balances for recovery - "set_remainder": True, - }, - }, - post_block_balance_updates={ - "did": { - "confirmed_wallet_balance": -101, - "set_remainder": True, - }, - }, - ), - ] - ) - - # DID Wallet 1 recovery spends into DID Wallet 4 - backup_data = did_wallet.create_backup() - - async with wallet_node_1.wallet_state_manager.lock: - did_wallet_4 = await DIDWallet.create_new_did_wallet_from_recovery( - wallet_node_1.wallet_state_manager, - wallet_1, - backup_data, - ) - env_1.wallet_aliases["did_2"] = 3 - coin = await did_wallet.get_coin() - with wallet_environments.new_puzzle_hashes_allowed(): - async with did_wallet_4.wallet_state_manager.new_action_scope( - wallet_environments.tx_config, push=True - ) as action_scope: - new_ph = ( - await did_wallet_4.get_did_innerpuz(action_scope, override_reuse_puzhash_with=False) - ).get_tree_hash() - pubkey = (await did_wallet_4.wallet_state_manager.get_unused_derivation_record(did_wallet_4.wallet_info.id)).pubkey - async with did_wallet_3.wallet_state_manager.new_action_scope( - wallet_environments.tx_config, push=True - ) as action_scope: - message_spend_bundle, attest1 = await did_wallet_3.create_attestment(coin.name(), new_ph, pubkey, action_scope) - - await wallet_environments.process_pending_states( - [ - WalletStateTransition( - pre_block_balance_updates={ - "did_2": { - "unconfirmed_wallet_balance": 0, - "set_remainder": True, - }, - }, - post_block_balance_updates={ - "did_2": { - "confirmed_wallet_balance": 0, - "set_remainder": True, - }, - }, - ), - WalletStateTransition( - pre_block_balance_updates={ - "did_2": { - "init": True, - "set_remainder": True, - }, - }, - post_block_balance_updates={ - "did_2": { - "confirmed_wallet_balance": 0, - "set_remainder": True, - }, - }, - ), - ] - ) - - ( - test_info_list, - test_message_spend_bundle, - ) = await did_wallet_4.load_attest_files_for_recovery_spend([attest1]) - async with did_wallet_4.wallet_state_manager.new_action_scope(DEFAULT_TX_CONFIG, push=True) as action_scope: - await did_wallet_4.recovery_spend(coin, new_ph, test_info_list, pubkey, test_message_spend_bundle, action_scope) + ) await wallet_environments.process_pending_states( [ WalletStateTransition( pre_block_balance_updates={ - "did": { + "xch": { "set_remainder": True, }, + "did": { + "init": True, + "unconfirmed_wallet_balance": 101, + "pending_change": 101, + "pending_coin_removal_count": 1, + }, }, post_block_balance_updates={ - "did": { - "confirmed_wallet_balance": -101, + "xch": { "set_remainder": True, }, + "did": { + "confirmed_wallet_balance": 101, + "spendable_balance": 101, + "max_send_amount": 101, + "unspent_coin_count": 1, + "pending_change": -101, + "pending_coin_removal_count": -1, + }, }, ), + ] + ) + + # Delete the coin and wallet + coin = await did_wallet_0.get_coin() + await wallet_node_0.wallet_state_manager.coin_store.delete_coin_record(coin.name()) + await wallet_node_0.wallet_state_manager.delete_wallet(did_wallet_0.wallet_info.id) + wallet_node_0.wallet_state_manager.wallets.pop(did_wallet_0.wallet_info.id) + assert len(wallet_node_0.wallet_state_manager.wallets) == 1 + # Find lost DID + assert did_wallet_0.did_info.origin_coin is not None # mypy + await env_0.rpc_client.find_lost_did(DIDFindLostDID(did_wallet_0.did_info.origin_coin.name().hex())) + did_wallets = list( + filter( + lambda w: (w.type == WalletType.DECENTRALIZED_ID), + await wallet_node_0.wallet_state_manager.get_all_wallet_info_entries(), + ) + ) + did_wallet = wallet_node_0.wallet_state_manager.wallets[did_wallets[0].id] + assert isinstance(did_wallet, DIDWallet) + env_0.wallet_aliases["did_found"] = did_wallets[0].id + await env_0.change_balances( + { + "did_found": { + "init": True, + "confirmed_wallet_balance": 101, + "unconfirmed_wallet_balance": 101, + "spendable_balance": 101, + "max_send_amount": 101, + "unspent_coin_count": 1, + } + } + ) + await env_0.check_balances() + + # Spend DID + async with did_wallet.wallet_state_manager.new_action_scope( + wallet_environments.tx_config, push=True + ) as action_scope: + await did_wallet.create_update_spend(action_scope) + + await wallet_environments.process_pending_states( + [ WalletStateTransition( pre_block_balance_updates={ - "did_2": { - "set_remainder": True, + "did_found": { + "spendable_balance": -101, + "max_send_amount": -101, + "pending_change": 101, + "pending_coin_removal_count": 1, }, }, post_block_balance_updates={ - "did_2": { - "confirmed_wallet_balance": 101, - "set_remainder": True, + "did_found": { + "spendable_balance": 101, + "max_send_amount": 101, + "pending_change": -101, + "pending_coin_removal_count": -1, }, }, ), ] ) - for wallet in [did_wallet, did_wallet_3, did_wallet_4]: - assert wallet.wallet_state_manager.wallets[wallet.id()] == wallet + # Delete the coin and change inner puzzle + coin = await did_wallet.get_coin() + await wallet_node_0.wallet_state_manager.coin_store.delete_coin_record(coin.name()) + with wallet_environments.new_puzzle_hashes_allowed(): + async with did_wallet.wallet_state_manager.new_action_scope( + wallet_environments.tx_config, push=True + ) as action_scope: + new_inner_puzzle = await did_wallet.get_did_innerpuz(action_scope, override_reuse_puzhash_with=False) + did_wallet.did_info = dataclasses.replace(did_wallet.did_info, current_inner=new_inner_puzzle) + # Recovery the coin + assert did_wallet.did_info.origin_coin is not None # mypy + await env_0.rpc_client.find_lost_did(DIDFindLostDID(did_wallet.did_info.origin_coin.name().hex())) + found_coin = await did_wallet.get_coin() + assert found_coin == coin + assert did_wallet.did_info.current_inner != new_inner_puzzle @pytest.mark.limit_consensus_modes(allowed=[ConsensusMode.PLAIN], reason="irrelevant") @pytest.mark.parametrize("wallet_environments", [{"num_environments": 2, "blocks_needed": [1, 1]}], indirect=True) -@pytest.mark.parametrize( - "with_recovery", - [True, False], -) @pytest.mark.anyio -async def test_did_transfer(wallet_environments: WalletTestFramework, with_recovery: bool): +async def test_did_transfer(wallet_environments: WalletTestFramework): env_0 = wallet_environments.environments[0] env_1 = wallet_environments.environments[1] wallet_node_0 = env_0.node @@ -1343,8 +448,6 @@ async def test_did_transfer(wallet_environments: WalletTestFramework, with_recov "xch": 1, "did": 2, } - async with wallet_0.wallet_state_manager.new_action_scope(wallet_environments.tx_config, push=True) as action_scope: - ph = await action_scope.get_puzzle_hash(wallet_0.wallet_state_manager) fee = uint64(1000) async with wallet_0.wallet_state_manager.new_action_scope(wallet_environments.tx_config, push=True) as action_scope: @@ -1353,8 +456,6 @@ async def test_did_transfer(wallet_environments: WalletTestFramework, with_recov wallet_0, uint64(101), action_scope, - [ph], - uint64(1), {"Twitter": "Test", "GitHub": "测试"}, fee=fee, ) @@ -1398,7 +499,7 @@ async def test_did_transfer(wallet_environments: WalletTestFramework, with_recov async with did_wallet_1.wallet_state_manager.new_action_scope( wallet_environments.tx_config, push=True ) as action_scope: - await did_wallet_1.transfer_did(new_puzhash, fee, with_recovery, action_scope) + await did_wallet_1.transfer_did(new_puzhash, fee, action_scope) await wallet_environments.process_pending_states( [ @@ -1442,9 +543,6 @@ async def test_did_transfer(wallet_environments: WalletTestFramework, with_recov assert isinstance(did_wallet_2, DIDWallet) # mypy assert len(wallet_node_0.wallet_state_manager.wallets) == 1 assert did_wallet_1.did_info.origin_coin == did_wallet_2.did_info.origin_coin - if with_recovery: - assert did_wallet_1.did_info.backup_ids[0] == did_wallet_2.did_info.backup_ids[0] - assert did_wallet_1.did_info.num_of_backup_ids_needed == did_wallet_2.did_info.num_of_backup_ids_needed metadata = json.loads(did_wallet_2.did_info.metadata) assert metadata["Twitter"] == "Test" assert metadata["GitHub"] == "测试" @@ -1475,8 +573,6 @@ async def test_did_auto_transfer_limit( wallet = wallet_node.wallet_state_manager.main_wallet wallet2 = wallet_node_2.wallet_state_manager.main_wallet api_1 = WalletRpcApi(wallet_node_2) - async with wallet.wallet_state_manager.new_action_scope(DEFAULT_TX_CONFIG, push=True) as action_scope: - ph = await action_scope.get_puzzle_hash(wallet.wallet_state_manager) if trusted: wallet_node.config["trusted_peers"] = { @@ -1501,8 +597,6 @@ async def test_did_auto_transfer_limit( wallet, uint64(101), action_scope, - [bytes32(bytes(ph))], - uint64(1), {"Twitter": "Test", "GitHub": "测试"}, fee=fee, ) @@ -1517,7 +611,7 @@ async def test_did_auto_transfer_limit( async with wallet2.wallet_state_manager.new_action_scope(DEFAULT_TX_CONFIG, push=True) as action_scope: new_puzhash = await action_scope.get_puzzle_hash(wallet2.wallet_state_manager) async with did_wallet_1.wallet_state_manager.new_action_scope(DEFAULT_TX_CONFIG, push=True) as action_scope: - await did_wallet_1.transfer_did(new_puzhash, fee, False, action_scope) + await did_wallet_1.transfer_did(new_puzhash, fee, action_scope) await full_node_api.process_transaction_records(records=action_scope.side_effects.transactions) await full_node_api.wait_for_wallets_synced(wallet_nodes=[wallet_node, wallet_node_2]) # Check if the DID wallet is created in the wallet2 @@ -1587,14 +681,11 @@ async def test_did_auto_transfer_limit( # Check we can still manually add new DIDs while at cap await full_node_api.farm_blocks_to_wallet(1, wallet2) async with wallet2.wallet_state_manager.new_action_scope(DEFAULT_TX_CONFIG, push=True) as action_scope: - ph = await action_scope.get_puzzle_hash(wallet2.wallet_state_manager) did_wallet_11: DIDWallet = await DIDWallet.create_new_did_wallet( wallet_node_2.wallet_state_manager, wallet2, uint64(101), action_scope, - [bytes32(bytes(ph))], - uint64(1), {"Twitter": "Test", "GitHub": "测试"}, fee=fee, ) @@ -1612,97 +703,10 @@ async def test_did_auto_transfer_limit( assert len(did_wallets) == 11 -@pytest.mark.limit_consensus_modes(allowed=[ConsensusMode.PLAIN], reason="irrelevant") -@pytest.mark.parametrize("wallet_environments", [{"num_environments": 1, "blocks_needed": [1]}], indirect=True) -@pytest.mark.parametrize( - "use_alternate_recovery", - [True, False], -) -@pytest.mark.anyio -async def test_update_recovery_list(wallet_environments: WalletTestFramework, use_alternate_recovery: bool): - env = wallet_environments.environments[0] - wallet_node = env.node - wallet = env.xch_wallet - - env.wallet_aliases = { - "xch": 1, - "did": 2, - } - - async with wallet.wallet_state_manager.new_action_scope(wallet_environments.tx_config, push=True) as action_scope: - ph = await action_scope.get_puzzle_hash(wallet.wallet_state_manager) - did_wallet_1: DIDWallet = await make_did_wallet( - wallet_node.wallet_state_manager, - wallet, - uint64(101), - action_scope, - use_alternate_recovery=use_alternate_recovery, - ) - - await wallet_environments.process_pending_states( - [ - WalletStateTransition( - pre_block_balance_updates={ - "xch": { - "set_remainder": True, - }, - "did": { - "init": True, - "unconfirmed_wallet_balance": 101, - "pending_change": 101, - "pending_coin_removal_count": 1, - }, - }, - post_block_balance_updates={ - "xch": { - "set_remainder": True, - }, - "did": { - "confirmed_wallet_balance": 101, - "spendable_balance": 101, - "max_send_amount": 101, - "unspent_coin_count": 1, - "pending_change": -101, - "pending_coin_removal_count": -1, - }, - }, - ), - ] - ) - await did_wallet_1.update_recovery_list([ph], uint64(1)) - async with did_wallet_1.wallet_state_manager.new_action_scope( - wallet_environments.tx_config, push=True - ) as action_scope: - await did_wallet_1.create_update_spend(action_scope) - - await wallet_environments.process_pending_states( - [ - WalletStateTransition( - pre_block_balance_updates={ - "did": { - "set_remainder": True, - }, - }, - post_block_balance_updates={ - "did": { - "set_remainder": True, - }, - }, - ), - ] - ) - assert did_wallet_1.did_info.backup_ids[0] == bytes(ph) - assert did_wallet_1.did_info.num_of_backup_ids_needed == 1 - - @pytest.mark.limit_consensus_modes(allowed=[ConsensusMode.PLAIN], reason="irrelevant") @pytest.mark.parametrize("wallet_environments", [{"num_environments": 2, "blocks_needed": [1, 1]}], indirect=True) -@pytest.mark.parametrize( - "use_alternate_recovery", - [True, False], -) @pytest.mark.anyio -async def test_get_info(wallet_environments: WalletTestFramework, use_alternate_recovery: bool): +async def test_get_info(wallet_environments: WalletTestFramework): env_0 = wallet_environments.environments[0] env_1 = wallet_environments.environments[1] wallet_node_0 = env_0.node @@ -1730,10 +734,8 @@ async def test_get_info(wallet_environments: WalletTestFramework, use_alternate_ wallet_0, did_amount, action_scope, - [], metadata={"twitter": "twitter"}, fee=fee, - use_alternate_recovery=use_alternate_recovery, ) await wallet_environments.process_pending_states( @@ -1781,10 +783,7 @@ async def test_get_info(wallet_environments: WalletTestFramework, use_alternate_ assert response.metadata["twitter"] == "twitter" assert response.latest_coin == (await did_wallet_1.get_coin()).name() assert response.num_verification == 0 - if use_alternate_recovery: - assert response.recovery_list_hash is None - else: - assert response.recovery_list_hash == Program(Program.to([])).get_tree_hash() + assert response.recovery_list_hash == Program(Program.to([])).get_tree_hash() assert decode_puzzle_hash(response.p2_address) == response.hints[0] # Test non-singleton coin @@ -1852,12 +851,8 @@ async def test_get_info(wallet_environments: WalletTestFramework, use_alternate_ @pytest.mark.limit_consensus_modes(allowed=[ConsensusMode.PLAIN], reason="irrelevant") @pytest.mark.parametrize("wallet_environments", [{"num_environments": 1, "blocks_needed": [1]}], indirect=True) -@pytest.mark.parametrize( - "use_alternate_recovery", - [True, False], -) @pytest.mark.anyio -async def test_message_spend(wallet_environments: WalletTestFramework, use_alternate_recovery: bool): +async def test_message_spend(wallet_environments: WalletTestFramework): env = wallet_environments.environments[0] wallet_node = env.node wallet = env.xch_wallet @@ -1876,9 +871,7 @@ async def test_message_spend(wallet_environments: WalletTestFramework, use_alter wallet, uint64(101), action_scope, - [], fee=fee, - use_alternate_recovery=use_alternate_recovery, ) await wallet_environments.process_pending_states( [ @@ -1926,12 +919,8 @@ async def test_message_spend(wallet_environments: WalletTestFramework, use_alter @pytest.mark.limit_consensus_modes(allowed=[ConsensusMode.PLAIN], reason="irrelevant") @pytest.mark.parametrize("wallet_environments", [{"num_environments": 1, "blocks_needed": [1]}], indirect=True) -@pytest.mark.parametrize( - "use_alternate_recovery", - [True, False], -) @pytest.mark.anyio -async def test_update_metadata(wallet_environments: WalletTestFramework, use_alternate_recovery: bool): +async def test_update_metadata(wallet_environments: WalletTestFramework): env = wallet_environments.environments[0] wallet_node = env.node wallet = env.xch_wallet @@ -1950,9 +939,7 @@ async def test_update_metadata(wallet_environments: WalletTestFramework, use_alt wallet, did_amount, action_scope, - [], fee=fee, - use_alternate_recovery=use_alternate_recovery, ) await wallet_environments.process_pending_states( @@ -2052,14 +1039,11 @@ async def test_did_sign_message(wallet_environments: WalletTestFramework): fee = uint64(1000) async with wallet.wallet_state_manager.new_action_scope(wallet_environments.tx_config, push=True) as action_scope: - ph = await action_scope.get_puzzle_hash(wallet.wallet_state_manager) did_wallet_1: DIDWallet = await DIDWallet.create_new_did_wallet( wallet_node.wallet_state_manager, wallet, uint64(101), action_scope, - [ph], - uint64(1), {"Twitter": "Test", "GitHub": "测试"}, fee=fee, ) @@ -2164,93 +1148,6 @@ async def test_did_sign_message(wallet_environments: WalletTestFramework): ) -@pytest.mark.parametrize( - "trusted", - [True, False], -) -@pytest.mark.anyio -async def test_create_did_with_recovery_list( - self_hostname: str, two_nodes_two_wallets_with_same_keys: OldSimulatorsAndWallets, trusted: bool -) -> None: - """ - A DID is created on-chain in client0, causing a DID Wallet to be created in client1, which shares the same key. - This can happen if someone uses the same key on multiple computers, or is syncing a wallet from scratch. - - For this test, we assign a recovery list hash at DID creation time, but the recovery list is not yet available - to the wallet_node that the DID Wallet is being created in (client1). - - """ - full_nodes, wallets, _ = two_nodes_two_wallets_with_same_keys - full_node_api = full_nodes[0] - full_node_server = full_node_api.server - wallet_node_0, server_0 = wallets[0] - wallet_node_1, server_1 = wallets[1] - - wallet_0 = wallet_node_0.wallet_state_manager.main_wallet - wallet_1 = wallet_node_1.wallet_state_manager.main_wallet - - async with wallet_0.wallet_state_manager.new_action_scope(DEFAULT_TX_CONFIG, push=True) as action_scope: - ph0 = await action_scope.get_puzzle_hash(wallet_0.wallet_state_manager) - async with wallet_1.wallet_state_manager.new_action_scope(DEFAULT_TX_CONFIG, push=True) as action_scope: - ph1 = await action_scope.get_puzzle_hash(wallet_1.wallet_state_manager) - - sk0 = await wallet_node_0.wallet_state_manager.get_private_key(ph0) - sk1 = await wallet_node_1.wallet_state_manager.get_private_key(ph1) - assert sk0 == sk1 - - if trusted: - wallet_node_0.config["trusted_peers"] = { - full_node_api.full_node.server.node_id.hex(): full_node_api.full_node.server.node_id.hex() - } - wallet_node_1.config["trusted_peers"] = { - full_node_api.full_node.server.node_id.hex(): full_node_api.full_node.server.node_id.hex() - } - - else: - wallet_node_0.config["trusted_peers"] = {} - wallet_node_1.config["trusted_peers"] = {} - await server_0.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None) - await server_1.start_client(PeerInfo(self_hostname, full_node_server.get_port()), None) - - await full_node_api.farm_blocks_to_wallet(1, wallet_0) - await full_node_api.farm_blocks_to_wallet(1, wallet_1) - - # Node 0 sets up a DID Wallet with a backup set, but num_of_backup_ids_needed=0 - # (a malformed solution, but legal for the clvm puzzle) - recovery_list = [bytes32(bytes.fromhex("00" * 32))] - async with wallet_0.wallet_state_manager.new_action_scope(DEFAULT_TX_CONFIG, push=True) as action_scope: - did_wallet_0: DIDWallet = await DIDWallet.create_new_did_wallet( - wallet_node_0.wallet_state_manager, - wallet_0, - uint64(101), - action_scope, - backups_ids=recovery_list, - num_of_backup_ids_needed=uint64(0), - ) - - await full_node_api.process_transaction_records(records=action_scope.side_effects.transactions) - await full_node_api.wait_for_wallets_synced(wallet_nodes=[wallet_node_0]) - - await time_out_assert(15, did_wallet_0.get_confirmed_balance, 101) - await time_out_assert(15, did_wallet_0.get_unconfirmed_balance, 101) - await time_out_assert(15, did_wallet_0.get_pending_change_balance, 0) - - await full_node_api.farm_blocks_to_wallet(1, wallet_0) - - ####################### - all_node_0_wallets = await wallet_node_0.wallet_state_manager.user_store.get_all_wallet_info_entries() - all_node_1_wallets = await wallet_node_1.wallet_state_manager.user_store.get_all_wallet_info_entries() - assert len(all_node_0_wallets) == len(all_node_1_wallets) - - # Note that the inner program we expect is different than the on-chain inner. - # This means that we have more work to do in the checks for the two different spend cases of - # the DID wallet Singleton - # assert ( - # json.loads(all_node_0_wallets[1].data)["current_inner"] - # == json.loads(all_node_1_wallets[1].data)["current_inner"] - # ) - - # TODO: See Issue CHIA-1544 # This test should be ported to WalletTestFramework once we can replace keys in the wallet node @pytest.mark.parametrize( @@ -2273,8 +1170,6 @@ async def test_did_resync( fee = uint64(0) wallet_api_1 = WalletRpcApi(wallet_node_1) wallet_api_2 = WalletRpcApi(wallet_node_2) - async with wallet.wallet_state_manager.new_action_scope(DEFAULT_TX_CONFIG, push=True) as action_scope: - ph = await action_scope.get_puzzle_hash(wallet.wallet_state_manager) if trusted: wallet_node_1.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()} wallet_node_2.config["trusted_peers"] = {full_node_server.node_id.hex(): full_node_server.node_id.hex()} @@ -2292,8 +1187,6 @@ async def test_did_resync( wallet, uint64(101), action_scope, - [bytes32(ph)], - uint64(1), {"Twitter": "Test", "GitHub": "测试"}, fee=fee, ) @@ -2306,7 +1199,7 @@ async def test_did_resync( async with wallet2.wallet_state_manager.new_action_scope(DEFAULT_TX_CONFIG, push=True) as action_scope: new_puzhash = await action_scope.get_puzzle_hash(wallet2.wallet_state_manager) async with did_wallet_1.wallet_state_manager.new_action_scope(DEFAULT_TX_CONFIG, push=True) as action_scope: - await did_wallet_1.transfer_did(new_puzhash, fee, True, action_scope=action_scope) + await did_wallet_1.transfer_did(new_puzhash, fee, action_scope=action_scope) await full_node_api.process_transaction_records(records=action_scope.side_effects.transactions) await full_node_api.wait_for_wallets_synced(wallet_nodes=[wallet_node_1, wallet_node_2]) # Check if the DID wallet is created in the wallet2 @@ -2354,12 +1247,8 @@ async def test_did_resync( ], indirect=True, ) -@pytest.mark.parametrize( - "use_alternate_recovery", - [True, False], -) @pytest.mark.anyio -async def test_did_coin_records(wallet_environments: WalletTestFramework, use_alternate_recovery: bool) -> None: +async def test_did_coin_records(wallet_environments: WalletTestFramework) -> None: # Setup wallet_node = wallet_environments.environments[0].node wallet = wallet_environments.environments[0].xch_wallet @@ -2371,7 +1260,6 @@ async def test_did_coin_records(wallet_environments: WalletTestFramework, use_al wallet, uint64(1), action_scope, - use_alternate_recovery=use_alternate_recovery, ) await wallet_environments.process_pending_states( @@ -2395,7 +1283,7 @@ async def test_did_coin_records(wallet_environments: WalletTestFramework, use_al wallet_environments.tx_config, push=True ) as action_scope: await did_wallet.transfer_did( - await action_scope.get_puzzle_hash(did_wallet.wallet_state_manager), uint64(0), True, action_scope + await action_scope.get_puzzle_hash(did_wallet.wallet_state_manager), uint64(0), action_scope ) await wallet_environments.process_pending_states( [ diff --git a/chia/_tests/wallet/nft_wallet/test_nft_wallet.py b/chia/_tests/wallet/nft_wallet/test_nft_wallet.py index 3ae580011504..87d793bd5273 100644 --- a/chia/_tests/wallet/nft_wallet/test_nft_wallet.py +++ b/chia/_tests/wallet/nft_wallet/test_nft_wallet.py @@ -1284,7 +1284,7 @@ async def test_nft_transfer_nft_with_did(wallet_environments: WalletTestFramewor async with did_wallet.wallet_state_manager.new_action_scope( wallet_environments.tx_config, push=True ) as action_scope: - await did_wallet.transfer_did(wallet_1_ph, uint64(0), True, action_scope) + await did_wallet.transfer_did(wallet_1_ph, uint64(0), action_scope) await wallet_environments.process_pending_states( [ diff --git a/chia/_tests/wallet/rpc/test_wallet_rpc.py b/chia/_tests/wallet/rpc/test_wallet_rpc.py index 661e4a5b09bf..7dd19c19e8c3 100644 --- a/chia/_tests/wallet/rpc/test_wallet_rpc.py +++ b/chia/_tests/wallet/rpc/test_wallet_rpc.py @@ -111,13 +111,11 @@ DIDGetDID, DIDGetMetadata, DIDGetPubkey, - DIDGetRecoveryList, DIDGetWalletName, DIDMessageSpend, DIDSetWalletName, DIDTransferDID, DIDUpdateMetadata, - DIDUpdateRecoveryIDs, FungibleAsset, GetNotifications, GetPrivateKey, @@ -1538,20 +1536,6 @@ async def test_did_endpoints(wallet_rpc_environment: WalletRpcTestEnvironment) - # Create backup file await wallet_1_rpc.create_did_backup_file(DIDCreateBackupFile(did_wallet_id_0)) - await time_out_assert(5, check_mempool_spend_count, True, full_node_api, 1) - await farm_transaction_block(full_node_api, wallet_1_node) - # Update recovery list - update_res = await wallet_1_rpc.update_did_recovery_list( - DIDUpdateRecoveryIDs( - wallet_id=uint32(did_wallet_id_0), new_list=[did_id_0], num_verifications_required=uint64(1), push=True - ), - DEFAULT_TX_CONFIG, - ) - assert len(update_res.transactions) > 0 - recovery_list_res = await wallet_1_rpc.get_did_recovery_list(DIDGetRecoveryList(did_wallet_id_0)) - assert recovery_list_res.num_required == 1 - assert recovery_list_res.recovery_list[0] == did_id_0 - await time_out_assert(5, check_mempool_spend_count, True, full_node_api, 1) await farm_transaction_block(full_node_api, wallet_1_node) diff --git a/chia/_tests/wallet/vc_wallet/test_vc_wallet.py b/chia/_tests/wallet/vc_wallet/test_vc_wallet.py index ccd47079c349..2776f4951dcb 100644 --- a/chia/_tests/wallet/vc_wallet/test_vc_wallet.py +++ b/chia/_tests/wallet/vc_wallet/test_vc_wallet.py @@ -753,7 +753,7 @@ async def test_self_revoke(wallet_environments: WalletTestFramework) -> None: async with did_wallet.wallet_state_manager.new_action_scope( wallet_environments.tx_config, push=True ) as action_scope: - await did_wallet.transfer_did(bytes32.zeros, uint64(0), False, action_scope) + await did_wallet.transfer_did(bytes32.zeros, uint64(0), action_scope) await wallet_environments.process_pending_states( [ diff --git a/chia/wallet/did_wallet/did_wallet.py b/chia/wallet/did_wallet/did_wallet.py index 13b6b79b2421..219bfb177003 100644 --- a/chia/wallet/did_wallet/did_wallet.py +++ b/chia/wallet/did_wallet/did_wallet.py @@ -78,8 +78,6 @@ async def create_new_did_wallet( wallet: Wallet, amount: uint64, action_scope: WalletActionScope, - backups_ids: list[bytes32] = [], - num_of_backup_ids_needed: uint64 = None, metadata: dict[str, str] = {}, name: Optional[str] = None, fee: uint64 = uint64(0), @@ -114,14 +112,10 @@ async def create_new_did_wallet( if amount & 1 == 0: raise ValueError("DID amount must be odd number") - if num_of_backup_ids_needed is None: - num_of_backup_ids_needed = uint64(len(backups_ids)) - if num_of_backup_ids_needed > len(backups_ids): - raise ValueError("Cannot require more IDs than are known.") self.did_info = DIDInfo( origin_coin=None, - backup_ids=backups_ids, - num_of_backup_ids_needed=num_of_backup_ids_needed, + backup_ids=[], + num_of_backup_ids_needed=uint64(0), parent_info=[], current_inner=None, temp_coin=None, @@ -229,6 +223,7 @@ async def create_new_did_wallet_from_coin_spend( recovery_list: list[bytes32] = [] backup_required: int = num_verification.as_int() if not did_recovery_is_nil(recovery_list_hash): + self.log.warning(f"DID {launch_coin.name().hex()} has a recovery list hash which has been deprecated.") try: for did in inner_solution.rest().rest().rest().rest().rest().as_python(): recovery_list.append(bytes32(did[0])) @@ -682,7 +677,6 @@ async def transfer_did( self, new_puzhash: bytes32, fee: uint64, - with_recovery: bool, action_scope: WalletActionScope, extra_conditions: tuple[Condition, ...] = tuple(), ) -> None: @@ -690,7 +684,6 @@ async def transfer_did( Transfer the current DID to another owner :param new_puzhash: New owner's p2_puzzle :param fee: Transaction fee - :param with_recovery: A boolean indicates if the recovery info will be sent through the blockchain :return: Spend bundle """ assert self.did_info.current_inner is not None @@ -698,9 +691,8 @@ async def transfer_did( coin = await self.get_coin() backup_ids = [] backup_required = uint64(0) - if with_recovery: - backup_ids = self.did_info.backup_ids - backup_required = self.did_info.num_of_backup_ids_needed + backup_ids = self.did_info.backup_ids + backup_required = self.did_info.num_of_backup_ids_needed new_did_puzhash = did_wallet_puzzles.get_inner_puzhash_by_p2( p2_puzhash=new_puzhash, recovery_list=backup_ids, @@ -713,12 +705,7 @@ async def transfer_did( primaries=[CreateCoin(new_did_puzhash, uint64(coin.amount), [new_puzhash])], conditions=(*extra_conditions, CreateCoinAnnouncement(coin.name())), ) - # Need to include backup list reveal here, even we are don't recover - # innerpuz solution is - # (mode, p2_solution) - innersol: Program = Program.to([2, p2_solution]) - if with_recovery: - innersol = Program.to([2, p2_solution, [], [], [], self.did_info.backup_ids]) + innersol = Program.to([2, p2_solution, [], [], [], self.did_info.backup_ids]) # full solution is (corehash parent_info my_amount innerpuz_reveal solution) full_puzzle: Program = create_singleton_puzzle( @@ -850,280 +837,6 @@ async def create_message_spend( async with action_scope.use() as interface: interface.side_effects.transactions.append(tx) - # This is used to cash out, or update the id_list - async def create_exit_spend(self, puzhash: bytes32, action_scope: WalletActionScope) -> None: - assert self.did_info.current_inner is not None - assert self.did_info.origin_coin is not None - coin = await self.get_coin() - message_puz = Program.to((1, [[51, puzhash, coin.amount - 1, [puzhash]], [51, 0x00, -113]])) - - # innerpuz solution is (mode p2_solution) - innersol: Program = Program.to([1, [[], message_puz, []]]) - # full solution is (corehash parent_info my_amount innerpuz_reveal solution) - innerpuz: Program = self.did_info.current_inner - - full_puzzle: Program = create_singleton_puzzle( - innerpuz, - self.did_info.origin_coin.name(), - ) - parent_info = self.get_parent_for_coin(coin) - assert parent_info is not None - fullsol = Program.to( - [ - [ - parent_info.parent_name, - parent_info.inner_puzzle_hash, - parent_info.amount, - ], - coin.amount, - innersol, - ] - ) - list_of_coinspends = [make_spend(coin, full_puzzle, fullsol)] - spend_bundle = WalletSpendBundle(list_of_coinspends, G2Element()) - - async with action_scope.use() as interface: - interface.side_effects.transactions.append( - TransactionRecord( - confirmed_at_height=uint32(0), - created_at_time=uint64(int(time.time())), - to_puzzle_hash=await action_scope.get_puzzle_hash( - self.wallet_state_manager, override_reuse_puzhash_with=True - ), - amount=uint64(coin.amount), - fee_amount=uint64(0), - confirmed=False, - sent=uint32(0), - spend_bundle=spend_bundle, - additions=spend_bundle.additions(), - removals=spend_bundle.removals(), - wallet_id=self.wallet_info.id, - sent_to=[], - trade_id=None, - type=uint32(TransactionType.OUTGOING_TX.value), - name=bytes32.secret(), - memos=list(compute_memos(spend_bundle).items()), - valid_times=ConditionValidTimes(), - ) - ) - - # Pushes a spend bundle to create a message coin on the blockchain - # Returns a spend bundle for the recoverer to spend the message coin - async def create_attestment( - self, - recovering_coin_name: bytes32, - newpuz: bytes32, - pubkey: G1Element, - action_scope: WalletActionScope, - extra_conditions: tuple[Condition, ...] = tuple(), - ) -> tuple[WalletSpendBundle, str]: - """ - Create an attestment - TODO: - 1. We should use/respect `action_scope.config.tx_config` (reuse_puzhash and co) - 2. We should take a fee as it's a requirement for every transaction function to do so - :param recovering_coin_name: Coin ID of the DID - :param newpuz: New puzzle hash - :param pubkey: New wallet pubkey - :return: (Spend bundle, attest string) - """ - assert self.did_info.current_inner is not None - assert self.did_info.origin_coin is not None - coin = await self.get_coin() - message = did_wallet_puzzles.create_recovery_message_puzzle(recovering_coin_name, newpuz, pubkey) - innermessage = message.get_tree_hash() - innerpuz: Program = self.did_info.current_inner - uncurried = did_wallet_puzzles.uncurry_innerpuz(innerpuz) - assert uncurried is not None - p2_puzzle = uncurried[0] - # innerpuz solution is (mode, p2_solution) - p2_solution = self.standard_wallet.make_solution( - primaries=[ - CreateCoin(innerpuz.get_tree_hash(), uint64(coin.amount), [p2_puzzle.get_tree_hash()]), - CreateCoin(innermessage, uint64(0)), - ], - conditions=extra_conditions, - ) - innersol = Program.to([1, p2_solution]) - - # full solution is (corehash parent_info my_amount innerpuz_reveal solution) - full_puzzle: Program = create_singleton_puzzle( - innerpuz, - self.did_info.origin_coin.name(), - ) - parent_info = self.get_parent_for_coin(coin) - assert parent_info is not None - - fullsol = Program.to( - [ - [ - parent_info.parent_name, - parent_info.inner_puzzle_hash, - parent_info.amount, - ], - coin.amount, - innersol, - ] - ) - list_of_coinspends = [make_spend(coin, full_puzzle, fullsol)] - message_spend = did_wallet_puzzles.create_spend_for_message(coin.name(), recovering_coin_name, newpuz, pubkey) - message_spend_bundle = WalletSpendBundle([message_spend], AugSchemeMPL.aggregate([])) - spend_bundle = WalletSpendBundle(list_of_coinspends, G2Element()) - did_record = TransactionRecord( - confirmed_at_height=uint32(0), - created_at_time=uint64(int(time.time())), - to_puzzle_hash=await action_scope.get_puzzle_hash( - self.wallet_state_manager, override_reuse_puzhash_with=True - ), - amount=uint64(coin.amount), - fee_amount=uint64(0), - confirmed=False, - sent=uint32(0), - spend_bundle=spend_bundle, - additions=spend_bundle.additions(), - removals=spend_bundle.removals(), - wallet_id=self.wallet_info.id, - sent_to=[], - trade_id=None, - type=uint32(TransactionType.INCOMING_TX.value), - name=bytes32.secret(), - memos=list(compute_memos(spend_bundle).items()), - valid_times=parse_timelock_info(extra_conditions), - ) - async with action_scope.use() as interface: - interface.side_effects.transactions.append(did_record) - attest_str: str = f"{self.get_my_DID()}:{bytes(message_spend_bundle).hex()}:{coin.parent_coin_info.hex()}:" - attest_str += f"{self.did_info.current_inner.get_tree_hash().hex()}:{coin.amount}" - return message_spend_bundle, attest_str - - async def get_info_for_recovery(self) -> Optional[tuple[bytes32, bytes32, uint64]]: - assert self.did_info.current_inner is not None - assert self.did_info.origin_coin is not None - try: - coin = await self.get_coin() - except RuntimeError: - return None - parent = coin.parent_coin_info - innerpuzhash = self.did_info.current_inner.get_tree_hash() - amount = uint64(coin.amount) - return (parent, innerpuzhash, amount) - - async def load_attest_files_for_recovery_spend(self, attest_data: list[str]) -> tuple[list, WalletSpendBundle]: - spend_bundle_list = [] - info_dict = {} - for attest in attest_data: - info = attest.split(":") - info_dict[info[0]] = [ - bytes.fromhex(info[2]), - bytes.fromhex(info[3]), - uint64(info[4]), - ] - new_sb = WalletSpendBundle.from_bytes(bytes.fromhex(info[1])) - spend_bundle_list.append(new_sb) - # info_dict {0xidentity: "(0xparent_info 0xinnerpuz amount)"} - my_recovery_list: list[bytes32] = self.did_info.backup_ids - - # convert info dict into recovery list - same order as wallet - info_list = [] - for entry in my_recovery_list: - if entry.hex() in info_dict: - info_list.append( - [ - info_dict[entry.hex()][0], - info_dict[entry.hex()][1], - info_dict[entry.hex()][2], - ] - ) - else: - info_list.append([]) - message_spend_bundle = WalletSpendBundle.aggregate(spend_bundle_list) - return info_list, message_spend_bundle - - async def recovery_spend( - self, - coin: Coin, - puzhash: bytes32, - parent_innerpuzhash_amounts_for_recovery_ids: list[tuple[bytes, bytes, int]], - pubkey: G1Element, - spend_bundle: WalletSpendBundle, - action_scope: WalletActionScope, - ) -> None: - assert self.did_info.origin_coin is not None - - # innersol is mode new_amount_or_p2_solution new_inner_puzhash parent_innerpuzhash_amounts_for_recovery_ids pubkey recovery_list_reveal my_id) # noqa - innersol: Program = Program.to( - [ - 0, - coin.amount, - puzhash, - parent_innerpuzhash_amounts_for_recovery_ids, - bytes(pubkey), - self.did_info.backup_ids, - coin.name(), - ] - ) - # full solution is (parent_info my_amount solution) - assert self.did_info.current_inner is not None - innerpuz: Program = self.did_info.current_inner - full_puzzle: Program = create_singleton_puzzle( - innerpuz, - self.did_info.origin_coin.name(), - ) - parent_info = self.get_parent_for_coin(coin) - assert parent_info is not None - fullsol = Program.to( - [ - [ - parent_info.parent_name, - parent_info.inner_puzzle_hash, - parent_info.amount, - ], - coin.amount, - innersol, - ] - ) - list_of_coinspends = [make_spend(coin, full_puzzle, fullsol)] - - spend_bundle = spend_bundle.aggregate([spend_bundle, WalletSpendBundle(list_of_coinspends, G2Element())]) - - async with action_scope.use() as interface: - interface.side_effects.transactions.append( - TransactionRecord( - confirmed_at_height=uint32(0), - created_at_time=uint64(int(time.time())), - to_puzzle_hash=await action_scope.get_puzzle_hash( - self.wallet_state_manager, override_reuse_puzhash_with=True - ), - amount=uint64(coin.amount), - fee_amount=uint64(0), - confirmed=False, - sent=uint32(0), - spend_bundle=spend_bundle, - additions=spend_bundle.additions(), - removals=spend_bundle.removals(), - wallet_id=self.wallet_info.id, - sent_to=[], - trade_id=None, - type=uint32(TransactionType.OUTGOING_TX.value), - name=bytes32.secret(), - memos=list(compute_memos(spend_bundle).items()), - valid_times=ConditionValidTimes(), - ) - ) - new_did_info = DIDInfo( - origin_coin=self.did_info.origin_coin, - backup_ids=self.did_info.backup_ids, - num_of_backup_ids_needed=self.did_info.num_of_backup_ids_needed, - parent_info=self.did_info.parent_info, - current_inner=self.did_info.current_inner, - temp_coin=self.did_info.temp_coin, - temp_puzhash=self.did_info.temp_puzhash, - temp_pubkey=self.did_info.temp_pubkey, - sent_recovery_transaction=True, - metadata=self.did_info.metadata, - ) - await self.save_info(new_did_info) - async def get_did_innerpuz( self, action_scope: WalletActionScope, @@ -1395,25 +1108,6 @@ async def add_parent(self, name: bytes32, parent: Optional[LineageProof]): ) await self.save_info(did_info) - async def update_recovery_list(self, recover_list: list[bytes32], num_of_backup_ids_needed: uint64) -> bool: - if num_of_backup_ids_needed > len(recover_list): - return False - did_info = DIDInfo( - origin_coin=self.did_info.origin_coin, - backup_ids=recover_list, - num_of_backup_ids_needed=num_of_backup_ids_needed, - parent_info=self.did_info.parent_info, - current_inner=self.did_info.current_inner, - temp_coin=self.did_info.temp_coin, - temp_puzhash=self.did_info.temp_puzhash, - temp_pubkey=self.did_info.temp_pubkey, - sent_recovery_transaction=self.did_info.sent_recovery_transaction, - metadata=self.did_info.metadata, - ) - await self.save_info(did_info) - await self.wallet_state_manager.update_wallet_puzzle_hashes(self.wallet_info.id) - return True - async def update_metadata(self, metadata: dict[str, str]) -> bool: # validate metadata if not all(isinstance(k, str) and isinstance(v, str) for k, v in metadata.items()): diff --git a/chia/wallet/vc_wallet/vc_wallet.py b/chia/wallet/vc_wallet/vc_wallet.py index c03ea76fc773..b386f4e9b688 100644 --- a/chia/wallet/vc_wallet/vc_wallet.py +++ b/chia/wallet/vc_wallet/vc_wallet.py @@ -400,11 +400,6 @@ async def revoke_vc( ) return - recovery_info: Optional[tuple[bytes32, bytes32, uint64]] = await did_wallet.get_info_for_recovery() - if recovery_info is None: - raise RuntimeError("DID could not currently be accessed while trying to revoke VC") # pragma: no cover - _, provider_inner_puzhash, _ = recovery_info - # Generate spend specific nonce coins = {await did_wallet.get_coin()} coins.add(vc.coin) @@ -421,7 +416,10 @@ async def revoke_vc( ) # Assemble final bundle - expected_did_announcement, vc_spend = vc.activate_backdoor(provider_inner_puzhash, announcement_nonce=nonce) + assert did_wallet.did_info.current_inner is not None + expected_did_announcement, vc_spend = vc.activate_backdoor( + did_wallet.did_info.current_inner.get_tree_hash(), announcement_nonce=nonce + ) await did_wallet.create_message_spend( action_scope, extra_conditions=(*extra_conditions, expected_did_announcement, vc_announcement), diff --git a/chia/wallet/wallet_request_types.py b/chia/wallet/wallet_request_types.py index e8df53c60677..7a3d18e4b97b 100644 --- a/chia/wallet/wallet_request_types.py +++ b/chia/wallet/wallet_request_types.py @@ -389,23 +389,6 @@ class DIDGetPubkeyResponse(Streamable): pubkey: G1Element -@streamable -@dataclass(frozen=True) -class DIDGetRecoveryInfo(Streamable): - wallet_id: uint32 - - -@streamable -@dataclass(frozen=True) -class DIDGetRecoveryInfoResponse(Streamable): - wallet_id: uint32 - my_did: str - coin_name: bytes32 - newpuzhash: Optional[bytes32] - pubkey: Optional[G1Element] - backup_dids: list[bytes32] - - @streamable @dataclass(frozen=True) class DIDGetCurrentCoinInfo(Streamable): @@ -449,20 +432,6 @@ class DIDGetDIDResponse(Streamable): coin_id: Optional[bytes32] = None -@streamable -@dataclass(frozen=True) -class DIDGetRecoveryList(Streamable): - wallet_id: uint32 - - -@streamable -@dataclass(frozen=True) -class DIDGetRecoveryListResponse(Streamable): - wallet_id: uint32 - recovery_list: list[str] - num_required: uint16 - - @streamable @dataclass(frozen=True) class DIDGetMetadata(Streamable): @@ -999,20 +968,6 @@ class CombineCoinsResponse(TransactionEndpointResponse): pass -@streamable -@kw_only_dataclass -class DIDUpdateRecoveryIDs(TransactionEndpointRequest): - wallet_id: uint32 = field(default_factory=default_raise) - new_list: list[str] = field(default_factory=default_raise) - num_verifications_required: Optional[uint64] = None - - -@streamable -@dataclass(frozen=True) -class DIDUpdateRecoveryIDsResponse(TransactionEndpointResponse): - pass - - @streamable @kw_only_dataclass class DIDMessageSpend(TransactionEndpointRequest): @@ -1048,6 +1003,11 @@ class DIDTransferDID(TransactionEndpointRequest): inner_address: str = field(default_factory=default_raise) with_recovery_info: bool = True + def __post_init__(self) -> None: + if self.with_recovery_info is False: + raise ValueError("Recovery related options are no longer supported. `with_recovery` must always be true.") + return super().__post_init__() + @streamable @dataclass(frozen=True) diff --git a/chia/wallet/wallet_rpc_api.py b/chia/wallet/wallet_rpc_api.py index a37f5bc15737..6cac029b9008 100644 --- a/chia/wallet/wallet_rpc_api.py +++ b/chia/wallet/wallet_rpc_api.py @@ -132,10 +132,6 @@ DIDGetMetadataResponse, DIDGetPubkey, DIDGetPubkeyResponse, - DIDGetRecoveryInfo, - DIDGetRecoveryInfoResponse, - DIDGetRecoveryList, - DIDGetRecoveryListResponse, DIDGetWalletName, DIDGetWalletNameResponse, DIDMessageSpend, @@ -146,8 +142,6 @@ DIDTransferDIDResponse, DIDUpdateMetadata, DIDUpdateMetadataResponse, - DIDUpdateRecoveryIDs, - DIDUpdateRecoveryIDsResponse, DLDeleteMirror, DLDeleteMirrorResponse, DLGetMirrors, @@ -556,15 +550,10 @@ def get_routes(self) -> dict[str, Endpoint]: # DID Wallet "/did_set_wallet_name": self.did_set_wallet_name, "/did_get_wallet_name": self.did_get_wallet_name, - "/did_update_recovery_ids": self.did_update_recovery_ids, "/did_update_metadata": self.did_update_metadata, "/did_get_pubkey": self.did_get_pubkey, "/did_get_did": self.did_get_did, - "/did_recovery_spend": self.did_recovery_spend, - "/did_get_recovery_list": self.did_get_recovery_list, "/did_get_metadata": self.did_get_metadata, - "/did_create_attest": self.did_create_attest, - "/did_get_information_needed_for_recovery": self.did_get_information_needed_for_recovery, "/did_get_current_coin_info": self.did_get_current_coin_info, "/did_create_backup_file": self.did_create_backup_file, "/did_transfer_did": self.did_transfer_did, @@ -1112,12 +1101,8 @@ async def create_new_wallet( elif request["wallet_type"] == "did_wallet": if request["did_type"] == "new": - backup_dids = [] - num_needed = 0 - for d in request["backup_dids"]: - backup_dids.append(decode_puzzle_hash(d)) - if len(backup_dids) > 0: - num_needed = uint64(request["num_of_backup_ids_needed"]) + if "backup_dids" in request and request["backup_dids"] != []: + raise ValueError("Recovery options are no longer supported. `backup_dids` cannot be set.") metadata: dict[str, str] = {} if "metadata" in request: if type(request["metadata"]) is dict: @@ -1132,8 +1117,6 @@ async def create_new_wallet( main_wallet, uint64(request["amount"]), action_scope, - backup_dids, - uint64(num_needed), metadata, did_wallet_name, uint64(request.get("fee", 0)), @@ -2591,31 +2574,6 @@ async def did_get_wallet_name(self, request: DIDGetWalletName) -> DIDGetWalletNa wallet = self.service.wallet_state_manager.get_wallet(id=request.wallet_id, required_type=DIDWallet) return DIDGetWalletNameResponse(request.wallet_id, wallet.get_name()) - @tx_endpoint(push=True) - @marshal - async def did_update_recovery_ids( - self, - request: DIDUpdateRecoveryIDs, - action_scope: WalletActionScope, - extra_conditions: tuple[Condition, ...] = tuple(), - ) -> DIDUpdateRecoveryIDsResponse: - wallet = self.service.wallet_state_manager.get_wallet(id=request.wallet_id, required_type=DIDWallet) - recovery_list = [decode_puzzle_hash(puzzle_hash) for puzzle_hash in request.new_list] - new_amount_verifications_required = ( - request.num_verifications_required - if request.num_verifications_required is not None - else uint64(len(recovery_list)) - ) - async with self.service.wallet_state_manager.lock: - update_success = await wallet.update_recovery_list(recovery_list, new_amount_verifications_required) - # Update coin with new ID info - if update_success: - await wallet.create_update_spend(action_scope, fee=request.fee, extra_conditions=extra_conditions) - # tx_endpoint will take care of default values here - return DIDUpdateRecoveryIDsResponse([], []) - else: - raise RuntimeError("updating recovery list failed") - @tx_endpoint(push=False) @marshal async def did_message_spend( @@ -2910,68 +2868,14 @@ async def did_get_did(self, request: DIDGetDID) -> DIDGetDIDResponse: except RuntimeError: return DIDGetDIDResponse(wallet_id=request.wallet_id, my_did=my_did) - @marshal - async def did_get_recovery_list(self, request: DIDGetRecoveryList) -> DIDGetRecoveryListResponse: - wallet = self.service.wallet_state_manager.get_wallet(id=request.wallet_id, required_type=DIDWallet) - recovery_list = wallet.did_info.backup_ids - recovery_dids = [] - for backup_id in recovery_list: - recovery_dids.append(encode_puzzle_hash(backup_id, AddressType.DID.hrp(self.service.config))) - return DIDGetRecoveryListResponse( - wallet_id=request.wallet_id, - recovery_list=recovery_dids, - num_required=uint16(wallet.did_info.num_of_backup_ids_needed), - ) - @marshal async def did_get_metadata(self, request: DIDGetMetadata) -> DIDGetMetadataResponse: wallet = self.service.wallet_state_manager.get_wallet(id=request.wallet_id, required_type=DIDWallet) metadata = json.loads(wallet.did_info.metadata) - return DIDGetMetadataResponse(wallet_id=request.wallet_id, metadata=metadata) - - # TODO: this needs a test - # Don't need full @tx_endpoint decorator here, but "push" is still a valid option - async def did_recovery_spend(self, request: dict[str, Any]) -> EndpointResult: # pragma: no cover - wallet_id = uint32(request["wallet_id"]) - wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=DIDWallet) - if len(request["attest_data"]) < wallet.did_info.num_of_backup_ids_needed: - return {"success": False, "reason": "insufficient messages"} - async with self.service.wallet_state_manager.lock: - ( - info_list, - message_spend_bundle, - ) = await wallet.load_attest_files_for_recovery_spend(request["attest_data"]) - - if "pubkey" in request: - pubkey = G1Element.from_bytes(hexstr_to_bytes(request["pubkey"])) - else: - assert wallet.did_info.temp_pubkey is not None - pubkey = G1Element.from_bytes(wallet.did_info.temp_pubkey) - - if "puzhash" in request: - puzhash = bytes32.from_hexstr(request["puzhash"]) - else: - assert wallet.did_info.temp_puzhash is not None - puzhash = wallet.did_info.temp_puzhash - - assert wallet.did_info.temp_coin is not None - async with self.service.wallet_state_manager.new_action_scope( - DEFAULT_TX_CONFIG, push=request.get("push", True) - ) as action_scope: - await wallet.recovery_spend( - wallet.did_info.temp_coin, - puzhash, - info_list, - pubkey, - message_spend_bundle, - action_scope, - ) - [tx] = action_scope.side_effects.transactions - return { - "success": True, - "spend_bundle": tx.spend_bundle, - "transactions": [tx.to_json_dict_convenience(self.service.config)], - } + return DIDGetMetadataResponse( + wallet_id=request.wallet_id, + metadata=metadata, + ) @marshal async def did_get_pubkey(self, request: DIDGetPubkey) -> DIDGetPubkeyResponse: @@ -2980,57 +2884,6 @@ async def did_get_pubkey(self, request: DIDGetPubkey) -> DIDGetPubkeyResponse: (await wallet.wallet_state_manager.get_unused_derivation_record(request.wallet_id)).pubkey ) - # TODO: this needs a test - @tx_endpoint(push=True) - async def did_create_attest( - self, - request: dict[str, Any], - action_scope: WalletActionScope, - extra_conditions: tuple[Condition, ...] = tuple(), - ) -> EndpointResult: # pragma: no cover - wallet_id = uint32(request["wallet_id"]) - wallet = self.service.wallet_state_manager.get_wallet(id=wallet_id, required_type=DIDWallet) - async with self.service.wallet_state_manager.lock: - info = await wallet.get_info_for_recovery() - coin = bytes32.from_hexstr(request["coin_name"]) - pubkey = G1Element.from_bytes(hexstr_to_bytes(request["pubkey"])) - message_spend_bundle, attest_data = await wallet.create_attestment( - coin, - bytes32.from_hexstr(request["puzhash"]), - pubkey, - action_scope, - extra_conditions=extra_conditions, - ) - if info is not None: - return { - "success": True, - "message_spend_bundle": bytes(message_spend_bundle).hex(), - "info": [info[0].hex(), info[1].hex(), info[2]], - "attest_data": attest_data, - "transactions": None, # tx_endpoint wrapper will take care of this - } - else: - return {"success": False} - - @marshal - async def did_get_information_needed_for_recovery(self, request: DIDGetRecoveryInfo) -> DIDGetRecoveryInfoResponse: - did_wallet = self.service.wallet_state_manager.get_wallet(id=request.wallet_id, required_type=DIDWallet) - my_did = encode_puzzle_hash( - bytes32.from_hexstr(did_wallet.get_my_DID()), AddressType.DID.hrp(self.service.config) - ) - assert did_wallet.did_info.temp_coin is not None - coin_name = did_wallet.did_info.temp_coin.name() - return DIDGetRecoveryInfoResponse( - wallet_id=request.wallet_id, - my_did=my_did, - coin_name=coin_name, - newpuzhash=did_wallet.did_info.temp_puzhash, - pubkey=G1Element.from_bytes(did_wallet.did_info.temp_pubkey) - if did_wallet.did_info.temp_pubkey is not None - else None, - backup_dids=did_wallet.did_info.backup_ids, - ) - @marshal async def did_get_current_coin_info(self, request: DIDGetCurrentCoinInfo) -> DIDGetCurrentCoinInfoResponse: did_wallet = self.service.wallet_state_manager.get_wallet(id=request.wallet_id, required_type=DIDWallet) @@ -3038,15 +2891,15 @@ async def did_get_current_coin_info(self, request: DIDGetCurrentCoinInfo) -> DID bytes32.from_hexstr(did_wallet.get_my_DID()), AddressType.DID.hrp(self.service.config) ) - did_coin_threeple = await did_wallet.get_info_for_recovery() + assert did_wallet.did_info.current_inner is not None + parent_coin = await did_wallet.get_coin() assert my_did is not None - assert did_coin_threeple is not None return DIDGetCurrentCoinInfoResponse( wallet_id=request.wallet_id, my_did=my_did, - did_parent=did_coin_threeple[0], - did_innerpuz=did_coin_threeple[1], - did_amount=did_coin_threeple[2], + did_parent=parent_coin.parent_coin_info, + did_innerpuz=did_wallet.did_info.current_inner.get_tree_hash(), + did_amount=parent_coin.amount, ) @marshal @@ -3070,7 +2923,6 @@ async def did_transfer_did( await did_wallet.transfer_did( puzzle_hash, request.fee, - request.with_recovery_info, action_scope, extra_conditions=extra_conditions, ) diff --git a/chia/wallet/wallet_rpc_client.py b/chia/wallet/wallet_rpc_client.py index d06d97abcefa..e4157c992c9b 100644 --- a/chia/wallet/wallet_rpc_client.py +++ b/chia/wallet/wallet_rpc_client.py @@ -52,10 +52,6 @@ DIDGetMetadataResponse, DIDGetPubkey, DIDGetPubkeyResponse, - DIDGetRecoveryInfo, - DIDGetRecoveryInfoResponse, - DIDGetRecoveryList, - DIDGetRecoveryListResponse, DIDGetWalletName, DIDGetWalletNameResponse, DIDMessageSpend, @@ -66,8 +62,6 @@ DIDTransferDIDResponse, DIDUpdateMetadata, DIDUpdateMetadataResponse, - DIDUpdateRecoveryIDs, - DIDUpdateRecoveryIDsResponse, DLDeleteMirror, DLDeleteMirrorResponse, DLGetMirrors, @@ -543,25 +537,6 @@ async def create_did_backup_file(self, request: DIDCreateBackupFile) -> DIDCreat await self.fetch("did_create_backup_file", request.to_json_dict()) ) - async def update_did_recovery_list( - self, - request: DIDUpdateRecoveryIDs, - tx_config: TXConfig, - extra_conditions: tuple[Condition, ...] = tuple(), - timelock_info: ConditionValidTimes = ConditionValidTimes(), - ) -> DIDUpdateRecoveryIDsResponse: - return DIDUpdateRecoveryIDsResponse.from_json_dict( - await self.fetch( - "did_update_recovery_ids", - request.json_serialize_for_transport(tx_config, extra_conditions, timelock_info), - ) - ) - - async def get_did_recovery_list(self, request: DIDGetRecoveryList) -> DIDGetRecoveryListResponse: - return DIDGetRecoveryListResponse.from_json_dict( - await self.fetch("did_get_recovery_list", request.to_json_dict()) - ) - async def did_message_spend( self, request: DIDMessageSpend, @@ -604,43 +579,11 @@ async def create_new_did_wallet_from_recovery(self, filename: str) -> dict[str, response = await self.fetch("create_new_wallet", request) return response - async def did_create_attest( - self, - wallet_id: int, - coin_name: str, - pubkey: str, - puzhash: str, - file_name: str, - extra_conditions: tuple[Condition, ...] = tuple(), - timelock_info: ConditionValidTimes = ConditionValidTimes(), - ) -> dict[str, Any]: - request = { - "wallet_id": wallet_id, - "coin_name": coin_name, - "pubkey": pubkey, - "puzhash": puzhash, - "filename": file_name, - "extra_conditions": conditions_to_json_dicts(extra_conditions), - **timelock_info.to_json_dict(), - } - response = await self.fetch("did_create_attest", request) - return response - - async def did_get_recovery_info(self, request: DIDGetRecoveryInfo) -> DIDGetRecoveryInfoResponse: - return DIDGetRecoveryInfoResponse.from_json_dict( - await self.fetch("did_get_information_needed_for_recovery", request.to_json_dict()) - ) - async def did_get_current_coin_info(self, request: DIDGetCurrentCoinInfo) -> DIDGetCurrentCoinInfoResponse: return DIDGetCurrentCoinInfoResponse.from_json_dict( await self.fetch("did_get_current_coin_info", request.to_json_dict()) ) - async def did_recovery_spend(self, wallet_id: int, attest_filenames: str) -> dict[str, Any]: - request = {"wallet_id": wallet_id, "attest_filenames": attest_filenames} - response = await self.fetch("did_recovery_spend", request) - return response - async def did_transfer_did( self, request: DIDTransferDID,