mirror of
https://github.com/Chia-Network/chia-blockchain.git
synced 2026-08-24 10:05:29 -05:00
[CHIA-3251] Delete DID recovery endpoints (#19767)
* Port `did_set_wallet_name` * Port `did_get_wallet_name` * Port `did_update_recovery_ids` * Port `did_message_spend` * Port `did_get_info` * Port `did_find_lost_did` * fix test * Port `did_update_metadata` * Port `did_get_did` * Port `did_get_recovery_list` * Port `did_get_metadata` * Port `did_get_pubkey` * Port `did_get_information_needed_for_recovery` * Port `did_get_current_coin_info` * Port `did_create_backup_file` * Port `did_transfer_did` * Add extra_conditions uniformly in CLI tests * test coverage * Delete DID recovery endpoints * Delete `create_exit_spend` as well * Missed one
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -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(
|
||||
[
|
||||
|
||||
@@ -111,13 +111,11 @@ from chia.wallet.wallet_request_types import (
|
||||
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)
|
||||
|
||||
|
||||
@@ -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(
|
||||
[
|
||||
|
||||
@@ -78,8 +78,6 @@ class DIDWallet:
|
||||
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 @@ class DIDWallet:
|
||||
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 @@ class DIDWallet:
|
||||
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 @@ class DIDWallet:
|
||||
self,
|
||||
new_puzhash: bytes32,
|
||||
fee: uint64,
|
||||
with_recovery: bool,
|
||||
action_scope: WalletActionScope,
|
||||
extra_conditions: tuple[Condition, ...] = tuple(),
|
||||
) -> None:
|
||||
@@ -690,7 +684,6 @@ class DIDWallet:
|
||||
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 @@ class DIDWallet:
|
||||
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 @@ class DIDWallet:
|
||||
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 @@ class DIDWallet:
|
||||
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 @@ class DIDWallet:
|
||||
)
|
||||
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()):
|
||||
|
||||
@@ -400,11 +400,6 @@ class VCWallet:
|
||||
)
|
||||
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 @@ class VCWallet:
|
||||
)
|
||||
|
||||
# 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),
|
||||
|
||||
@@ -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)
|
||||
|
||||
+11
-159
@@ -132,10 +132,6 @@ from chia.wallet.wallet_request_types import (
|
||||
DIDGetMetadataResponse,
|
||||
DIDGetPubkey,
|
||||
DIDGetPubkeyResponse,
|
||||
DIDGetRecoveryInfo,
|
||||
DIDGetRecoveryInfoResponse,
|
||||
DIDGetRecoveryList,
|
||||
DIDGetRecoveryListResponse,
|
||||
DIDGetWalletName,
|
||||
DIDGetWalletNameResponse,
|
||||
DIDMessageSpend,
|
||||
@@ -146,8 +142,6 @@ from chia.wallet.wallet_request_types import (
|
||||
DIDTransferDIDResponse,
|
||||
DIDUpdateMetadata,
|
||||
DIDUpdateMetadataResponse,
|
||||
DIDUpdateRecoveryIDs,
|
||||
DIDUpdateRecoveryIDsResponse,
|
||||
DLDeleteMirror,
|
||||
DLDeleteMirrorResponse,
|
||||
DLGetMirrors,
|
||||
@@ -556,15 +550,10 @@ class WalletRpcApi:
|
||||
# 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 @@ class WalletRpcApi:
|
||||
|
||||
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 @@ class WalletRpcApi:
|
||||
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 @@ class WalletRpcApi:
|
||||
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 @@ class WalletRpcApi:
|
||||
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 @@ class WalletRpcApi:
|
||||
(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 @@ class WalletRpcApi:
|
||||
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 @@ class WalletRpcApi:
|
||||
await did_wallet.transfer_did(
|
||||
puzzle_hash,
|
||||
request.fee,
|
||||
request.with_recovery_info,
|
||||
action_scope,
|
||||
extra_conditions=extra_conditions,
|
||||
)
|
||||
|
||||
@@ -52,10 +52,6 @@ from chia.wallet.wallet_request_types import (
|
||||
DIDGetMetadataResponse,
|
||||
DIDGetPubkey,
|
||||
DIDGetPubkeyResponse,
|
||||
DIDGetRecoveryInfo,
|
||||
DIDGetRecoveryInfoResponse,
|
||||
DIDGetRecoveryList,
|
||||
DIDGetRecoveryListResponse,
|
||||
DIDGetWalletName,
|
||||
DIDGetWalletNameResponse,
|
||||
DIDMessageSpend,
|
||||
@@ -66,8 +62,6 @@ from chia.wallet.wallet_request_types import (
|
||||
DIDTransferDIDResponse,
|
||||
DIDUpdateMetadata,
|
||||
DIDUpdateMetadataResponse,
|
||||
DIDUpdateRecoveryIDs,
|
||||
DIDUpdateRecoveryIDsResponse,
|
||||
DLDeleteMirror,
|
||||
DLDeleteMirrorResponse,
|
||||
DLGetMirrors,
|
||||
@@ -543,25 +537,6 @@ class WalletRpcClient(RpcClient):
|
||||
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 @@ class WalletRpcClient(RpcClient):
|
||||
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,
|
||||
|
||||
Reference in New Issue
Block a user