diff --git a/chia/_tests/wallet/cat_wallet/test_cat_wallet.py b/chia/_tests/wallet/cat_wallet/test_cat_wallet.py index bde0cb52dc..058c35e205 100644 --- a/chia/_tests/wallet/cat_wallet/test_cat_wallet.py +++ b/chia/_tests/wallet/cat_wallet/test_cat_wallet.py @@ -1695,7 +1695,7 @@ async def test_cat_melt_balance(wallet_environments: WalletTestFramework) -> Non solution=UnknownSolution(program=Program.NIL), ), ), - ), + ).program, extra_delta=-1, ) ], diff --git a/chia/_tests/wallet/puzzles/__init__.py b/chia/_tests/wallet/puzzles/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/chia/_tests/wallet/puzzles/test_standard_puzzle_drivers.py b/chia/_tests/wallet/puzzles/test_standard_puzzle_drivers.py new file mode 100644 index 0000000000..fe36f4ea55 --- /dev/null +++ b/chia/_tests/wallet/puzzles/test_standard_puzzle_drivers.py @@ -0,0 +1,134 @@ +from __future__ import annotations + +from unittest import mock + +import pytest +from chia_rs import AugSchemeMPL +from chia_rs.sized_bytes import bytes32 +from chia_rs.sized_ints import uint64 + +from chia.types.blockchain_format.program import Program +from chia.wallet.conditions import CreateCoin, Remark +from chia.wallet.puzzles.p2_delegated_puzzle_or_hidden_puzzle import ( + DEFAULT_HIDDEN_PUZZLE, + DEFAULT_HIDDEN_PUZZLE_HASH, + MOD, + calculate_synthetic_public_key, +) +from chia.wallet.puzzles.puzzle_drivers import ( + ACSPuzzle, + NilSolution, + P2Conditions, + UnknownPuzzle, + UnknownSolution, +) +from chia.wallet.puzzles.standard_puzzle_drivers import DefaultHiddenPuzzle, StandardPuzzle, StandardPuzzleSolution + + +def test_standard_puzzle_drivers() -> None: + original_public_key = AugSchemeMPL.key_gen(bytes([1] * 32)).get_g1() + synthetic_public_key = calculate_synthetic_public_key(original_public_key, DEFAULT_HIDDEN_PUZZLE_HASH) + + with pytest.raises(ValueError, match="Must specify either the synthetic or original pubkey"): + StandardPuzzle() + + from_original: StandardPuzzle[DefaultHiddenPuzzle] = StandardPuzzle( + pre_known_original_public_key=original_public_key + ) + assert from_original.synthetic_public_key == synthetic_public_key + from_synthetic: StandardPuzzle[DefaultHiddenPuzzle] = StandardPuzzle( + pre_known_synthetic_public_key=synthetic_public_key + ) + assert from_synthetic.tree_hash == from_original.tree_hash + + custom_hidden = ACSPuzzle() + custom_synthetic = calculate_synthetic_public_key(original_public_key, custom_hidden.tree_hash) + from_custom_hidden = StandardPuzzle( + pre_known_original_public_key=original_public_key, + hidden_puzzle=custom_hidden, + ) + assert from_custom_hidden.synthetic_public_key == custom_synthetic + assert from_custom_hidden.hidden_puzzle.tree_hash == custom_hidden.tree_hash + + assert StandardPuzzle.match(unknown_puzzle=UnknownPuzzle(known_program=ACSPuzzle().program)) is None + assert ( + StandardPuzzle.match(unknown_puzzle=UnknownPuzzle(known_program=MOD.curry(synthetic_public_key, Program.to(2)))) + is None + ) + + matched_without_solution = StandardPuzzle.match(unknown_puzzle=UnknownPuzzle(known_program=from_synthetic.program)) + assert matched_without_solution is not None + assert matched_without_solution.pre_known_synthetic_public_key == synthetic_public_key + assert matched_without_solution.pre_known_original_public_key is None + assert matched_without_solution.hidden_puzzle.program == DEFAULT_HIDDEN_PUZZLE + + with mock.patch.object(UnknownPuzzle, "curried_args", new_callable=mock.PropertyMock, return_value=None): + assert StandardPuzzle.match(unknown_puzzle=UnknownPuzzle(known_program=from_synthetic.program)) is None + + delegated_reveal = P2Conditions(conditions=[Remark(Program.to("delegated"))]) + delegated_solution = NilSolution() + solution_without_original = StandardPuzzleSolution( + delegated_puzzle=delegated_reveal, + delegated_solution=delegated_solution, + ) + matched_delegated = StandardPuzzle.match( + unknown_puzzle=UnknownPuzzle(known_program=from_synthetic.program), + ) + assert matched_delegated is not None + assert matched_delegated.pre_known_synthetic_public_key == synthetic_public_key + assert matched_delegated.pre_known_original_public_key is None + assert matched_delegated.hidden_puzzle.program == DEFAULT_HIDDEN_PUZZLE + assert ( + DefaultHiddenPuzzle.match(unknown_puzzle=UnknownPuzzle(known_program=DEFAULT_HIDDEN_PUZZLE)) + == DefaultHiddenPuzzle() + ) + assert ( + DefaultHiddenPuzzle.match(unknown_puzzle=UnknownPuzzle(known_tree_hash=DEFAULT_HIDDEN_PUZZLE_HASH)) + == DefaultHiddenPuzzle() + ) + assert DefaultHiddenPuzzle.match(unknown_puzzle=UnknownPuzzle(known_program=Program.to("foo"))) is None + + hidden_reveal = ACSPuzzle() + solution_with_original = StandardPuzzleSolution( + original_public_key=original_public_key, + delegated_puzzle=hidden_reveal, + delegated_solution=UnknownSolution(program=Program.to([42])), + ) + + assert StandardPuzzleSolution.match(unknown_solution=UnknownSolution(Program.to(1))) is None + assert StandardPuzzleSolution.match(unknown_solution=UnknownSolution(Program.to([1, 2]))) is None + assert StandardPuzzleSolution.match(unknown_solution=UnknownSolution(Program.to([1, 2, 3, 4]))) is None + + matched_solution_nil_key = StandardPuzzleSolution.match( + unknown_solution=UnknownSolution(solution_without_original.program) + ) + assert matched_solution_nil_key is not None + assert matched_solution_nil_key.original_public_key is None + assert matched_solution_nil_key.delegated_puzzle.program == delegated_reveal.program + assert matched_solution_nil_key.delegated_solution.program == delegated_solution.program + + matched_solution_with_key = StandardPuzzleSolution.match( + unknown_solution=UnknownSolution(solution_with_original.program) + ) + assert matched_solution_with_key is not None + assert matched_solution_with_key.original_public_key == original_public_key + assert matched_solution_with_key.delegated_puzzle.program == hidden_reveal.program + assert matched_solution_with_key.delegated_solution.program == Program.to([42]) + + create_coin = CreateCoin(bytes32(bytes([3] * 32)), uint64(500)) + remark = Remark(Program.to("hi")) + condition_solution = StandardPuzzleSolution.for_conditions([create_coin, remark]) + expected_delegated = P2Conditions(conditions=[create_coin, remark]) + solution_match = StandardPuzzleSolution.match(unknown_solution=UnknownSolution(condition_solution.program)) + assert solution_match is not None + assert solution_match.original_public_key is None + assert solution_match.delegated_puzzle.program == expected_delegated.program + + hidden_solution: StandardPuzzleSolution[DefaultHiddenPuzzle, UnknownSolution] = StandardPuzzle( + pre_known_original_public_key=original_public_key + ).hidden_puzzle_solution(UnknownSolution(program=Program.to([99]))) + solution_match = StandardPuzzleSolution.match(unknown_solution=UnknownSolution(hidden_solution.program)) + assert solution_match is not None + assert solution_match.original_public_key is not None + assert solution_match.delegated_puzzle.program == DefaultHiddenPuzzle().program + assert solution_match.delegated_solution.program == Program.to([99]) diff --git a/chia/data_layer/data_layer_wallet.py b/chia/data_layer/data_layer_wallet.py index 8334f91dd5..8c3be602ae 100644 --- a/chia/data_layer/data_layer_wallet.py +++ b/chia/data_layer/data_layer_wallet.py @@ -541,7 +541,7 @@ class DataLayerWallet: inner_sol: Program = self.standard_wallet.make_solution( primaries=primaries, conditions=(*extra_conditions, CreateCoinAnnouncement(b"$")) if fee > 0 else extra_conditions, - ) + ).program db_layer_sol = Program.to([inner_sol]) full_sol = Program.to( [ @@ -732,7 +732,7 @@ class DataLayerWallet: inner_sol: Program = self.standard_wallet.make_solution( primaries=[CreateCoin(new_puzhash, uint64(mirror_coin.amount - fee))] if excess_fee < 0 else [], conditions=(*extra_conditions, CreateCoinAnnouncement(b"$")) if excess_fee > 0 else extra_conditions, - ) + ).program mirror_spend = make_spend( mirror_coin, create_mirror_puzzle(), diff --git a/chia/wallet/cat_wallet/cat_wallet.py b/chia/wallet/cat_wallet/cat_wallet.py index f16d5f7367..e27f66f16b 100644 --- a/chia/wallet/cat_wallet/cat_wallet.py +++ b/chia/wallet/cat_wallet/cat_wallet.py @@ -778,7 +778,7 @@ class CATWallet: primaries: list[CreateCoin], conditions: tuple[Condition, ...] = tuple(), ) -> Program: - return self.standard_wallet.make_solution(primaries=primaries, conditions=conditions) + return self.standard_wallet.make_solution(primaries=primaries, conditions=conditions).program async def generate_unsigned_spendbundle( self, diff --git a/chia/wallet/clawback_manager.py b/chia/wallet/clawback_manager.py index 460047678b..ce9e4b8067 100644 --- a/chia/wallet/clawback_manager.py +++ b/chia/wallet/clawback_manager.py @@ -255,7 +255,7 @@ class ClawbackManager: if len(coin_spends) > 0 or fee == 0 else (*extra_conditions, CreateCoinAnnouncement(message)) ), - ) + ).program coin_spend: CoinSpend = generate_clawback_spend_bundle(coin, metadata, inner_puzzle, inner_solution) coin_spends.append(coin_spend) # Update incoming tx to prevent double spend and mark it is pending diff --git a/chia/wallet/did_wallet/did_wallet.py b/chia/wallet/did_wallet/did_wallet.py index 8bff9b0401..25f9f680e1 100644 --- a/chia/wallet/did_wallet/did_wallet.py +++ b/chia/wallet/did_wallet/did_wallet.py @@ -736,7 +736,7 @@ class DIDWallet: ) ], conditions=(*extra_conditions, CreateCoinAnnouncement(coin.name())), - ) + ).program innersol: Program = Program.to([1, p2_solution]) # full solution is (corehash parent_info my_amount innerpuz_reveal solution) innerpuz: Program = self.did_info.current_inner @@ -834,7 +834,7 @@ class DIDWallet: p2_solution = self.standard_wallet.make_solution( primaries=[CreateCoin(new_did_puzhash, uint64(coin.amount), [new_puzhash])], conditions=(*extra_conditions, CreateCoinAnnouncement(coin.name())), - ) + ).program innersol = Program.to([2, p2_solution, [], [], [], self.did_info.backup_ids]) # full solution is (corehash parent_info my_amount innerpuz_reveal solution) @@ -912,7 +912,7 @@ class DIDWallet: p2_solution = self.standard_wallet.make_solution( primaries=[CreateCoin(puzzle_hash=new_innerpuzzle_hash, amount=uint64(coin.amount), memos=[p2_ph])], conditions=extra_conditions, - ) + ).program # innerpuz solution is (mode p2_solution) innersol: Program = Program.to([1, p2_solution]) @@ -1141,7 +1141,7 @@ class DIDWallet: p2_solution = self.standard_wallet.make_solution( primaries=[CreateCoin(innerpuz.get_tree_hash(), uint64(coin.amount), [p2_puzzle.get_tree_hash()])], conditions=extra_conditions, - ) + ).program innersol = Program.to([1, p2_solution]) # full solution is (lineage_proof my_amount inner_solution) fullsol = Program.to( diff --git a/chia/wallet/nft_wallet/nft_wallet.py b/chia/wallet/nft_wallet/nft_wallet.py index aeb5bd96c7..cd94b5a845 100644 --- a/chia/wallet/nft_wallet/nft_wallet.py +++ b/chia/wallet/nft_wallet/nft_wallet.py @@ -823,7 +823,7 @@ class NFTWallet: innersol: Program = self.standard_wallet.make_solution( primaries=payments, conditions=(*extra_conditions, CreateCoinAnnouncement(coin_name)) if fee > 0 else extra_conditions, - ) + ).program if unft.supports_did: innersol = Program.to([innersol]) diff --git a/chia/wallet/plotnft_wallet/plotnft_wallet.py b/chia/wallet/plotnft_wallet/plotnft_wallet.py index 8b15d491c1..0a1ec2a848 100644 --- a/chia/wallet/plotnft_wallet/plotnft_wallet.py +++ b/chia/wallet/plotnft_wallet/plotnft_wallet.py @@ -170,20 +170,18 @@ class PlotNFT2Wallet: rewards_to_claim=rewards_to_claim, reward_delegated_puzzles_and_solutions=[ DelegatedPuzzleAndSolution( - puzzle=UnknownPuzzle( - known_program=self.xch_wallet.make_solution( - primaries=[ - CreateCoin( - puzzle_hash=self.rewards_claim_puzhash, - amount=uint64(total_reward_amount - fee), - ), - ], - fee=fee, - conditions=(*extra_conditions, CreateCoinAnnouncement(b"")) - if len(rewards_to_claim) > 1 - else extra_conditions, - ).at("rf") # strips away to just the delegated puzzle (bit of a hack) - ), + puzzle=self.xch_wallet.make_solution( + primaries=[ + CreateCoin( + puzzle_hash=self.rewards_claim_puzhash, + amount=uint64(total_reward_amount - fee), + ), + ], + fee=fee, + conditions=(*extra_conditions, CreateCoinAnnouncement(b"")) + if len(rewards_to_claim) > 1 + else extra_conditions, + ).delegated_puzzle, solution=NilSolution(), ) if i == 0 @@ -312,12 +310,10 @@ class PlotNFT2Wallet: fee_hook = CreateCoinAnnouncement(msg=b"", coin_id=plotnft.coin.name()) exit_create_coin = plotnft.exit_to_waiting_room_condition() exit_to_waiting_room_dpuz_and_sol = DelegatedPuzzleAndSolution( - puzzle=UnknownPuzzle( - known_program=self.xch_wallet.make_solution( - primaries=[exit_create_coin], - conditions=(*extra_conditions, fee_hook), - ).at("rf") # strips away to just the delegated puzzle (bit of a hack) - ), + puzzle=self.xch_wallet.make_solution( + primaries=[exit_create_coin], + conditions=(*extra_conditions, fee_hook), + ).delegated_puzzle, solution=NilSolution(), ) coin_spends = plotnft.exit_to_waiting_room(exit_to_waiting_room_dpuz_and_sol) @@ -370,12 +366,10 @@ class PlotNFT2Wallet: fee_hook = CreateCoinAnnouncement(msg=b"", coin_id=plotnft.coin.name()) heightlock, exit_create_coin = plotnft.exit_from_waiting_room_conditions() exit_to_waiting_room_dpuz_and_sol = DelegatedPuzzleAndSolution( - puzzle=UnknownPuzzle( - known_program=self.xch_wallet.make_solution( - primaries=[exit_create_coin], - conditions=(fee_hook, heightlock, *extra_conditions), - ).at("rf") # strips away to just the delegated puzzle (bit of a hack) - ), + puzzle=self.xch_wallet.make_solution( + primaries=[exit_create_coin], + conditions=(fee_hook, heightlock, *extra_conditions), + ).delegated_puzzle, solution=NilSolution(), ) coin_spends = plotnft.exit_waiting_room(exit_to_waiting_room_dpuz_and_sol) diff --git a/chia/wallet/puzzles/puzzle_driver_registry.py b/chia/wallet/puzzles/puzzle_driver_registry.py index 581c3f4b08..fe1f670c12 100644 --- a/chia/wallet/puzzles/puzzle_driver_registry.py +++ b/chia/wallet/puzzles/puzzle_driver_registry.py @@ -2,7 +2,7 @@ from __future__ import annotations from dataclasses import dataclass -from chia.wallet.puzzles import puzzle_drivers +from chia.wallet.puzzles import puzzle_drivers, standard_puzzle_drivers from chia.wallet.puzzles.custody import custody_architecture, member_puzzles, restriction_utilities, restrictions """ @@ -91,4 +91,14 @@ DRIVER_REGISTRY = [ puzzle=restriction_utilities.ValidatorStackRestriction, solution=restriction_utilities.ValidatorStackRestrictionSolution, ), + PuzzleDriverSet( + name="default_hidden_puzzle", + puzzle=standard_puzzle_drivers.DefaultHiddenPuzzle, + solution=puzzle_drivers.NilSolution, + ), + PuzzleDriverSet( + name="standard_puzzle", + puzzle=standard_puzzle_drivers.StandardPuzzle, + solution=standard_puzzle_drivers.StandardPuzzleSolution, + ), ] diff --git a/chia/wallet/puzzles/standard_puzzle_drivers.py b/chia/wallet/puzzles/standard_puzzle_drivers.py new file mode 100644 index 0000000000..118b52ee20 --- /dev/null +++ b/chia/wallet/puzzles/standard_puzzle_drivers.py @@ -0,0 +1,149 @@ +from __future__ import annotations + +from dataclasses import dataclass, field +from functools import cached_property +from typing import Generic, TypeVar + +from chia_rs import G1Element +from chia_rs.sized_bytes import bytes32 + +from chia.types.blockchain_format.program import Program +from chia.wallet.conditions import Condition +from chia.wallet.puzzles.p2_delegated_puzzle_or_hidden_puzzle import ( + DEFAULT_HIDDEN_PUZZLE, + DEFAULT_HIDDEN_PUZZLE_HASH, + MOD, + QUOTED_MOD_HASH, + calculate_synthetic_public_key, +) +from chia.wallet.puzzles.puzzle_drivers import ( + NilSolution, + P2Conditions, + Puzzle, + PuzzleBase, + Solution, + UnknownPuzzle, + UnknownSolution, +) +from chia.wallet.util.curry_and_treehash import curry_and_treehash, shatree_atom + + +@dataclass(kw_only=True, frozen=True) +class DefaultHiddenPuzzle(PuzzleBase): + @property + def program(self) -> Program: + return DEFAULT_HIDDEN_PUZZLE + + @property + def tree_hash_optimized(self) -> bytes32: + return DEFAULT_HIDDEN_PUZZLE_HASH + + @classmethod + def match(cls, *, unknown_puzzle: UnknownPuzzle) -> DefaultHiddenPuzzle | None: + if ( + unknown_puzzle.known_tree_hash == DEFAULT_HIDDEN_PUZZLE_HASH + or unknown_puzzle.known_program == DEFAULT_HIDDEN_PUZZLE + ): + return cls() + return None + + +_T_Puzzle = TypeVar("_T_Puzzle", bound=Puzzle) +_T_HiddenPuzzleSolution = TypeVar("_T_HiddenPuzzleSolution", bound=Solution) + + +@dataclass(kw_only=True, frozen=True) +class StandardPuzzle(PuzzleBase, Generic[_T_Puzzle]): + pre_known_synthetic_public_key: G1Element | None = None + pre_known_original_public_key: G1Element | None = None + hidden_puzzle: _T_Puzzle = field(default_factory=DefaultHiddenPuzzle) # type: ignore[assignment] + + def __post_init__(self) -> None: + if self.pre_known_synthetic_public_key is None and self.pre_known_original_public_key is None: + raise ValueError("Must specify either the synthetic or original pubkey to construct a StandardPuzzle") + + @cached_property + def synthetic_public_key(self) -> G1Element: + if self.pre_known_synthetic_public_key is None: + assert self.pre_known_original_public_key is not None # guarded by __post_init__ + synth_pubkey = calculate_synthetic_public_key( + self.pre_known_original_public_key, self.hidden_puzzle.tree_hash + ) + else: + synth_pubkey = self.pre_known_synthetic_public_key + return synth_pubkey + + @property + def program(self) -> Program: + return MOD.curry(self.synthetic_public_key) + + @property + def tree_hash_optimized(self) -> bytes32: + public_key_hash = shatree_atom(bytes(self.synthetic_public_key)) + return curry_and_treehash(QUOTED_MOD_HASH, public_key_hash) + + @classmethod + def match(cls, *, unknown_puzzle: UnknownPuzzle) -> StandardPuzzle[DefaultHiddenPuzzle] | None: + if unknown_puzzle.mod == MOD: + if unknown_puzzle.curried_args is None: + return None + list_of_args = [arg for arg in unknown_puzzle.curried_args] + if len(list_of_args) != 1: + return None + try: + synth_pubkey = G1Element.from_bytes(list_of_args[0].as_atom()) + except Exception: + return None + return StandardPuzzle(pre_known_synthetic_public_key=synth_pubkey, hidden_puzzle=DefaultHiddenPuzzle()) + return None + + def hidden_puzzle_solution( + self, solution: _T_HiddenPuzzleSolution + ) -> StandardPuzzleSolution[_T_Puzzle, _T_HiddenPuzzleSolution]: + if self.pre_known_original_public_key is None: + raise ValueError( + "Must set `pre_known_original_public_key` on `StandardPuzzle` before you can exercise the hidden puzzle" + ) + return StandardPuzzleSolution( + original_public_key=self.pre_known_original_public_key, + delegated_puzzle=self.hidden_puzzle, + delegated_solution=solution, + ) + + +_T_Solution = TypeVar("_T_Solution", bound=Solution) + + +@dataclass(kw_only=True) +class StandardPuzzleSolution(Generic[_T_Puzzle, _T_Solution]): + original_public_key: G1Element | None = None + delegated_puzzle: _T_Puzzle + delegated_solution: _T_Solution + + @classmethod + def for_conditions(cls, conditions: list[Condition]) -> StandardPuzzleSolution[P2Conditions, NilSolution]: + return StandardPuzzleSolution( + delegated_puzzle=P2Conditions(conditions=conditions), + delegated_solution=NilSolution(), + ) + + @property + def program(self) -> Program: + return Program.to([self.original_public_key, self.delegated_puzzle.program, self.delegated_solution.program]) + + @classmethod + def match( + cls, *, unknown_solution: UnknownSolution + ) -> StandardPuzzleSolution[UnknownPuzzle, UnknownSolution] | None: + if unknown_solution.program.atom is not None: + return None + list_of_values = list(unknown_solution.program.as_iter()) + if len(list_of_values) != 3: + return None + return StandardPuzzleSolution( + original_public_key=G1Element.from_bytes(list_of_values[0].as_atom()) + if list_of_values[0] != Program.to(None) + else None, + delegated_puzzle=UnknownPuzzle(known_program=list_of_values[1]), + delegated_solution=UnknownSolution(program=list_of_values[2]), + ) diff --git a/chia/wallet/puzzles/tails.py b/chia/wallet/puzzles/tails.py index 1cc9571557..a156e7ebfb 100644 --- a/chia/wallet/puzzles/tails.py +++ b/chia/wallet/puzzles/tails.py @@ -133,7 +133,7 @@ class GenesisById(LimitationsProgram): solution=UnknownSolution(program=Program.NIL), ), ), - ) + ).program eve_spend = unsigned_spend_bundle_for_spendable_cats( CAT_MOD, [ diff --git a/chia/wallet/vc_wallet/cr_cat_wallet.py b/chia/wallet/vc_wallet/cr_cat_wallet.py index 4d369cf6d9..3261e23885 100644 --- a/chia/wallet/vc_wallet/cr_cat_wallet.py +++ b/chia/wallet/vc_wallet/cr_cat_wallet.py @@ -514,7 +514,7 @@ class CRCATWallet(CATWallet): innersol = self.standard_wallet.make_solution( primaries=primaries, conditions=(*extra_conditions, announcement), - ) + ).program elif regular_chia_to_claim > fee: xch_announcement = await self.create_tandem_xch_tx( fee, @@ -525,7 +525,7 @@ class CRCATWallet(CATWallet): innersol = self.standard_wallet.make_solution( primaries=primaries, conditions=(*extra_conditions, xch_announcement, announcement), - ) + ).program else: # TODO: what about when they are equal? raise Exception("Equality not handled") @@ -533,13 +533,13 @@ class CRCATWallet(CATWallet): innersol = self.standard_wallet.make_solution( primaries=primaries, conditions=(*extra_conditions, announcement), - ) + ).program else: assert announcement is not None innersol = self.standard_wallet.make_solution( primaries=[], conditions=(announcement.corresponding_assertion(),), - ) + ).program inner_derivation_record = ( await self.wallet_state_manager.puzzle_store.get_derivation_record_for_puzzle_hash( crcat.inner_puzzle_hash diff --git a/chia/wallet/vc_wallet/vc_wallet.py b/chia/wallet/vc_wallet/vc_wallet.py index 40686dda6c..dd2bc34191 100644 --- a/chia/wallet/vc_wallet/vc_wallet.py +++ b/chia/wallet/vc_wallet/vc_wallet.py @@ -338,7 +338,7 @@ class VCWallet: innersol: Program = self.standard_wallet.make_solution( primaries=primaries, conditions=extra_conditions, - ) + ).program did_announcement, coin_spend, _vc = vc_record.vc.do_spend(inner_puzzle, innersol, new_proof_hash) spend_bundle = WalletSpendBundle([coin_spend], G2Element()) if did_announcement is not None: diff --git a/chia/wallet/wallet.py b/chia/wallet/wallet.py index 44d38e6fe3..a6d63f3241 100644 --- a/chia/wallet/wallet.py +++ b/chia/wallet/wallet.py @@ -19,16 +19,13 @@ from chia.wallet.conditions import ( Condition, CreateCoin, CreateCoinAnnouncement, + ReserveFee, ) from chia.wallet.puzzles.p2_delegated_puzzle_or_hidden_puzzle import ( - DEFAULT_HIDDEN_PUZZLE_HASH, - calculate_synthetic_public_key, calculate_synthetic_secret_key, - puzzle_for_pk, - puzzle_hash_for_pk, - solution_for_conditions, ) -from chia.wallet.puzzles.puzzle_utils import make_reserve_fee_condition +from chia.wallet.puzzles.puzzle_drivers import NilSolution, P2Conditions +from chia.wallet.puzzles.standard_puzzle_drivers import DefaultHiddenPuzzle, StandardPuzzle, StandardPuzzleSolution from chia.wallet.transaction_record import TransactionRecord from chia.wallet.util.puzzle_decorator import PuzzleDecoratorManager from chia.wallet.util.transaction_type import CLAWBACK_INCOMING_TRANSACTION_TYPES @@ -94,10 +91,10 @@ class Wallet: return self.wallet_id def convert_secret_key_to_synthetic(self, secret_key: PrivateKey) -> PrivateKey: - return calculate_synthetic_secret_key(secret_key, DEFAULT_HIDDEN_PUZZLE_HASH) + return calculate_synthetic_secret_key(secret_key, DefaultHiddenPuzzle().tree_hash) def convert_public_key_to_synthetic(self, public_key: G1Element) -> G1Element: - return calculate_synthetic_public_key(public_key, DEFAULT_HIDDEN_PUZZLE_HASH) + return StandardPuzzle(pre_known_original_public_key=public_key).synthetic_public_key async def get_confirmed_balance(self, record_list: set[WalletCoinRecord] | None = None) -> uint128: return await self.wallet_state_manager.get_confirmed_balance_for_wallet(self.id(), record_list) @@ -153,33 +150,32 @@ class Wallet: return True def puzzle_for_pk(self, pubkey: G1Element) -> Program: - return puzzle_for_pk(pubkey) + return StandardPuzzle(pre_known_original_public_key=pubkey).program def puzzle_hash_for_pk(self, pubkey: G1Element) -> bytes32: - return puzzle_hash_for_pk(pubkey) + return StandardPuzzle(pre_known_original_public_key=pubkey).tree_hash async def convert_puzzle_hash(self, puzzle_hash: bytes32) -> bytes32: return puzzle_hash # Looks unimpressive, but it's more complicated in other wallets async def puzzle_for_puzzle_hash(self, puzzle_hash: bytes32) -> Program: public_key = await self.wallet_state_manager.get_public_key(puzzle_hash) - return puzzle_for_pk(G1Element.from_bytes(public_key)) + return self.puzzle_for_pk(G1Element.from_bytes(public_key)) def make_solution( self, primaries: list[CreateCoin], conditions: tuple[Condition, ...] = tuple(), fee: uint64 = uint64(0), - ) -> Program: - assert fee >= 0 - condition_list: list[Any] = [condition.to_program() for condition in conditions] - if len(primaries) > 0: - for primary in primaries: - condition_list.append(primary.to_program()) + ) -> StandardPuzzleSolution[P2Conditions, NilSolution]: + condition_list = list(conditions) + condition_list.extend(primaries) if fee: - condition_list.append(make_reserve_fee_condition(fee)) + condition_list.append(ReserveFee(fee)) - return solution_for_conditions(condition_list) + return StandardPuzzleSolution( + delegated_puzzle=P2Conditions(conditions=condition_list), delegated_solution=NilSolution() + ) async def select_coins( self, @@ -315,11 +311,11 @@ class Wallet: message_list.append(Coin(coin.name(), primary.puzzle_hash, primary.amount).name()) message: bytes32 = std_hash(b"".join(message_list)) puzzle: Program = await self.puzzle_for_puzzle_hash(coin.puzzle_hash) - solution: Program = self.make_solution( + solution = self.make_solution( primaries=primaries, fee=fee if reserve_fee is None else reserve_fee, conditions=(*extra_conditions, CreateCoinAnnouncement(message)), - ) + ).program solution = decorator_manager.solve(inner_puzzle, target_primaries, solution) primary_announcement = AssertCoinAnnouncement(asserted_id=coin.name(), asserted_msg=message) @@ -337,7 +333,7 @@ class Wallet: if coin.name() == origin_id: continue puzzle = await self.puzzle_for_puzzle_hash(coin.puzzle_hash) - solution = self.make_solution(primaries=[], conditions=(primary_announcement,)) + solution = self.make_solution(primaries=[], conditions=(primary_announcement,)).program solution = decorator_manager.solve(puzzle, [], solution) spends.append( make_spend(