mirror of
https://github.com/Chia-Network/chia-blockchain.git
synced 2026-11-06 20:58:11 -04:00
[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:
@@ -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])
|
||||
@@ -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(),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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]),
|
||||
)
|
||||
@@ -133,7 +133,7 @@ class GenesisById(LimitationsProgram):
|
||||
solution=UnknownSolution(program=Program.NIL),
|
||||
),
|
||||
),
|
||||
)
|
||||
).program
|
||||
eve_spend = unsigned_spend_bundle_for_spendable_cats(
|
||||
CAT_MOD,
|
||||
[
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user