From 7d9b26be778459576c4505a8a9de995aa3630a3e Mon Sep 17 00:00:00 2001 From: Matt Date: Fri, 18 Jul 2025 10:48:16 +0200 Subject: [PATCH 1/7] Change `TransactionRecord` to use dict instead of list of tuples for memos --- chia/_tests/cmds/cmd_test_utils.py | 4 +-- chia/_tests/cmds/wallet/test_consts.py | 2 +- chia/_tests/cmds/wallet/test_wallet.py | 4 +-- .../wallet/cat_wallet/test_cat_wallet.py | 7 ++--- chia/_tests/wallet/rpc/test_wallet_rpc.py | 8 +++--- chia/_tests/wallet/test_transaction_store.py | 6 ++-- chia/_tests/wallet/test_wallet.py | 2 +- chia/data_layer/data_layer_wallet.py | 4 +-- chia/pools/pool_wallet.py | 4 +-- chia/wallet/cat_wallet/cat_wallet.py | 6 ++-- chia/wallet/did_wallet/did_wallet.py | 14 +++++----- chia/wallet/nft_wallet/nft_wallet.py | 4 +-- chia/wallet/trade_manager.py | 6 ++-- chia/wallet/transaction_record.py | 28 ++++--------------- chia/wallet/vc_wallet/cr_cat_wallet.py | 6 ++-- chia/wallet/vc_wallet/vc_wallet.py | 4 +-- chia/wallet/wallet.py | 2 +- chia/wallet/wallet_rpc_api.py | 2 +- chia/wallet/wallet_state_manager.py | 14 +++++----- 19 files changed, 55 insertions(+), 72 deletions(-) diff --git a/chia/_tests/cmds/cmd_test_utils.py b/chia/_tests/cmds/cmd_test_utils.py index 922bccba854f..44672b148cce 100644 --- a/chia/_tests/cmds/cmd_test_utils.py +++ b/chia/_tests/cmds/cmd_test_utils.py @@ -133,7 +133,7 @@ async def get_transaction(self, transaction_id: bytes32) -> TransactionRecord: trade_id=None, type=uint32(TransactionType.OUTGOING_TX.value), name=bytes32([2] * 32), - memos=[(bytes32([3] * 32), [bytes([4] * 32)])], + memos={bytes32([3] * 32): [bytes([4] * 32)]}, valid_times=ConditionValidTimes(), ) @@ -283,7 +283,7 @@ async def send_transaction_multi( trade_id=None, type=uint32(TransactionType.OUTGOING_TX.value), name=name, - memos=[(bytes32([3] * 32), [bytes([4] * 32)])], + memos={bytes32([3] * 32): [bytes([4] * 32)]}, valid_times=ConditionValidTimes(), ), name, diff --git a/chia/_tests/cmds/wallet/test_consts.py b/chia/_tests/cmds/wallet/test_consts.py index a4b5db919b07..c5c06e406ca6 100644 --- a/chia/_tests/cmds/wallet/test_consts.py +++ b/chia/_tests/cmds/wallet/test_consts.py @@ -39,7 +39,7 @@ def get_bytes32(bytes_index: int) -> bytes32: trade_id=None, type=uint32(TransactionType.OUTGOING_TX.value), name=get_bytes32(2), - memos=[(get_bytes32(3), [bytes([4] * 32)])], + memos={get_bytes32(3): [bytes([4] * 32)]}, valid_times=ConditionValidTimes(), ) diff --git a/chia/_tests/cmds/wallet/test_wallet.py b/chia/_tests/cmds/wallet/test_wallet.py index 4f7c6409c8f5..38a7f2e35ba8 100644 --- a/chia/_tests/cmds/wallet/test_wallet.py +++ b/chia/_tests/cmds/wallet/test_wallet.py @@ -143,7 +143,7 @@ async def get_transactions( trade_id=None, type=uint32(t_type.value), name=bytes32([2 + i] * 32), - memos=[(bytes32([3 + i] * 32), [bytes([4 + i] * 32)])], + memos={bytes32([3 + i] * 32): [bytes([4 + i] * 32)]}, valid_times=ConditionValidTimes(), ) l_tx_rec.append(tx_rec) @@ -352,7 +352,7 @@ async def send_transaction( trade_id=None, type=uint32(TransactionType.OUTGOING_CLAWBACK.value), name=name, - memos=[(get_bytes32(3), [bytes([4] * 32)])], + memos={get_bytes32(3): [bytes([4] * 32)]}, valid_times=ConditionValidTimes(), ) return SendTransactionResponse([STD_UTX], [STD_TX], tx_rec, name) diff --git a/chia/_tests/wallet/cat_wallet/test_cat_wallet.py b/chia/_tests/wallet/cat_wallet/test_cat_wallet.py index 8d7e00559505..93d04a608149 100644 --- a/chia/_tests/wallet/cat_wallet/test_cat_wallet.py +++ b/chia/_tests/wallet/cat_wallet/test_cat_wallet.py @@ -1043,11 +1043,10 @@ async def test_cat_spend_multiple(wallet_environments: WalletTestFramework, wall txs = await wallet_1.wallet_state_manager.tx_store.get_transactions_between(cat_wallet_1.id(), 0, 100000) for tx in txs: if tx.amount == 30: - memos = tx.get_memos() - assert len(memos) == 2 # One for tx, one for change - assert b"Markus Walburg" in [v for v_list in memos.values() for v in v_list] + assert len(tx.memos) == 2 # One for tx, one for change + assert b"Markus Walburg" in [v for v_list in tx.memos.values() for v in v_list] assert tx.spend_bundle is not None - assert next(iter(memos.keys())) in [a.name() for a in tx.spend_bundle.additions()] + assert next(iter(tx.memos.keys())) in [a.name() for a in tx.spend_bundle.additions()] @pytest.mark.limit_consensus_modes(allowed=[ConsensusMode.PLAIN], reason="irrelevant") diff --git a/chia/_tests/wallet/rpc/test_wallet_rpc.py b/chia/_tests/wallet/rpc/test_wallet_rpc.py index 661e4a5b09bf..962048d31ae7 100644 --- a/chia/_tests/wallet/rpc/test_wallet_rpc.py +++ b/chia/_tests/wallet/rpc/test_wallet_rpc.py @@ -426,9 +426,9 @@ async def test_send_transaction(wallet_rpc_environment: WalletRpcTestEnvironment # Checks that the memo can be retrieved tx_confirmed = await client.get_transaction(transaction_id) assert tx_confirmed.confirmed - assert len(tx_confirmed.get_memos()) == 1 - assert [b"this is a basic tx"] in tx_confirmed.get_memos().values() - assert next(iter(tx_confirmed.get_memos().keys())) in [a.name() for a in spend_bundle.additions()] + assert len(tx_confirmed.memos) == 1 + assert [b"this is a basic tx"] in tx_confirmed.memos.values() + assert next(iter(tx_confirmed.memos.keys())) in [a.name() for a in spend_bundle.additions()] await time_out_assert(20, get_confirmed_balance, generated_funds - tx_amount, client, 1) @@ -968,7 +968,7 @@ async def test_send_transaction_multi(wallet_rpc_environment: WalletRpcTestEnvir # Checks that the memo can be retrieved tx_confirmed = await client.get_transaction(send_tx_res.name) assert tx_confirmed.confirmed - memos = tx_confirmed.get_memos() + memos = tx_confirmed.memos assert len(memos) == len(outputs) for output in outputs: assert [output["memos"][0].encode()] in memos.values() diff --git a/chia/_tests/wallet/test_transaction_store.py b/chia/_tests/wallet/test_transaction_store.py index 195932faab7e..00b89f1f9d70 100644 --- a/chia/_tests/wallet/test_transaction_store.py +++ b/chia/_tests/wallet/test_transaction_store.py @@ -45,7 +45,7 @@ bytes32(bytes32.random(module_seeded_random)), # trade_id uint32(TransactionType.OUTGOING_TX), # type bytes32(bytes32.random(module_seeded_random)), # name - [], # list[tuple[bytes32, list[bytes]]] memos + {}, # memos ConditionValidTimes(), ) @@ -859,7 +859,7 @@ async def test_valid_times_migration() -> None: trade_id=None, type=uint32(TransactionType.INCOMING_TX.value), name=bytes32.zeros, - memos=[], + memos={}, ) async with db_wrapper.writer_maybe_transaction() as conn: @@ -910,7 +910,7 @@ async def test_large_tx_record_query() -> None: trade_id=None, type=uint32(TransactionType.INCOMING_TX.value), name=name, - memos=[], + memos={}, ) tx_records_to_insert.append( ( diff --git a/chia/_tests/wallet/test_wallet.py b/chia/_tests/wallet/test_wallet.py index 3a4d49679b5c..9b751c4847d9 100644 --- a/chia/_tests/wallet/test_wallet.py +++ b/chia/_tests/wallet/test_wallet.py @@ -1733,7 +1733,7 @@ async def test_wallet_prevent_fee_theft(self, wallet_environments: WalletTestFra trade_id=None, type=uint32(TransactionType.OUTGOING_TX.value), name=name, - memos=[], + memos={}, valid_times=ConditionValidTimes(), ) [stolen_tx] = await wallet.wallet_state_manager.add_pending_transactions([stolen_tx]) diff --git a/chia/data_layer/data_layer_wallet.py b/chia/data_layer/data_layer_wallet.py index ae389c3d7bf7..d602a93590c8 100644 --- a/chia/data_layer/data_layer_wallet.py +++ b/chia/data_layer/data_layer_wallet.py @@ -545,7 +545,7 @@ async def create_update_state_spend( spend_bundle=spend_bundle, additions=spend_bundle.additions(), removals=spend_bundle.removals(), - memos=list(compute_memos(spend_bundle).items()), + memos=compute_memos(spend_bundle), wallet_id=self.id(), sent_to=[], trade_id=None, @@ -740,7 +740,7 @@ async def delete_mirror( spend_bundle=mirror_bundle, additions=mirror_bundle.additions(), removals=mirror_bundle.removals(), - memos=list(compute_memos(mirror_bundle).items()), + memos=compute_memos(mirror_bundle), wallet_id=self.id(), # This is being called before the wallet is created so we're using a ID of 0 sent_to=[], trade_id=None, diff --git a/chia/pools/pool_wallet.py b/chia/pools/pool_wallet.py index ce08ab78aa33..5566f8c63921 100644 --- a/chia/pools/pool_wallet.py +++ b/chia/pools/pool_wallet.py @@ -537,7 +537,7 @@ async def generate_travel_transactions(self, fee: uint64, action_scope: WalletAc wallet_id=self.id(), sent_to=[], trade_id=None, - memos=[], + memos={}, type=uint32(TransactionType.OUTGOING_TX.value), name=unsigned_spend_bundle.name(), valid_times=ConditionValidTimes(), @@ -809,7 +809,7 @@ async def claim_pool_rewards( removals=claim_spend.removals(), wallet_id=uint32(self.wallet_id), sent_to=[], - memos=[], + memos={}, trade_id=None, type=uint32(TransactionType.OUTGOING_TX.value), name=claim_spend.name(), diff --git a/chia/wallet/cat_wallet/cat_wallet.py b/chia/wallet/cat_wallet/cat_wallet.py index d35447785373..fd88fb07d200 100644 --- a/chia/wallet/cat_wallet/cat_wallet.py +++ b/chia/wallet/cat_wallet/cat_wallet.py @@ -188,7 +188,7 @@ async def create_new_cat_wallet( trade_id=None, type=uint32(TransactionType.INCOMING_TX.value), name=spend_bundle.name(), - memos=[], + memos={}, valid_times=ConditionValidTimes(), ) async with action_scope.use() as interface: @@ -467,7 +467,7 @@ async def get_pending_change_balance(self) -> uint64: for coin in record.additions: hint_dict = { coin_id: bytes32(memos[0]) - for coin_id, memos in record.memos + for coin_id, memos in record.memos.items() if len(memos) > 0 and len(memos[0]) == 32 } if await self.wallet_state_manager.does_coin_belong_to_wallet(coin, self.id(), hint_dict=hint_dict): @@ -808,7 +808,7 @@ async def generate_signed_transaction( trade_id=None, type=uint32(TransactionType.OUTGOING_TX.value), name=spend_bundle.name(), - memos=list(compute_memos(spend_bundle).items()), + memos=compute_memos(spend_bundle), valid_times=parse_timelock_info(extra_conditions), ) ) diff --git a/chia/wallet/did_wallet/did_wallet.py b/chia/wallet/did_wallet/did_wallet.py index 13b6b79b2421..b2c0f86a1eb5 100644 --- a/chia/wallet/did_wallet/did_wallet.py +++ b/chia/wallet/did_wallet/did_wallet.py @@ -671,7 +671,7 @@ async def create_update_spend( trade_id=None, type=uint32(TransactionType.OUTGOING_TX.value), name=bytes32.secret(), - memos=list(compute_memos(spend_bundle).items()), + memos=compute_memos(spend_bundle), valid_times=parse_timelock_info(extra_conditions), ) @@ -765,7 +765,7 @@ async def transfer_did( trade_id=None, type=uint32(TransactionType.OUTGOING_TX.value), name=spend_bundle.name(), - memos=list(compute_memos(spend_bundle).items()), + memos=compute_memos(spend_bundle), valid_times=parse_timelock_info(extra_conditions), ) @@ -844,7 +844,7 @@ async def create_message_spend( trade_id=None, type=uint32(TransactionType.OUTGOING_TX.value), name=unsigned_spend_bundle.name(), - memos=list(compute_memos(unsigned_spend_bundle).items()), + memos=compute_memos(unsigned_spend_bundle), valid_times=parse_timelock_info(extra_conditions), ) async with action_scope.use() as interface: @@ -902,7 +902,7 @@ async def create_exit_spend(self, puzhash: bytes32, action_scope: WalletActionSc trade_id=None, type=uint32(TransactionType.OUTGOING_TX.value), name=bytes32.secret(), - memos=list(compute_memos(spend_bundle).items()), + memos=compute_memos(spend_bundle), valid_times=ConditionValidTimes(), ) ) @@ -987,7 +987,7 @@ async def create_attestment( trade_id=None, type=uint32(TransactionType.INCOMING_TX.value), name=bytes32.secret(), - memos=list(compute_memos(spend_bundle).items()), + memos=compute_memos(spend_bundle), valid_times=parse_timelock_info(extra_conditions), ) async with action_scope.use() as interface: @@ -1106,7 +1106,7 @@ async def recovery_spend( trade_id=None, type=uint32(TransactionType.OUTGOING_TX.value), name=bytes32.secret(), - memos=list(compute_memos(spend_bundle).items()), + memos=compute_memos(spend_bundle), valid_times=ConditionValidTimes(), ) ) @@ -1329,7 +1329,7 @@ async def generate_new_decentralised_id( trade_id=None, type=uint32(TransactionType.INCOMING_TX.value), name=full_spend.name(), - memos=[], + memos={}, valid_times=ConditionValidTimes(), ) async with action_scope.use() as interface: diff --git a/chia/wallet/nft_wallet/nft_wallet.py b/chia/wallet/nft_wallet/nft_wallet.py index 72882e184cb4..293b42f02b5d 100644 --- a/chia/wallet/nft_wallet/nft_wallet.py +++ b/chia/wallet/nft_wallet/nft_wallet.py @@ -629,7 +629,7 @@ async def generate_signed_transaction( trade_id=None, type=uint32(TransactionType.OUTGOING_TX.value), name=spend_bundle.name(), - memos=list(compute_memos(spend_bundle).items()), + memos=compute_memos(spend_bundle), valid_times=parse_timelock_info(extra_conditions), ) @@ -1418,7 +1418,7 @@ async def mint_from_did( trade_id=None, type=uint32(TransactionType.OUTGOING_TX.value), name=unsigned_spend_bundle.name(), - memos=list(compute_memos(unsigned_spend_bundle).items()), + memos=compute_memos(unsigned_spend_bundle), valid_times=parse_timelock_info(extra_conditions), ) ) diff --git a/chia/wallet/trade_manager.py b/chia/wallet/trade_manager.py index e4d8ee473e08..d0ea4143469c 100644 --- a/chia/wallet/trade_manager.py +++ b/chia/wallet/trade_manager.py @@ -373,7 +373,7 @@ async def cancel_pending_offers( trade_id=None, type=uint32(TransactionType.INCOMING_TX.value), name=cancellation_additions[0].name(), - memos=[], + memos={}, valid_times=valid_times, ) all_txs.append(incoming_tx) @@ -740,7 +740,7 @@ async def calculate_tx_records_for_offer(self, offer: Offer, validate: bool) -> trade_id=offer.name(), type=uint32(TransactionType.INCOMING_TRADE.value), name=std_hash(final_spend_bundle.name() + addition.name()), - memos=[(coin_id, [hint]) for coin_id, hint in hint_dict.items()], + memos={coin_id: [hint] for coin_id, hint in hint_dict.items()}, valid_times=valid_times, ) ) @@ -800,7 +800,7 @@ async def calculate_tx_records_for_offer(self, offer: Offer, validate: bool) -> trade_id=offer.name(), type=uint32(TransactionType.OUTGOING_TRADE.value), name=std_hash(final_spend_bundle.name() + removal_tree_hash), - memos=[(coin_id, [hint]) for coin_id, hint in hint_dict.items()], + memos={coin_id: [hint] for coin_id, hint in hint_dict.items()}, valid_times=valid_times, ) ) diff --git a/chia/wallet/transaction_record.py b/chia/wallet/transaction_record.py index a795b851afbc..2c3d2bd97205 100644 --- a/chia/wallet/transaction_record.py +++ b/chia/wallet/transaction_record.py @@ -57,7 +57,7 @@ class TransactionRecordOld(Streamable): # name is also called bundle_id and tx_id name: bytes32 - memos: list[tuple[bytes32, list[bytes]]] + memos: dict[bytes32, list[bytes]] def is_in_mempool(self) -> bool: # If one of the nodes we sent it to responded with success or pending, we return True @@ -81,9 +81,6 @@ def height_farmed(self, genesis_challenge: bytes32) -> Optional[uint32]: return uint32(block_index) return None - def get_memos(self) -> dict[bytes32, list[bytes]]: - return {coin_id: ms for coin_id, ms in self.memos} - @classmethod def from_json_dict_convenience( cls: builtins.type[_T_TransactionRecord], modified_tx_input: dict @@ -93,17 +90,6 @@ def from_json_dict_convenience( modified_tx["to_puzzle_hash"] = decode_puzzle_hash(modified_tx["to_address"]).hex() if "to_address" in modified_tx: del modified_tx["to_address"] - # Converts memos from a flat dict into a nested list - memos_dict: dict[str, list[str]] = {} - memos_list: list = [] - if "memos" in modified_tx: - for coin_id, memo in modified_tx["memos"].items(): - if coin_id not in memos_dict: - memos_dict[coin_id] = [] - memos_dict[coin_id].append(memo) - for coin_id, memos in memos_dict.items(): - memos_list.append((coin_id, memos)) - modified_tx["memos"] = memos_list return cls.from_json_dict(modified_tx) @classmethod @@ -118,12 +104,6 @@ def to_json_dict_convenience(self, config: dict) -> dict: prefix = config["network_overrides"]["config"][selected]["address_prefix"] formatted = self.to_json_dict() formatted["to_address"] = encode_puzzle_hash(self.to_puzzle_hash, prefix) - formatted["memos"] = { - coin_id.hex(): memo.hex() - for coin_id, memos in self.get_memos().items() - for memo in memos - if memo is not None - } return formatted def is_valid(self) -> bool: @@ -139,7 +119,11 @@ def is_valid(self) -> bool: return False def hint_dict(self) -> dict[bytes32, bytes32]: - return {coin_id: bytes32(memos[0]) for coin_id, memos in self.memos if len(memos) > 0 and len(memos[0]) == 32} + return { + coin_id: bytes32(memos[0]) + for coin_id, memos in self.memos.items() + if len(memos) > 0 and len(memos[0]) == 32 + } @streamable diff --git a/chia/wallet/vc_wallet/cr_cat_wallet.py b/chia/wallet/vc_wallet/cr_cat_wallet.py index 0862d3af55d6..83dcfe39dad9 100644 --- a/chia/wallet/vc_wallet/cr_cat_wallet.py +++ b/chia/wallet/vc_wallet/cr_cat_wallet.py @@ -265,7 +265,7 @@ async def add_crcat_coin(self, coin_spend: CoinSpend, coin: Coin, height: uint32 trade_id=None, type=uint32(TransactionType.INCOMING_CRCAT_PENDING), name=coin.name(), - memos=list(memos.items()), + memos=memos, valid_times=ConditionValidTimes(), ) await self.wallet_state_manager.tx_store.add_transaction_record(tx_record) @@ -674,7 +674,7 @@ async def generate_signed_transaction( trade_id=None, type=uint32(TransactionType.OUTGOING_TX.value), name=spend_bundle.name() if i == 0 else payment.to_program().get_tree_hash(), - memos=list(compute_memos(spend_bundle).items()), + memos=compute_memos(spend_bundle), valid_times=parse_timelock_info(extra_conditions), ) for i, payment in enumerate(payments) @@ -805,7 +805,7 @@ async def claim_pending_approval_balance( trade_id=None, type=uint32(TransactionType.INCOMING_TX.value), name=claim_bundle.name(), - memos=list(compute_memos(claim_bundle).items()), + memos=compute_memos(claim_bundle), valid_times=parse_timelock_info(extra_conditions), ) ) diff --git a/chia/wallet/vc_wallet/vc_wallet.py b/chia/wallet/vc_wallet/vc_wallet.py index c03ea76fc773..ed2b4a3a5d12 100644 --- a/chia/wallet/vc_wallet/vc_wallet.py +++ b/chia/wallet/vc_wallet/vc_wallet.py @@ -222,7 +222,7 @@ async def launch_new_vc( trade_id=None, type=uint32(TransactionType.OUTGOING_TX.value), name=spend_bundle.name(), - memos=list(compute_memos(spend_bundle).items()), + memos=compute_memos(spend_bundle), valid_times=parse_timelock_info(extra_conditions), ) ) @@ -359,7 +359,7 @@ async def generate_signed_transaction( trade_id=None, type=uint32(TransactionType.OUTGOING_TX.value), name=spend_bundle.name(), - memos=list(compute_memos(spend_bundle).items()), + memos=compute_memos(spend_bundle), valid_times=parse_timelock_info(extra_conditions), ) ) diff --git a/chia/wallet/wallet.py b/chia/wallet/wallet.py index ca4bae5daf02..4e6f23e4ac27 100644 --- a/chia/wallet/wallet.py +++ b/chia/wallet/wallet.py @@ -449,7 +449,7 @@ async def generate_signed_transaction( trade_id=None, type=uint32(TransactionType.OUTGOING_TX.value), name=spend_bundle.name(), - memos=list(compute_memos(spend_bundle).items()), + memos=compute_memos(spend_bundle), valid_times=parse_timelock_info(extra_conditions), ) ) diff --git a/chia/wallet/wallet_rpc_api.py b/chia/wallet/wallet_rpc_api.py index a37f5bc15737..ab866caa5031 100644 --- a/chia/wallet/wallet_rpc_api.py +++ b/chia/wallet/wallet_rpc_api.py @@ -465,7 +465,7 @@ async def rpc_endpoint( trade_id=None, type=uint32(0), name=bytes32.zeros, - memos=[], + memos={}, valid_times=ConditionValidTimes(), ) diff --git a/chia/wallet/wallet_state_manager.py b/chia/wallet/wallet_state_manager.py index 62f5e8f975af..2339918d121d 100644 --- a/chia/wallet/wallet_state_manager.py +++ b/chia/wallet/wallet_state_manager.py @@ -988,7 +988,7 @@ async def spend_clawback_coins( assert derivation_record is not None amount = uint64(amount + coin.amount) # Remove the clawback hint since it is unnecessary for the XCH coin - memos: list[bytes] = [] if len(incoming_tx.memos) == 0 else incoming_tx.memos[0][1][1:] + memos: list[bytes] = [] if len(incoming_tx.memos) == 0 else next(iter(incoming_tx.memos.items()))[1][1:] inner_puzzle: Program = self.main_wallet.puzzle_for_pk(derivation_record.pubkey) inner_solution: Program = self.main_wallet.make_solution( primaries=[ @@ -1065,7 +1065,7 @@ async def spend_clawback_coins( trade_id=None, type=uint32(TransactionType.OUTGOING_CLAWBACK), name=spend_bundle.name(), - memos=list(compute_memos(spend_bundle).items()), + memos=compute_memos(spend_bundle), valid_times=parse_timelock_info(extra_conditions), ) async with action_scope.use() as interface: @@ -1569,7 +1569,7 @@ async def handle_clawback( trade_id=None, type=uint32(TransactionType.OUTGOING_CLAWBACK), name=clawback_spend_bundle.name(), - memos=list(compute_memos(clawback_spend_bundle).items()), + memos=compute_memos(clawback_spend_bundle), valid_times=ConditionValidTimes(), ) await self.tx_store.add_transaction_record(tx_record) @@ -1611,7 +1611,7 @@ async def handle_clawback( ), # Use coin ID as the TX ID to mapping with the coin table name=coin_record.coin.name(), - memos=list(memos.items()), + memos=memos, valid_times=ConditionValidTimes(), ) await self.tx_store.add_transaction_record(tx_record) @@ -1810,7 +1810,7 @@ async def _add_coin_states( trade_id=None, type=uint32(tx_type), name=bytes32.secret(), - memos=[], + memos={}, valid_times=ConditionValidTimes(), ) await self.tx_store.add_transaction_record(tx_record) @@ -1890,7 +1890,7 @@ async def _add_coin_states( trade_id=None, type=uint32(TransactionType.OUTGOING_TX.value), name=tx_name, - memos=[], + memos={}, valid_times=ConditionValidTimes(), ) @@ -2215,7 +2215,7 @@ async def coin_added( trade_id=None, type=uint32(tx_type), name=coin_name, - memos=[], + memos={}, valid_times=ConditionValidTimes(), ) if tx_record.amount > 0: From 67e5a27efb8d7bdf8cf2072e1aac07123cd3dbdd Mon Sep 17 00:00:00 2001 From: Matt Date: Fri, 18 Jul 2025 11:21:18 +0200 Subject: [PATCH 2/7] Update streamable and fix tests --- chia/util/streamable.py | 4 +++- chia/wallet/did_wallet/did_wallet.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/chia/util/streamable.py b/chia/util/streamable.py index 869a8246c474..7d04210a01c1 100644 --- a/chia/util/streamable.py +++ b/chia/util/streamable.py @@ -303,7 +303,9 @@ def recurse_jsonify( elif isinstance(d, dict): new_dict = {} for name, val in d.items(): - new_dict[name] = next_recursion_step(val, None, **next_recursion_env) + new_dict[next_recursion_step(name, None, **next_recursion_env)] = next_recursion_step( + val, None, **next_recursion_env + ) return new_dict elif issubclass(type(d), bytes): diff --git a/chia/wallet/did_wallet/did_wallet.py b/chia/wallet/did_wallet/did_wallet.py index b2c0f86a1eb5..8e129e10ac63 100644 --- a/chia/wallet/did_wallet/did_wallet.py +++ b/chia/wallet/did_wallet/did_wallet.py @@ -329,7 +329,7 @@ async def get_pending_change_balance(self) -> uint64: for coin in record.additions: hint_dict = { coin_id: bytes32(memos[0]) - for coin_id, memos in record.memos + for coin_id, memos in record.memos.items() if len(memos) > 0 and len(memos[0]) == 32 } if (await self.wallet_state_manager.does_coin_belong_to_wallet(coin, self.id(), hint_dict)) and ( From 57d7ac658ef09ecf9506cc1d9847abd4d0dad370 Mon Sep 17 00:00:00 2001 From: Matt Date: Mon, 21 Jul 2025 11:29:54 +0200 Subject: [PATCH 3/7] Update test to accidentally fixed memos generation --- chia/_tests/wallet/test_wallet.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chia/_tests/wallet/test_wallet.py b/chia/_tests/wallet/test_wallet.py index 9b751c4847d9..72acf55da663 100644 --- a/chia/_tests/wallet/test_wallet.py +++ b/chia/_tests/wallet/test_wallet.py @@ -587,7 +587,7 @@ async def test_wallet_clawback_sent_self(self, wallet_environments: WalletTestFr assert txs["transactions"][0]["confirmed"] assert txs["transactions"][1]["confirmed"] assert txs["transactions"][0]["memos"] != txs["transactions"][1]["memos"] - assert next(iter(txs["transactions"][0]["memos"].values())) == b"Test".hex() + assert "0x" + b"Test".hex() in next(iter(txs["transactions"][0]["memos"].values())) @pytest.mark.parametrize( "wallet_environments", From dc4eb0767e4bbefa52b19b26e19a32ddff7049de Mon Sep 17 00:00:00 2001 From: Matt Date: Wed, 23 Jul 2025 16:01:16 +0200 Subject: [PATCH 4/7] Remove `TransactionRecord.*_json_dict_convenience` --- chia/_tests/cmds/cmd_test_utils.py | 2 + chia/_tests/cmds/wallet/test_consts.py | 2 + chia/_tests/cmds/wallet/test_did.py | 7 +-- chia/_tests/cmds/wallet/test_wallet.py | 11 +--- chia/_tests/core/data_layer/test_data_rpc.py | 2 +- chia/_tests/wallet/rpc/test_wallet_rpc.py | 8 +-- chia/_tests/wallet/test_transaction_store.py | 59 +++++++++++--------- chia/_tests/wallet/test_wallet.py | 1 + chia/cmds/wallet_funcs.py | 4 +- chia/data_layer/data_layer_wallet.py | 2 + chia/pools/pool_wallet.py | 2 + chia/wallet/cat_wallet/cat_wallet.py | 5 +- chia/wallet/did_wallet/did_wallet.py | 19 ++++--- chia/wallet/nft_wallet/nft_wallet.py | 2 + chia/wallet/trade_manager.py | 3 + chia/wallet/transaction_record.py | 37 +++--------- chia/wallet/vc_wallet/cr_cat_wallet.py | 3 + chia/wallet/vc_wallet/vc_wallet.py | 2 + chia/wallet/wallet.py | 4 +- chia/wallet/wallet_request_types.py | 5 +- chia/wallet/wallet_rpc_api.py | 12 ++-- chia/wallet/wallet_rpc_client.py | 10 ++-- chia/wallet/wallet_state_manager.py | 41 +++++++++----- chia/wallet/wallet_transaction_store.py | 10 +++- 24 files changed, 133 insertions(+), 120 deletions(-) diff --git a/chia/_tests/cmds/cmd_test_utils.py b/chia/_tests/cmds/cmd_test_utils.py index 44672b148cce..b0be04d1c1b8 100644 --- a/chia/_tests/cmds/cmd_test_utils.py +++ b/chia/_tests/cmds/cmd_test_utils.py @@ -121,6 +121,7 @@ async def get_transaction(self, transaction_id: bytes32) -> TransactionRecord: confirmed_at_height=uint32(1), created_at_time=uint64(1234), to_puzzle_hash=bytes32([1] * 32), + to_address=encode_puzzle_hash(bytes32([1] * 32), "xch"), amount=uint64(12345678), fee_amount=uint64(1234567), confirmed=False, @@ -271,6 +272,7 @@ async def send_transaction_multi( confirmed_at_height=uint32(1), created_at_time=uint64(1234), to_puzzle_hash=bytes32([1] * 32), + to_address=encode_puzzle_hash(bytes32([1] * 32), "xch"), amount=uint64(12345678), fee_amount=uint64(1234567), confirmed=False, diff --git a/chia/_tests/cmds/wallet/test_consts.py b/chia/_tests/cmds/wallet/test_consts.py index c5c06e406ca6..42cf60c50f01 100644 --- a/chia/_tests/cmds/wallet/test_consts.py +++ b/chia/_tests/cmds/wallet/test_consts.py @@ -4,6 +4,7 @@ from chia_rs.sized_bytes import bytes32 from chia_rs.sized_ints import uint32, uint64 +from chia.util.bech32m import encode_puzzle_hash from chia.wallet.conditions import ConditionValidTimes from chia.wallet.signer_protocol import KeyHints, SigningInstructions, TransactionInfo, UnsignedTransaction from chia.wallet.transaction_record import TransactionRecord @@ -27,6 +28,7 @@ def get_bytes32(bytes_index: int) -> bytes32: confirmed_at_height=uint32(1), created_at_time=uint64(1234), to_puzzle_hash=get_bytes32(1), + to_address=encode_puzzle_hash(get_bytes32(1), "xch"), amount=uint64(12345678), fee_amount=uint64(1234567), confirmed=False, diff --git a/chia/_tests/cmds/wallet/test_did.py b/chia/_tests/cmds/wallet/test_did.py index 4fefab7e2fd6..d017408d381d 100644 --- a/chia/_tests/cmds/wallet/test_did.py +++ b/chia/_tests/cmds/wallet/test_did.py @@ -13,7 +13,6 @@ from chia.types.blockchain_format.program import NIL, Program from chia.types.signing_mode import SigningMode from chia.util.bech32m import encode_puzzle_hash -from chia.util.config import load_config from chia.wallet.conditions import Condition, ConditionValidTimes, CreateCoinAnnouncement, CreatePuzzleAnnouncement from chia.wallet.did_wallet.did_info import did_recovery_is_nil from chia.wallet.util.curry_and_treehash import NIL_TREEHASH @@ -421,14 +420,10 @@ async def did_transfer_did( "150", ] # these are various things that should be in the output - config = load_config( - root_dir, - "config.yaml", - ) assert_list = [ f"Successfully transferred DID to {t_address}", f"Transaction ID: {get_bytes32(2).hex()}", - f"Transaction: {STD_TX.to_json_dict_convenience(config)}", + f"Transaction: {STD_TX.to_json_dict()}", ] run_cli_command_and_assert(capsys, root_dir, command_args, assert_list) expected_calls: logType = { diff --git a/chia/_tests/cmds/wallet/test_wallet.py b/chia/_tests/cmds/wallet/test_wallet.py index 38a7f2e35ba8..518692f5bddd 100644 --- a/chia/_tests/cmds/wallet/test_wallet.py +++ b/chia/_tests/cmds/wallet/test_wallet.py @@ -131,6 +131,7 @@ async def get_transactions( confirmed_at_height=uint32(1 + i), created_at_time=uint64(1234 + i), to_puzzle_hash=bytes32([1 + i] * 32), + to_address=encode_puzzle_hash(bytes32([1 + i] * 32), "xch"), amount=uint64(12345678 + i), fee_amount=uint64(1234567 + i), confirmed=False, @@ -340,6 +341,7 @@ async def send_transaction( confirmed_at_height=uint32(1), created_at_time=uint64(1234), to_puzzle_hash=get_bytes32(1), + to_address=encode_puzzle_hash(get_bytes32(1), "xch"), amount=uint64(12345678), fee_amount=uint64(1234567), confirmed=False, @@ -529,14 +531,7 @@ async def spend_clawback_coins( tx_hex_list = [get_bytes32(6).hex(), get_bytes32(7).hex(), get_bytes32(8).hex()] return { "transaction_ids": tx_hex_list, - "transactions": [ - STD_TX.to_json_dict_convenience( - { - "selected_network": "mainnet", - "network_overrides": {"config": {"mainnet": {"address_prefix": "xch"}}}, - } - ) - ], + "transactions": [STD_TX.to_json_dict()], } inst_rpc_client = ClawbackWalletRpcClient() diff --git a/chia/_tests/core/data_layer/test_data_rpc.py b/chia/_tests/core/data_layer/test_data_rpc.py index 89959a26703f..4ee942e50229 100644 --- a/chia/_tests/core/data_layer/test_data_rpc.py +++ b/chia/_tests/core/data_layer/test_data_rpc.py @@ -190,7 +190,7 @@ async def is_transaction_confirmed(api: WalletRpcApi, tx_id: bytes32) -> bool: except ValueError: # pragma: no cover return False - return True if TransactionRecord.from_json_dict_convenience(val["transaction"]).confirmed else False # mypy + return True if TransactionRecord.from_json_dict(val["transaction"]).confirmed else False # mypy async def farm_block_with_spend( diff --git a/chia/_tests/wallet/rpc/test_wallet_rpc.py b/chia/_tests/wallet/rpc/test_wallet_rpc.py index ec390bb5ce6f..6a49131053f6 100644 --- a/chia/_tests/wallet/rpc/test_wallet_rpc.py +++ b/chia/_tests/wallet/rpc/test_wallet_rpc.py @@ -404,7 +404,7 @@ async def test_send_transaction(wallet_rpc_environment: WalletRpcTestEnvironment }, ) assert response["success"] - tx = TransactionRecord.from_json_dict_convenience(response["transactions"][0]) + tx = TransactionRecord.from_json_dict(response["transactions"][0]) [ byte_deserialize_clvm_streamable( bytes.fromhex(utx), UnsignedTransaction, translation_layer=BLIND_SIGNER_TRANSLATION @@ -456,9 +456,7 @@ async def test_push_transactions(wallet_rpc_environment: WalletRpcTestEnvironmen PushTransactions(transactions=[tx], fee=uint64(10)), DEFAULT_TX_CONFIG, ) - resp = await client.fetch( - "push_transactions", {"transactions": [tx.to_json_dict_convenience(wallet_node.config)], "fee": 10} - ) + resp = await client.fetch("push_transactions", {"transactions": [tx.to_json_dict()], "fee": 10}) assert resp["success"] resp = await client.fetch("push_transactions", {"transactions": [bytes(tx).hex()], "fee": 10}) assert resp["success"] @@ -912,7 +910,7 @@ async def test_spend_clawback_coins(wallet_rpc_environment: WalletRpcTestEnviron assert resp["success"] assert len(resp["transaction_ids"]) == 2 for _tx in resp["transactions"]: - clawback_tx = TransactionRecord.from_json_dict_convenience(_tx) + clawback_tx = TransactionRecord.from_json_dict(_tx) if clawback_tx.spend_bundle is not None: await time_out_assert_not_none( 10, full_node_api.full_node.mempool_manager.get_spendbundle, clawback_tx.spend_bundle.name() diff --git a/chia/_tests/wallet/test_transaction_store.py b/chia/_tests/wallet/test_transaction_store.py index 00b89f1f9d70..780eeed10fbe 100644 --- a/chia/_tests/wallet/test_transaction_store.py +++ b/chia/_tests/wallet/test_transaction_store.py @@ -11,6 +11,7 @@ from chia._tests.util.db_connection import DBConnection from chia.types.blockchain_format.coin import Coin from chia.types.mempool_inclusion_status import MempoolInclusionStatus +from chia.util.bech32m import encode_puzzle_hash from chia.util.errors import Err from chia.wallet.conditions import ConditionValidTimes from chia.wallet.transaction_record import TransactionRecord, TransactionRecordOld, minimum_send_attempts @@ -32,7 +33,7 @@ tr1 = TransactionRecord( uint32(0), # confirmed height uint64(1000), # created_at_time - bytes32(bytes32.random(module_seeded_random)), # to_puzzle_hash + bytes32(bytes32.zeros), # to_puzzle_hash uint64(1234), # amount uint64(12), # fee_amount False, # confirmed @@ -47,13 +48,19 @@ bytes32(bytes32.random(module_seeded_random)), # name {}, # memos ConditionValidTimes(), + encode_puzzle_hash(bytes32(bytes32.zeros), "xch"), ) +MINIMUM_CONFIG = { + "network_overrides": {"config": {"testnet": {"address_prefix": "txch"}}}, + "selected_network": "testnet", +} + @pytest.mark.anyio async def test_add() -> None: async with DBConnection(1) as db_wrapper: - store = await WalletTransactionStore.create(db_wrapper) + store = await WalletTransactionStore.create(db_wrapper, MINIMUM_CONFIG) assert await store.get_transaction_record(tr1.name) is None await store.add_transaction_record(tr1) @@ -63,7 +70,7 @@ async def test_add() -> None: @pytest.mark.anyio async def test_delete() -> None: async with DBConnection(1) as db_wrapper: - store = await WalletTransactionStore.create(db_wrapper) + store = await WalletTransactionStore.create(db_wrapper, MINIMUM_CONFIG) await store.add_transaction_record(tr1) assert await store.get_transaction_record(tr1.name) == tr1 @@ -74,7 +81,7 @@ async def test_delete() -> None: @pytest.mark.anyio async def test_set_confirmed() -> None: async with DBConnection(1) as db_wrapper: - store = await WalletTransactionStore.create(db_wrapper) + store = await WalletTransactionStore.create(db_wrapper, MINIMUM_CONFIG) await store.add_transaction_record(tr1) await store.set_confirmed(tr1.name, uint32(100)) @@ -87,7 +94,7 @@ async def test_set_confirmed() -> None: @pytest.mark.anyio async def test_increment_sent_noop(seeded_random: random.Random) -> None: async with DBConnection(1) as db_wrapper: - store = await WalletTransactionStore.create(db_wrapper) + store = await WalletTransactionStore.create(db_wrapper, MINIMUM_CONFIG) assert ( await store.increment_sent( @@ -100,7 +107,7 @@ async def test_increment_sent_noop(seeded_random: random.Random) -> None: @pytest.mark.anyio async def test_increment_sent() -> None: async with DBConnection(1) as db_wrapper: - store = await WalletTransactionStore.create(db_wrapper) + store = await WalletTransactionStore.create(db_wrapper, MINIMUM_CONFIG) await store.add_transaction_record(tr1) tr = await store.get_transaction_record(tr1.name) @@ -126,7 +133,7 @@ async def test_increment_sent() -> None: @pytest.mark.anyio async def test_increment_sent_error() -> None: async with DBConnection(1) as db_wrapper: - store = await WalletTransactionStore.create(db_wrapper) + store = await WalletTransactionStore.create(db_wrapper, MINIMUM_CONFIG) await store.add_transaction_record(tr1) tr = await store.get_transaction_record(tr1.name) @@ -156,7 +163,7 @@ def test_filter_ok_mempool_status() -> None: @pytest.mark.anyio async def test_tx_reorged_update() -> None: async with DBConnection(1) as db_wrapper: - store = await WalletTransactionStore.create(db_wrapper) + store = await WalletTransactionStore.create(db_wrapper, MINIMUM_CONFIG) tr = dataclasses.replace(tr1, sent=uint32(2), sent_to=[("peer1", uint8(1), None), ("peer2", uint8(1), None)]) await store.add_transaction_record(tr) @@ -173,7 +180,7 @@ async def test_tx_reorged_update() -> None: @pytest.mark.anyio async def test_tx_reorged_add() -> None: async with DBConnection(1) as db_wrapper: - store = await WalletTransactionStore.create(db_wrapper) + store = await WalletTransactionStore.create(db_wrapper, MINIMUM_CONFIG) tr = dataclasses.replace(tr1, sent=uint32(2), sent_to=[("peer1", uint8(1), None), ("peer2", uint8(1), None)]) @@ -187,7 +194,7 @@ async def test_tx_reorged_add() -> None: @pytest.mark.anyio async def test_get_tx_record(seeded_random: random.Random) -> None: async with DBConnection(1) as db_wrapper: - store = await WalletTransactionStore.create(db_wrapper) + store = await WalletTransactionStore.create(db_wrapper, MINIMUM_CONFIG) tr2 = dataclasses.replace(tr1, name=bytes32.random(seeded_random)) tr3 = dataclasses.replace(tr1, name=bytes32.random(seeded_random)) @@ -212,7 +219,7 @@ async def test_get_tx_record(seeded_random: random.Random) -> None: @pytest.mark.anyio async def test_get_farming_rewards(seeded_random: random.Random) -> None: async with DBConnection(1) as db_wrapper: - store = await WalletTransactionStore.create(db_wrapper) + store = await WalletTransactionStore.create(db_wrapper, MINIMUM_CONFIG) test_trs: list[TransactionRecord] = [] # tr1 is type OUTGOING_TX @@ -249,7 +256,7 @@ async def test_get_farming_rewards(seeded_random: random.Random) -> None: @pytest.mark.anyio async def test_get_all_unconfirmed(seeded_random: random.Random) -> None: async with DBConnection(1) as db_wrapper: - store = await WalletTransactionStore.create(db_wrapper) + store = await WalletTransactionStore.create(db_wrapper, MINIMUM_CONFIG) tr2 = dataclasses.replace( tr1, name=bytes32.random(seeded_random), confirmed=True, confirmed_at_height=uint32(100) @@ -263,7 +270,7 @@ async def test_get_all_unconfirmed(seeded_random: random.Random) -> None: @pytest.mark.anyio async def test_get_unconfirmed_for_wallet(seeded_random: random.Random) -> None: async with DBConnection(1) as db_wrapper: - store = await WalletTransactionStore.create(db_wrapper) + store = await WalletTransactionStore.create(db_wrapper, MINIMUM_CONFIG) tr2 = dataclasses.replace( tr1, name=bytes32.random(seeded_random), confirmed=True, confirmed_at_height=uint32(100) @@ -282,7 +289,7 @@ async def test_get_unconfirmed_for_wallet(seeded_random: random.Random) -> None: @pytest.mark.anyio async def test_transaction_count_for_wallet(seeded_random: random.Random) -> None: async with DBConnection(1) as db_wrapper: - store = await WalletTransactionStore.create(db_wrapper) + store = await WalletTransactionStore.create(db_wrapper, MINIMUM_CONFIG) tr2 = dataclasses.replace(tr1, name=bytes32.random(seeded_random), wallet_id=uint32(2)) @@ -322,7 +329,7 @@ async def test_transaction_count_for_wallet(seeded_random: random.Random) -> Non @pytest.mark.anyio async def test_all_transactions_for_wallet(seeded_random: random.Random) -> None: async with DBConnection(1) as db_wrapper: - store = await WalletTransactionStore.create(db_wrapper) + store = await WalletTransactionStore.create(db_wrapper, MINIMUM_CONFIG) test_trs: list[TransactionRecord] = [] for wallet_id in [1, 2]: @@ -373,7 +380,7 @@ def cmp(lhs: list[Any], rhs: list[Any]) -> bool: @pytest.mark.anyio async def test_get_all_transactions(seeded_random: random.Random) -> None: async with DBConnection(1) as db_wrapper: - store = await WalletTransactionStore.create(db_wrapper) + store = await WalletTransactionStore.create(db_wrapper, MINIMUM_CONFIG) test_trs: list[TransactionRecord] = [] assert await store.get_all_transactions() == [] @@ -390,7 +397,7 @@ async def test_get_all_transactions(seeded_random: random.Random) -> None: @pytest.mark.anyio async def test_get_transaction_above(seeded_random: random.Random) -> None: async with DBConnection(1) as db_wrapper: - store = await WalletTransactionStore.create(db_wrapper) + store = await WalletTransactionStore.create(db_wrapper, MINIMUM_CONFIG) test_trs: list[TransactionRecord] = [] assert await store.get_transaction_above(uint32(0)) == [] @@ -410,7 +417,7 @@ async def test_get_transaction_above(seeded_random: random.Random) -> None: @pytest.mark.anyio async def test_get_tx_by_trade_id(seeded_random: random.Random) -> None: async with DBConnection(1) as db_wrapper: - store = await WalletTransactionStore.create(db_wrapper) + store = await WalletTransactionStore.create(db_wrapper, MINIMUM_CONFIG) tr2 = dataclasses.replace(tr1, name=bytes32.random(seeded_random), trade_id=bytes32.random(seeded_random)) tr3 = dataclasses.replace(tr1, name=bytes32.random(seeded_random), trade_id=bytes32.random(seeded_random)) @@ -442,7 +449,7 @@ async def test_get_tx_by_trade_id(seeded_random: random.Random) -> None: @pytest.mark.anyio async def test_rollback_to_block(seeded_random: random.Random) -> None: async with DBConnection(1) as db_wrapper: - store = await WalletTransactionStore.create(db_wrapper) + store = await WalletTransactionStore.create(db_wrapper, MINIMUM_CONFIG) test_trs: list[TransactionRecord] = [] for height in range(10): @@ -465,7 +472,7 @@ async def test_rollback_to_block(seeded_random: random.Random) -> None: @pytest.mark.anyio async def test_delete_unconfirmed(seeded_random: random.Random) -> None: async with DBConnection(1) as db_wrapper: - store = await WalletTransactionStore.create(db_wrapper) + store = await WalletTransactionStore.create(db_wrapper, MINIMUM_CONFIG) tr2 = dataclasses.replace(tr1, name=bytes32.random(seeded_random), confirmed=True) tr3 = dataclasses.replace(tr1, name=bytes32.random(seeded_random), confirmed=True, wallet_id=uint32(2)) @@ -493,7 +500,7 @@ async def test_delete_unconfirmed(seeded_random: random.Random) -> None: @pytest.mark.anyio async def test_get_transactions_between_confirmed(seeded_random: random.Random) -> None: async with DBConnection(1) as db_wrapper: - store = await WalletTransactionStore.create(db_wrapper) + store = await WalletTransactionStore.create(db_wrapper, MINIMUM_CONFIG) tr2 = dataclasses.replace( tr1, name=bytes32.random(seeded_random), confirmed=True, confirmed_at_height=uint32(1) @@ -585,7 +592,7 @@ async def test_get_transactions_between_confirmed(seeded_random: random.Random) @pytest.mark.anyio async def test_get_transactions_between_relevance(seeded_random: random.Random) -> None: async with DBConnection(1) as db_wrapper: - store = await WalletTransactionStore.create(db_wrapper) + store = await WalletTransactionStore.create(db_wrapper, MINIMUM_CONFIG) t1 = dataclasses.replace( tr1, @@ -716,7 +723,7 @@ async def test_get_transactions_between_relevance(seeded_random: random.Random) @pytest.mark.anyio async def test_get_transactions_between_to_puzzle_hash(seeded_random: random.Random) -> None: async with DBConnection(1) as db_wrapper: - store = await WalletTransactionStore.create(db_wrapper) + store = await WalletTransactionStore.create(db_wrapper, MINIMUM_CONFIG) ph1 = bytes32.random(seeded_random) ph2 = bytes32.random(seeded_random) @@ -762,7 +769,7 @@ async def test_get_transactions_between_to_puzzle_hash(seeded_random: random.Ran @pytest.mark.anyio async def test_get_not_sent(seeded_random: random.Random) -> None: async with DBConnection(1) as db_wrapper: - store = await WalletTransactionStore.create(db_wrapper) + store = await WalletTransactionStore.create(db_wrapper, MINIMUM_CONFIG) tr2 = dataclasses.replace( tr1, name=bytes32.random(seeded_random), confirmed=True, confirmed_at_height=uint32(1) @@ -881,7 +888,7 @@ async def test_valid_times_migration() -> None: ), ) - store = await WalletTransactionStore.create(db_wrapper) + store = await WalletTransactionStore.create(db_wrapper, MINIMUM_CONFIG) rec = await store.get_transaction_record(old_record.name) assert rec is not None assert rec.valid_times == ConditionValidTimes() @@ -890,7 +897,7 @@ async def test_valid_times_migration() -> None: @pytest.mark.anyio async def test_large_tx_record_query() -> None: async with DBConnection(1) as db_wrapper: - store = await WalletTransactionStore.create(db_wrapper) + store = await WalletTransactionStore.create(db_wrapper, MINIMUM_CONFIG) tx_records_to_insert = [] for _ in range(db_wrapper.host_parameter_limit + 1): name = bytes32.random() diff --git a/chia/_tests/wallet/test_wallet.py b/chia/_tests/wallet/test_wallet.py index 72acf55da663..af8345def433 100644 --- a/chia/_tests/wallet/test_wallet.py +++ b/chia/_tests/wallet/test_wallet.py @@ -1721,6 +1721,7 @@ async def test_wallet_prevent_fee_theft(self, wallet_environments: WalletTestFra confirmed_at_height=uint32(0), created_at_time=uint64(0), to_puzzle_hash=bytes32(32 * b"0"), + to_address=encode_puzzle_hash(bytes32(32 * b"0"), "txch"), amount=uint64(0), fee_amount=uint64(0), confirmed=False, diff --git a/chia/cmds/wallet_funcs.py b/chia/cmds/wallet_funcs.py index 5052a2fe6dbc..37d3a490cd90 100644 --- a/chia/cmds/wallet_funcs.py +++ b/chia/cmds/wallet_funcs.py @@ -1152,7 +1152,7 @@ async def transfer_did( if push: print(f"Successfully transferred DID to {target_address}") print(f"Transaction ID: {response.transaction_id.hex()}") - print(f"Transaction: {response.transaction.to_json_dict_convenience(config)}") + print(f"Transaction: {response.transaction.to_json_dict()}") return response.transactions except Exception as e: print(f"Failed to transfer DID: {e}") @@ -1651,7 +1651,7 @@ async def spend_clawback( timelock_info=condition_valid_times, ) print(str(response)) - return [TransactionRecord.from_json_dict_convenience(tx) for tx in response["transactions"]] + return [TransactionRecord.from_json_dict(tx) for tx in response["transactions"]] async def mint_vc( diff --git a/chia/data_layer/data_layer_wallet.py b/chia/data_layer/data_layer_wallet.py index d602a93590c8..58f47142c98b 100644 --- a/chia/data_layer/data_layer_wallet.py +++ b/chia/data_layer/data_layer_wallet.py @@ -538,6 +538,7 @@ async def create_update_state_spend( confirmed_at_height=uint32(0), created_at_time=uint64(int(time.time())), to_puzzle_hash=new_puz_hash, + to_address=self.wallet_state_manager.encode_puzzle_hash(new_puz_hash), amount=uint64(singleton_record.lineage_proof.amount), fee_amount=fee, confirmed=False, @@ -733,6 +734,7 @@ async def delete_mirror( confirmed_at_height=uint32(0), created_at_time=uint64(int(time.time())), to_puzzle_hash=new_puzhash, + to_address=self.wallet_state_manager.encode_puzzle_hash(new_puzhash), amount=uint64(mirror_coin.amount), fee_amount=fee, confirmed=False, diff --git a/chia/pools/pool_wallet.py b/chia/pools/pool_wallet.py index 5566f8c63921..6eb06c05c7b9 100644 --- a/chia/pools/pool_wallet.py +++ b/chia/pools/pool_wallet.py @@ -527,6 +527,7 @@ async def generate_travel_transactions(self, fee: uint64, action_scope: WalletAc confirmed_at_height=uint32(0), created_at_time=uint64(int(time.time())), to_puzzle_hash=new_full_puzzle.get_tree_hash(), + to_address=self.wallet_state_manager.encode_puzzle_hash(new_full_puzzle.get_tree_hash()), amount=uint64(1), fee_amount=fee, confirmed=False, @@ -800,6 +801,7 @@ async def claim_pool_rewards( confirmed_at_height=uint32(0), created_at_time=current_time, to_puzzle_hash=current_state.current.target_puzzle_hash, + to_address=self.wallet_state_manager.encode_puzzle_hash(current_state.current.target_puzzle_hash), amount=uint64(total_amount), fee_amount=fee, # This will not be double counted in self.standard_wallet confirmed=False, diff --git a/chia/wallet/cat_wallet/cat_wallet.py b/chia/wallet/cat_wallet/cat_wallet.py index fd88fb07d200..eb2046aa5738 100644 --- a/chia/wallet/cat_wallet/cat_wallet.py +++ b/chia/wallet/cat_wallet/cat_wallet.py @@ -172,10 +172,12 @@ async def create_new_cat_wallet( raise ValueError("Internal Error, unable to generate new CAT coin") cat_pid: bytes32 = cat_coin.parent_coin_info + converted_ph = await self.convert_puzzle_hash(cat_coin.puzzle_hash) cat_record = TransactionRecord( confirmed_at_height=uint32(0), created_at_time=uint64(int(time.time())), - to_puzzle_hash=(await self.convert_puzzle_hash(cat_coin.puzzle_hash)), + to_puzzle_hash=converted_ph, + to_address=self.wallet_state_manager.encode_puzzle_hash(converted_ph), amount=uint64(cat_coin.amount), fee_amount=fee, confirmed=False, @@ -796,6 +798,7 @@ async def generate_signed_transaction( confirmed_at_height=uint32(0), created_at_time=uint64(int(time.time())), to_puzzle_hash=puzzle_hashes[0], + to_address=self.wallet_state_manager.encode_puzzle_hash(puzzle_hashes[0]), amount=uint64(payment_sum), fee_amount=fee, confirmed=False, diff --git a/chia/wallet/did_wallet/did_wallet.py b/chia/wallet/did_wallet/did_wallet.py index e6faa34f706e..c0f4b6087bf3 100644 --- a/chia/wallet/did_wallet/did_wallet.py +++ b/chia/wallet/did_wallet/did_wallet.py @@ -648,12 +648,12 @@ async def create_update_spend( action_scope, extra_conditions=(AssertCoinAnnouncement(asserted_id=coin_name, asserted_msg=coin_name),), ) + to_ph = await action_scope.get_puzzle_hash(self.wallet_state_manager, override_reuse_puzhash_with=True) 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 - ), + to_puzzle_hash=to_ph, + to_address=self.wallet_state_manager.encode_puzzle_hash(to_ph), amount=uint64(coin.amount), fee_amount=uint64(0), confirmed=False, @@ -734,12 +734,12 @@ async def transfer_did( action_scope, extra_conditions=(AssertCoinAnnouncement(asserted_id=coin_name, asserted_msg=coin_name),), ) + to_ph = await action_scope.get_puzzle_hash(self.wallet_state_manager, override_reuse_puzhash_with=True) 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 - ), + to_puzzle_hash=to_ph, + to_address=self.wallet_state_manager.encode_puzzle_hash(to_ph), amount=uint64(coin.amount), fee_amount=fee, confirmed=False, @@ -819,6 +819,7 @@ async def create_message_spend( confirmed_at_height=uint32(0), created_at_time=uint64(int(time.time())), to_puzzle_hash=p2_ph, + to_address=self.wallet_state_manager.encode_puzzle_hash(p2_ph), amount=uint64(coin.amount), fee_amount=uint64(0), confirmed=False, @@ -1024,13 +1025,13 @@ async def generate_new_decentralised_id( assert self.did_info.origin_coin is not None assert self.did_info.current_inner is not None + to_ph = await action_scope.get_puzzle_hash(self.wallet_state_manager, override_reuse_puzhash_with=True) did_record = TransactionRecord( confirmed_at_height=uint32(0), created_at_time=uint64(int(time.time())), amount=uint64(amount), - to_puzzle_hash=await action_scope.get_puzzle_hash( - self.wallet_state_manager, override_reuse_puzhash_with=True - ), + to_puzzle_hash=to_ph, + to_address=self.wallet_state_manager.encode_puzzle_hash(to_ph), fee_amount=fee, confirmed=False, sent=uint32(0), diff --git a/chia/wallet/nft_wallet/nft_wallet.py b/chia/wallet/nft_wallet/nft_wallet.py index 293b42f02b5d..1020c4eb130f 100644 --- a/chia/wallet/nft_wallet/nft_wallet.py +++ b/chia/wallet/nft_wallet/nft_wallet.py @@ -617,6 +617,7 @@ async def generate_signed_transaction( confirmed_at_height=uint32(0), created_at_time=uint64(int(time.time())), to_puzzle_hash=puzzle_hashes[0], + to_address=self.wallet_state_manager.encode_puzzle_hash(puzzle_hashes[0]), amount=uint64(payment_sum), fee_amount=fee, confirmed=False, @@ -1406,6 +1407,7 @@ async def mint_from_did( confirmed_at_height=uint32(0), created_at_time=uint64(int(time.time())), to_puzzle_hash=innerpuz.get_tree_hash(), + to_address=self.wallet_state_manager.encode_puzzle_hash(innerpuz.get_tree_hash()), amount=uint64(1), fee_amount=fee, confirmed=False, diff --git a/chia/wallet/trade_manager.py b/chia/wallet/trade_manager.py index d0ea4143469c..aea47ba670c5 100644 --- a/chia/wallet/trade_manager.py +++ b/chia/wallet/trade_manager.py @@ -361,6 +361,7 @@ async def cancel_pending_offers( confirmed_at_height=uint32(0), created_at_time=uint64(int(time.time())), to_puzzle_hash=new_ph, + to_address=self.wallet_state_manager.encode_puzzle_hash(new_ph), amount=uint64(coin.amount), fee_amount=fee, confirmed=False, @@ -728,6 +729,7 @@ async def calculate_tx_records_for_offer(self, offer: Offer, validate: bool) -> confirmed_at_height=uint32(0), created_at_time=uint64(int(time.time())), to_puzzle_hash=to_puzzle_hash, + to_address=self.wallet_state_manager.encode_puzzle_hash(to_puzzle_hash), amount=uint64(addition.amount), fee_amount=uint64(0), confirmed=False, @@ -788,6 +790,7 @@ async def calculate_tx_records_for_offer(self, offer: Offer, validate: bool) -> confirmed_at_height=uint32(0), created_at_time=uint64(int(time.time())), to_puzzle_hash=to_puzzle_hash, + to_address=self.wallet_state_manager.encode_puzzle_hash(to_puzzle_hash), amount=uint64(sent_amount), fee_amount=all_fees, confirmed=False, diff --git a/chia/wallet/transaction_record.py b/chia/wallet/transaction_record.py index 2c3d2bd97205..c11c25d6cbef 100644 --- a/chia/wallet/transaction_record.py +++ b/chia/wallet/transaction_record.py @@ -1,8 +1,7 @@ from __future__ import annotations -import builtins from dataclasses import dataclass -from typing import Any, Generic, Optional, TypeVar +from typing import Generic, Optional, TypeVar from chia_rs import SpendBundle from chia_rs.sized_bytes import bytes32 @@ -11,7 +10,7 @@ from chia.consensus.coinbase import farmer_parent_id, pool_parent_id from chia.types.blockchain_format.coin import Coin from chia.types.mempool_inclusion_status import MempoolInclusionStatus -from chia.util.bech32m import decode_puzzle_hash, encode_puzzle_hash +from chia.util.bech32m import decode_puzzle_hash from chia.util.errors import Err from chia.util.streamable import Streamable, streamable from chia.wallet.conditions import ConditionValidTimes @@ -19,7 +18,6 @@ from chia.wallet.wallet_spend_bundle import WalletSpendBundle T = TypeVar("T") -_T_TransactionRecord = TypeVar("_T_TransactionRecord", bound="TransactionRecordOld") minimum_send_attempts = 6 @@ -81,31 +79,6 @@ def height_farmed(self, genesis_challenge: bytes32) -> Optional[uint32]: return uint32(block_index) return None - @classmethod - def from_json_dict_convenience( - cls: builtins.type[_T_TransactionRecord], modified_tx_input: dict - ) -> _T_TransactionRecord: - modified_tx = modified_tx_input.copy() - if "to_address" in modified_tx: - modified_tx["to_puzzle_hash"] = decode_puzzle_hash(modified_tx["to_address"]).hex() - if "to_address" in modified_tx: - del modified_tx["to_address"] - return cls.from_json_dict(modified_tx) - - @classmethod - def from_json_dict(cls: builtins.type[_T_TransactionRecord], json_dict: dict[str, Any]) -> _T_TransactionRecord: - try: - return super().from_json_dict(json_dict) - except Exception: - return cls.from_json_dict_convenience(json_dict) - - def to_json_dict_convenience(self, config: dict) -> dict: - selected = config["selected_network"] - prefix = config["network_overrides"]["config"][selected]["address_prefix"] - formatted = self.to_json_dict() - formatted["to_address"] = encode_puzzle_hash(self.to_puzzle_hash, prefix) - return formatted - def is_valid(self) -> bool: if len(self.sent_to) < minimum_send_attempts: # we haven't tried enough peers yet @@ -130,6 +103,12 @@ def hint_dict(self) -> dict[bytes32, bytes32]: @dataclass(frozen=True) class TransactionRecord(TransactionRecordOld): valid_times: ConditionValidTimes + to_address: str + + def __post_init__(self) -> None: + if decode_puzzle_hash(self.to_address) != self.to_puzzle_hash: + raise ValueError("Invalid tx record initialization, to_address must match to_puzzle_hash") + return super().__post_init__() @streamable diff --git a/chia/wallet/vc_wallet/cr_cat_wallet.py b/chia/wallet/vc_wallet/cr_cat_wallet.py index 83dcfe39dad9..16720417793f 100644 --- a/chia/wallet/vc_wallet/cr_cat_wallet.py +++ b/chia/wallet/vc_wallet/cr_cat_wallet.py @@ -253,6 +253,7 @@ async def add_crcat_coin(self, coin_spend: CoinSpend, coin: Coin, height: uint32 confirmed_at_height=height, created_at_time=uint64(created_timestamp), to_puzzle_hash=hint_dict[coin.name()], + to_address=self.wallet_state_manager.encode_puzzle_hash(hint_dict[coin.name()]), amount=uint64(coin.amount), fee_amount=uint64(0), confirmed=True, @@ -662,6 +663,7 @@ async def generate_signed_transaction( confirmed_at_height=uint32(0), created_at_time=uint64(int(time.time())), to_puzzle_hash=payment.puzzle_hash, + to_address=self.wallet_state_manager.encode_puzzle_hash(payment.puzzle_hash), amount=payment.amount, fee_amount=fee, confirmed=False, @@ -793,6 +795,7 @@ async def claim_pending_approval_balance( confirmed_at_height=uint32(0), created_at_time=uint64(int(time.time())), to_puzzle_hash=to_puzzle_hash, + to_address=self.wallet_state_manager.encode_puzzle_hash(to_puzzle_hash), amount=uint64(sum(c.amount for c in coins)), fee_amount=fee, confirmed=False, diff --git a/chia/wallet/vc_wallet/vc_wallet.py b/chia/wallet/vc_wallet/vc_wallet.py index 9cf7ef708521..f9b68e83a433 100644 --- a/chia/wallet/vc_wallet/vc_wallet.py +++ b/chia/wallet/vc_wallet/vc_wallet.py @@ -210,6 +210,7 @@ async def launch_new_vc( confirmed_at_height=uint32(0), created_at_time=now, to_puzzle_hash=inner_puzzle_hash, + to_address=self.wallet_state_manager.encode_puzzle_hash(inner_puzzle_hash), amount=uint64(1), fee_amount=uint64(fee), confirmed=False, @@ -347,6 +348,7 @@ async def generate_signed_transaction( confirmed_at_height=uint32(0), created_at_time=now, to_puzzle_hash=puzzle_hashes[0], + to_address=self.wallet_state_manager.encode_puzzle_hash(puzzle_hashes[0]), amount=uint64(1), fee_amount=uint64(fee), confirmed=False, diff --git a/chia/wallet/wallet.py b/chia/wallet/wallet.py index 4e6f23e4ac27..45bc5488eaf6 100644 --- a/chia/wallet/wallet.py +++ b/chia/wallet/wallet.py @@ -431,12 +431,14 @@ async def generate_signed_transaction( else: assert output_amount == input_amount + to_ph = add_list[0].puzzle_hash if len(add_list) > 0 else bytes32.zeros async with action_scope.use() as interface: interface.side_effects.transactions.append( TransactionRecord( confirmed_at_height=uint32(0), created_at_time=now, - to_puzzle_hash=add_list[0].puzzle_hash if len(add_list) > 0 else bytes32.zeros, + to_puzzle_hash=to_ph, + to_address=self.wallet_state_manager.encode_puzzle_hash(to_ph), amount=uint64(non_change_amount), fee_amount=uint64(fee), confirmed=False, diff --git a/chia/wallet/wallet_request_types.py b/chia/wallet/wallet_request_types.py index 7a3d18e4b97b..c51ea19ae24d 100644 --- a/chia/wallet/wallet_request_types.py +++ b/chia/wallet/wallet_request_types.py @@ -920,10 +920,7 @@ def from_json_dict(cls, json_dict: dict[str, Any]) -> PushTransactions: if isinstance(transaction_hexstr_or_json, str): tx = TransactionRecord.from_bytes(hexstr_to_bytes(transaction_hexstr_or_json)) else: - try: - tx = TransactionRecord.from_json_dict_convenience(transaction_hexstr_or_json) - except AttributeError: - tx = TransactionRecord.from_json_dict(transaction_hexstr_or_json) + tx = TransactionRecord.from_json_dict(transaction_hexstr_or_json) transactions.append(tx) json_dict["transactions"] = [tx.to_json_dict() for tx in transactions] diff --git a/chia/wallet/wallet_rpc_api.py b/chia/wallet/wallet_rpc_api.py index 78dba440a748..c30ba42ed320 100644 --- a/chia/wallet/wallet_rpc_api.py +++ b/chia/wallet/wallet_rpc_api.py @@ -340,10 +340,7 @@ async def rpc_endpoint( else: response["unsigned_transactions"] = [tx.to_json_dict() for tx in unsigned_txs] - response["transactions"] = [ - TransactionRecord.to_json_dict_convenience(tx, self.service.config) - for tx in action_scope.side_effects.transactions - ] + response["transactions"] = [tx.to_json_dict() for tx in action_scope.side_effects.transactions] # Some backwards compatibility code here because transaction information being returned was not uniform # until the "transactions" key was applied to all of them. Unfortunately, since .add_pending_transactions @@ -447,6 +444,7 @@ async def rpc_endpoint( confirmed_at_height=uint32(0), created_at_time=uint64(0), to_puzzle_hash=bytes32.zeros, + to_address=encode_puzzle_hash(bytes32.zeros, "replace"), amount=uint64(0), fee_amount=uint64(0), confirmed=False, @@ -1304,7 +1302,7 @@ async def get_transaction(self, request: dict[str, Any]) -> EndpointResult: raise ValueError(f"Transaction 0x{transaction_id.hex()} not found") return { - "transaction": (await self._convert_tx_puzzle_hash(tr)).to_json_dict_convenience(self.service.config), + "transaction": (await self._convert_tx_puzzle_hash(tr)).to_json_dict(), "transaction_id": tr.name, } @@ -1518,7 +1516,7 @@ async def get_transactions(self, request: dict[str, Any]) -> EndpointResult: tx_list = [] # Format for clawback transactions for tr in transactions: - tx = (await self._convert_tx_puzzle_hash(tr)).to_json_dict_convenience(self.service.config) + tx = (await self._convert_tx_puzzle_hash(tr)).to_json_dict() tx_list.append(tx) if tx["type"] not in CLAWBACK_INCOMING_TRANSACTION_TYPES: continue @@ -1654,7 +1652,7 @@ async def send_transaction_multi(self, request: dict[str, Any]) -> EndpointResul # Transaction may not have been included in the mempool yet. Use get_transaction to check. return { "transaction": transaction, - "transaction_id": TransactionRecord.from_json_dict_convenience(transaction).name, + "transaction_id": TransactionRecord.from_json_dict(transaction).name, "transactions": transactions, "unsigned_transactions": response["unsigned_transactions"], } diff --git a/chia/wallet/wallet_rpc_client.py b/chia/wallet/wallet_rpc_client.py index e4157c992c9b..8d50e0b97aba 100644 --- a/chia/wallet/wallet_rpc_client.py +++ b/chia/wallet/wallet_rpc_client.py @@ -171,7 +171,7 @@ def parse_result_transactions(result: dict[str, Any]) -> dict[str, Any]: result["transaction"] = TransactionRecord.from_json_dict(result["transaction"]) - result["transactions"] = [TransactionRecord.from_json_dict_convenience(tx) for tx in result["transactions"]] + result["transactions"] = [TransactionRecord.from_json_dict(tx) for tx in result["transactions"]] if result["fee_transaction"]: result["fee_transaction"] = TransactionRecord.from_json_dict(result["fee_transaction"]) return result @@ -271,7 +271,7 @@ async def get_wallet_balances(self, wallet_ids: Optional[list[int]] = None) -> d async def get_transaction(self, transaction_id: bytes32) -> TransactionRecord: request = {"transaction_id": transaction_id.hex()} response = await self.fetch("get_transaction", request) - return TransactionRecord.from_json_dict_convenience(response["transaction"]) + return TransactionRecord.from_json_dict(response["transaction"]) async def get_transactions( self, @@ -304,7 +304,7 @@ async def get_transactions( request["confirmed"] = confirmed res = await self.fetch("get_transactions", request) - return [TransactionRecord.from_json_dict_convenience(tx) for tx in res["transactions"]] + return [TransactionRecord.from_json_dict(tx) for tx in res["transactions"]] async def get_transaction_count( self, wallet_id: int, confirmed: Optional[bool] = None, type_filter: Optional[TransactionTypeFilter] = None @@ -1184,7 +1184,7 @@ async def send_notification( **timelock_info.to_json_dict(), }, ) - return TransactionRecord.from_json_dict_convenience(response["tx"]) + return TransactionRecord.from_json_dict(response["tx"]) async def sign_message_by_address(self, address: str, message: str) -> tuple[str, str, str]: response = await self.fetch("sign_message_by_address", {"address": address, "message": message}) @@ -1279,7 +1279,7 @@ async def crcat_approve_pending( **timelock_info.to_json_dict(), }, ) - return [TransactionRecord.from_json_dict_convenience(tx) for tx in response["transactions"]] + return [TransactionRecord.from_json_dict(tx) for tx in response["transactions"]] async def gather_signing_info( self, diff --git a/chia/wallet/wallet_state_manager.py b/chia/wallet/wallet_state_manager.py index 2339918d121d..ee4e80a14971 100644 --- a/chia/wallet/wallet_state_manager.py +++ b/chia/wallet/wallet_state_manager.py @@ -232,7 +232,7 @@ async def create( self.initial_num_public_keys = min_num_public_keys self.coin_store = await WalletCoinStore.create(self.db_wrapper) - self.tx_store = await WalletTransactionStore.create(self.db_wrapper) + self.tx_store = await WalletTransactionStore.create(self.db_wrapper, self.config) self.puzzle_store = await WalletPuzzleStore.create(self.db_wrapper) self.user_store = await WalletUserStore.create(self.db_wrapper) self.nft_store = await WalletNftStore.create(self.db_wrapper) @@ -1053,6 +1053,7 @@ async def spend_clawback_coins( confirmed_at_height=uint32(0), created_at_time=now, to_puzzle_hash=derivation_record.puzzle_hash, + to_address=self.encode_puzzle_hash(derivation_record.puzzle_hash), amount=amount, fee_amount=uint64(fee), confirmed=False, @@ -1549,14 +1550,16 @@ async def handle_clawback( clawback_coin_spend: CoinSpend = await fetch_coin_spend_for_coin_state(coin_state, peer) clawback_spend_bundle = WalletSpendBundle([clawback_coin_spend], G2Element()) if await self.puzzle_store.puzzle_hash_exists(clawback_spend_bundle.additions()[0].puzzle_hash): + to_ph = ( + metadata.sender_puzzle_hash + if clawback_spend_bundle.additions()[0].puzzle_hash == metadata.sender_puzzle_hash + else metadata.recipient_puzzle_hash + ) tx_record = TransactionRecord( confirmed_at_height=uint32(coin_state.spent_height), created_at_time=created_timestamp, - to_puzzle_hash=( - metadata.sender_puzzle_hash - if clawback_spend_bundle.additions()[0].puzzle_hash == metadata.sender_puzzle_hash - else metadata.recipient_puzzle_hash - ), + to_puzzle_hash=to_ph, + to_address=self.encode_puzzle_hash(to_ph), amount=uint64(coin_state.coin.amount), fee_amount=uint64(0), confirmed=True, @@ -1594,6 +1597,7 @@ async def handle_clawback( confirmed_at_height=uint32(coin_state.created_height), created_at_time=uint64(created_timestamp), to_puzzle_hash=metadata.recipient_puzzle_hash, + to_address=self.encode_puzzle_hash(metadata.recipient_puzzle_hash), amount=uint64(coin_state.coin.amount), fee_amount=uint64(0), confirmed=spent_height != 0, @@ -1790,14 +1794,14 @@ async def _add_coin_states( created_timestamp = await self.wallet_node.get_timestamp_for_height( uint32(coin_state.created_height) ) + to_ph = await self.convert_puzzle_hash( + wallet_identifier.id, coin_state.coin.puzzle_hash + ) tx_record = TransactionRecord( confirmed_at_height=uint32(coin_state.created_height), created_at_time=uint64(created_timestamp), - to_puzzle_hash=( - await self.convert_puzzle_hash( - wallet_identifier.id, coin_state.coin.puzzle_hash - ) - ), + to_puzzle_hash=to_ph, + to_address=self.encode_puzzle_hash(to_ph), amount=uint64(coin_state.coin.amount), fee_amount=uint64(0), confirmed=True, @@ -1872,12 +1876,12 @@ async def _add_coin_states( for added_coin in additions: tx_name += bytes(added_coin.name()) tx_name = std_hash(tx_name) + to_ph = await self.convert_puzzle_hash(wallet_identifier.id, to_puzzle_hash) tx_record = TransactionRecord( confirmed_at_height=uint32(coin_state.spent_height), created_at_time=uint64(spent_timestamp), - to_puzzle_hash=( - await self.convert_puzzle_hash(wallet_identifier.id, to_puzzle_hash) - ), + to_puzzle_hash=to_ph, + to_address=self.encode_puzzle_hash(to_ph), amount=uint64(int(amount)), fee_amount=uint64(fee), confirmed=True, @@ -2199,10 +2203,12 @@ async def coin_added( clawback = parent_coin_record is not None and parent_coin_record.coin_type == CoinType.CLAWBACK if coinbase or clawback or (not coin_confirmed_transaction and not change): + to_ph = await self.convert_puzzle_hash(wallet_id, coin.puzzle_hash) tx_record = TransactionRecord( confirmed_at_height=uint32(height), created_at_time=await self.wallet_node.get_timestamp_for_height(height), - to_puzzle_hash=await self.convert_puzzle_hash(wallet_id, coin.puzzle_hash), + to_puzzle_hash=to_ph, + to_address=self.encode_puzzle_hash(to_ph), amount=uint64(coin.amount), fee_amount=uint64(0), confirmed=True, @@ -2769,3 +2775,8 @@ async def new_action_scope( async def delete_wallet(self, wallet_id: uint32) -> None: await self.user_store.delete_wallet(wallet_id) await self.puzzle_store.delete_wallet(wallet_id) + + def encode_puzzle_hash(self, puzzle_hash: bytes32) -> str: + return encode_puzzle_hash( + puzzle_hash, self.config["network_overrides"]["config"][self.config["selected_network"]]["address_prefix"] + ) diff --git a/chia/wallet/wallet_transaction_store.py b/chia/wallet/wallet_transaction_store.py index 040c42340c6a..7956240d4690 100644 --- a/chia/wallet/wallet_transaction_store.py +++ b/chia/wallet/wallet_transaction_store.py @@ -8,8 +8,10 @@ import aiosqlite from chia_rs.sized_bytes import bytes32 from chia_rs.sized_ints import uint8, uint32 +from typing_extensions import Any from chia.types.mempool_inclusion_status import MempoolInclusionStatus +from chia.util.bech32m import encode_puzzle_hash from chia.util.db_wrapper import DBWrapper2 from chia.util.errors import Err from chia.wallet.conditions import ConditionValidTimes @@ -44,11 +46,13 @@ class WalletTransactionStore: tx_submitted: dict[bytes32, tuple[int, int]] # tx_id: [time submitted: count] unconfirmed_txs: list[LightTransactionRecord] # tx_id: [time submitted: count] last_wallet_tx_resend_time: int # Epoch time in seconds + config: dict[str, Any] @classmethod - async def create(cls, db_wrapper: DBWrapper2): + async def create(cls, db_wrapper: DBWrapper2, config: dict[str, Any]): self = cls() + self.config = config self.db_wrapper = db_wrapper async with self.db_wrapper.writer_maybe_transaction() as conn: await conn.execute( @@ -485,6 +489,10 @@ async def _get_new_tx_records_from_old(self, old_records: list[TransactionRecord valid_times=( tx_id_to_valid_times[record.name] if record.name in tx_id_to_valid_times else empty_valid_times ), + to_address=encode_puzzle_hash( + record.to_puzzle_hash, + self.config["network_overrides"]["config"][self.config["selected_network"]]["address_prefix"], + ), ) for record in old_records ] From e1c803c0993ba9bc76b55971da09abf1e128b471 Mon Sep 17 00:00:00 2001 From: Matt Date: Thu, 24 Jul 2025 12:51:36 +0200 Subject: [PATCH 5/7] Fix test --- chia/_tests/wallet/test_transaction_store.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/chia/_tests/wallet/test_transaction_store.py b/chia/_tests/wallet/test_transaction_store.py index 780eeed10fbe..fc2078481435 100644 --- a/chia/_tests/wallet/test_transaction_store.py +++ b/chia/_tests/wallet/test_transaction_store.py @@ -48,7 +48,7 @@ bytes32(bytes32.random(module_seeded_random)), # name {}, # memos ConditionValidTimes(), - encode_puzzle_hash(bytes32(bytes32.zeros), "xch"), + encode_puzzle_hash(bytes32(bytes32.zeros), "txch"), ) MINIMUM_CONFIG = { @@ -726,19 +726,21 @@ async def test_get_transactions_between_to_puzzle_hash(seeded_random: random.Ran store = await WalletTransactionStore.create(db_wrapper, MINIMUM_CONFIG) ph1 = bytes32.random(seeded_random) + ad1 = encode_puzzle_hash(ph1, "txch") ph2 = bytes32.random(seeded_random) + ad2 = encode_puzzle_hash(ph2, "txch") tr2 = dataclasses.replace( - tr1, name=bytes32.random(seeded_random), confirmed_at_height=uint32(1), to_puzzle_hash=ph1 + tr1, name=bytes32.random(seeded_random), confirmed_at_height=uint32(1), to_puzzle_hash=ph1, to_address=ad1 ) tr3 = dataclasses.replace( - tr1, name=bytes32.random(seeded_random), confirmed_at_height=uint32(2), to_puzzle_hash=ph1 + tr1, name=bytes32.random(seeded_random), confirmed_at_height=uint32(2), to_puzzle_hash=ph1, to_address=ad1 ) tr4 = dataclasses.replace( - tr1, name=bytes32.random(seeded_random), confirmed_at_height=uint32(3), to_puzzle_hash=ph2 + tr1, name=bytes32.random(seeded_random), confirmed_at_height=uint32(3), to_puzzle_hash=ph2, to_address=ad2 ) tr5 = dataclasses.replace( - tr1, name=bytes32.random(seeded_random), confirmed_at_height=uint32(4), to_puzzle_hash=ph2 + tr1, name=bytes32.random(seeded_random), confirmed_at_height=uint32(4), to_puzzle_hash=ph2, to_address=ad2 ) await store.add_transaction_record(tr1) From d7e7b63e6b0d8c711d7cc3be511c40f2a6bdcb2a Mon Sep 17 00:00:00 2001 From: Matt Date: Mon, 4 Aug 2025 11:46:55 -0700 Subject: [PATCH 6/7] Use AddressType library in WSM method --- chia/wallet/wallet_state_manager.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/chia/wallet/wallet_state_manager.py b/chia/wallet/wallet_state_manager.py index d9c5f7995c48..a71f6e0473f7 100644 --- a/chia/wallet/wallet_state_manager.py +++ b/chia/wallet/wallet_state_manager.py @@ -2777,6 +2777,4 @@ async def delete_wallet(self, wallet_id: uint32) -> None: await self.puzzle_store.delete_wallet(wallet_id) def encode_puzzle_hash(self, puzzle_hash: bytes32) -> str: - return encode_puzzle_hash( - puzzle_hash, self.config["network_overrides"]["config"][self.config["selected_network"]]["address_prefix"] - ) + return encode_puzzle_hash(puzzle_hash, AddressType.XCH.hrp(self.config)) From e32b2e94c473e4b10b05ba5abffe9e7911fc691d Mon Sep 17 00:00:00 2001 From: Matt Date: Mon, 4 Aug 2025 12:37:30 -0700 Subject: [PATCH 7/7] Use AddressType library in transaction_store method --- chia/wallet/wallet_transaction_store.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chia/wallet/wallet_transaction_store.py b/chia/wallet/wallet_transaction_store.py index 7956240d4690..9e1b764306e2 100644 --- a/chia/wallet/wallet_transaction_store.py +++ b/chia/wallet/wallet_transaction_store.py @@ -22,6 +22,7 @@ minimum_send_attempts, ) from chia.wallet.transaction_sorting import SortKey +from chia.wallet.util.address_type import AddressType from chia.wallet.util.query_filter import FilterMode, TransactionTypeFilter from chia.wallet.util.transaction_type import TransactionType @@ -491,7 +492,7 @@ async def _get_new_tx_records_from_old(self, old_records: list[TransactionRecord ), to_address=encode_puzzle_hash( record.to_puzzle_hash, - self.config["network_overrides"]["config"][self.config["selected_network"]]["address_prefix"], + AddressType.XCH.hrp(self.config), ), ) for record in old_records