[LABS-501] Make conforming StandardPuzzle drivers (#21499)

* Replace `UncurriedPuzzle` with a more useful `UnknownPuzzle`

* 3.10 Self

* test coverage

* pivot some naming conventions

* Add some common puzzle drivers

* use puzzle drivers where applicable

* remove executable bits

* fix tests

* remove executable bit

* fix test

* Add condition drivers for magic conditions

* Conform MIPS drivers to `Puzzle/Solution` protocols

* comments by @cursor

* Tweak MofN drivers to allow for initializing merkle tree with root only

* Comments by @cursor

* Comments by @cursor

* test coverage

* A bit of test coverage

* Make `StandardPuzzle` drivers

* remove redundant tests

* test coverage

* Comments by @richardkiss

* Comments by @cursor
This commit is contained in:
Matt Hauff
2026-10-23 09:02:17 -07:00
committed by GitHub
parent 090f7bc41d
commit 37ca84df4f
15 changed files with 348 additions and 65 deletions
@@ -1695,7 +1695,7 @@ async def test_cat_melt_balance(wallet_environments: WalletTestFramework) -> Non
solution=UnknownSolution(program=Program.NIL),
),
),
),
).program,
extra_delta=-1,
)
],
@@ -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])
+2 -2
View File
@@ -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(),
+1 -1
View File
@@ -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,
+1 -1
View File
@@ -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
+4 -4
View File
@@ -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(
+1 -1
View File
@@ -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])
+20 -26
View File
@@ -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)
+11 -1
View File
@@ -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,
),
]
@@ -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]),
)
+1 -1
View File
@@ -133,7 +133,7 @@ class GenesisById(LimitationsProgram):
solution=UnknownSolution(program=Program.NIL),
),
),
)
).program
eve_spend = unsigned_spend_bundle_for_spendable_cats(
CAT_MOD,
[
+4 -4
View File
@@ -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
+1 -1
View File
@@ -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:
+18 -22
View File
@@ -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(