Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

introduce an UncurriedPuzzle class #13211

Merged
merged 2 commits into from
Aug 30, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 10 additions & 9 deletions chia/wallet/cat_wallet/cat_outer_puzzle.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,38 +15,39 @@
from chia.wallet.lineage_proof import LineageProof
from chia.wallet.puzzle_drivers import PuzzleInfo, Solver
from chia.wallet.puzzles.cat_loader import CAT_MOD
from chia.wallet.uncurried_puzzle import UncurriedPuzzle, uncurry_puzzle


@dataclass(frozen=True)
class CATOuterPuzzle:
_match: Callable[[Program], Optional[PuzzleInfo]]
_match: Callable[[UncurriedPuzzle], Optional[PuzzleInfo]]
_construct: Callable[[PuzzleInfo, Program], Program]
_solve: Callable[[PuzzleInfo, Solver, Program, Program], Program]
_get_inner_puzzle: Callable[[PuzzleInfo, Program], Optional[Program]]
_get_inner_puzzle: Callable[[PuzzleInfo, UncurriedPuzzle], Optional[Program]]
_get_inner_solution: Callable[[PuzzleInfo, Program], Optional[Program]]

def match(self, puzzle: Program) -> Optional[PuzzleInfo]:
args = match_cat_puzzle(*puzzle.uncurry())
def match(self, puzzle: UncurriedPuzzle) -> Optional[PuzzleInfo]:
args = match_cat_puzzle(puzzle)
if args is None:
return None
_, tail_hash, inner_puzzle = args
constructor_dict = {
"type": "CAT",
"tail": "0x" + tail_hash.as_python().hex(),
}
next_constructor = self._match(inner_puzzle)
next_constructor = self._match(uncurry_puzzle(inner_puzzle))
if next_constructor is not None:
constructor_dict["also"] = next_constructor.info
return PuzzleInfo(constructor_dict)

def get_inner_puzzle(self, constructor: PuzzleInfo, puzzle_reveal: Program) -> Optional[Program]:
args = match_cat_puzzle(*puzzle_reveal.uncurry())
def get_inner_puzzle(self, constructor: PuzzleInfo, puzzle_reveal: UncurriedPuzzle) -> Optional[Program]:
args = match_cat_puzzle(puzzle_reveal)
if args is None:
raise ValueError("This driver is not for the specified puzzle reveal")
_, _, inner_puzzle = args
also = constructor.also()
if also is not None:
deep_inner_puzzle: Optional[Program] = self._get_inner_puzzle(also, inner_puzzle)
deep_inner_puzzle: Optional[Program] = self._get_inner_puzzle(also, uncurry_puzzle(inner_puzzle))
return deep_inner_puzzle
else:
return inner_puzzle
Expand Down Expand Up @@ -97,7 +98,7 @@ def solve(self, constructor: PuzzleInfo, solver: Solver, inner_puzzle: Program,
if also is not None:
puzzle = self._construct(also, puzzle)
solution = self._solve(also, solver, inner_puzzle, inner_solution)
args = match_cat_puzzle(*parent_spend.puzzle_reveal.to_program().uncurry())
args = match_cat_puzzle(uncurry_puzzle(parent_spend.puzzle_reveal.to_program()))
assert args is not None
_, _, parent_inner_puzzle = args
spendable_cats.append(
Expand Down
7 changes: 4 additions & 3 deletions chia/wallet/cat_wallet/cat_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from chia.util.condition_tools import conditions_dict_for_solution
from chia.wallet.lineage_proof import LineageProof
from chia.wallet.puzzles.cat_loader import CAT_MOD
from chia.wallet.uncurried_puzzle import UncurriedPuzzle

NULL_SIGNATURE = G2Element()

Expand All @@ -30,13 +31,13 @@ class SpendableCAT:
limitations_program_reveal: Program = Program.to([])


def match_cat_puzzle(mod: Program, curried_args: Program) -> Optional[Iterator[Program]]:
def match_cat_puzzle(puzzle: UncurriedPuzzle) -> Optional[Iterator[Program]]:
"""
Given the curried puzzle and args, test if it's a CAT and,
if it is, return the curried arguments
"""
if mod == CAT_MOD:
ret: Iterator[Program] = curried_args.as_iter()
if puzzle.mod == CAT_MOD:
ret: Iterator[Program] = puzzle.args.as_iter()
return ret
else:
return None
Expand Down
5 changes: 3 additions & 2 deletions chia/wallet/cat_wallet/cat_wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
from chia.wallet.wallet import Wallet
from chia.wallet.wallet_coin_record import WalletCoinRecord
from chia.wallet.wallet_info import WalletInfo
from chia.wallet.uncurried_puzzle import uncurry_puzzle

if TYPE_CHECKING:
from chia.wallet.wallet_state_manager import WalletStateManager
Expand Down Expand Up @@ -351,7 +352,7 @@ async def coin_added(self, coin: Coin, height: uint32, peer: WSChiaConnection) -
async def puzzle_solution_received(self, coin_spend: CoinSpend, parent_coin: Coin) -> None:
coin_name = coin_spend.coin.name()
puzzle: Program = Program.from_bytes(bytes(coin_spend.puzzle_reveal))
args = match_cat_puzzle(*puzzle.uncurry())
args = match_cat_puzzle(uncurry_puzzle(puzzle))
if args is not None:
mod_hash, genesis_coin_checker_hash, inner_puzzle = args
self.log.info(f"parent: {coin_name.hex()} inner_puzzle for parent is {inner_puzzle}")
Expand Down Expand Up @@ -469,7 +470,7 @@ async def select_coins(
async def sign(self, spend_bundle: SpendBundle) -> SpendBundle:
sigs: List[G2Element] = []
for spend in spend_bundle.coin_spends:
args = match_cat_puzzle(*spend.puzzle_reveal.to_program().uncurry())
args = match_cat_puzzle(uncurry_puzzle(spend.puzzle_reveal.to_program()))
if args is not None:
_, _, inner_puzzle = args
puzzle_hash = inner_puzzle.get_tree_hash()
Expand Down
5 changes: 3 additions & 2 deletions chia/wallet/driver_protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@
from chia.types.blockchain_format.program import Program
from chia.types.blockchain_format.sized_bytes import bytes32
from chia.wallet.puzzle_drivers import PuzzleInfo, Solver
from chia.wallet.uncurried_puzzle import UncurriedPuzzle


class DriverProtocol(Protocol):
def match(self, puzzle: Program) -> Optional[PuzzleInfo]:
def match(self, puzzle: UncurriedPuzzle) -> Optional[PuzzleInfo]:
...

def get_inner_puzzle(self, constructor: PuzzleInfo, puzzle_reveal: Program) -> Optional[Program]:
def get_inner_puzzle(self, constructor: PuzzleInfo, puzzle_reveal: UncurriedPuzzle) -> Optional[Program]:
...

def get_inner_solution(self, constructor: PuzzleInfo, solution: Program) -> Optional[Program]:
Expand Down
20 changes: 10 additions & 10 deletions chia/wallet/nft_wallet/metadata_outer_puzzle.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@
from chia.util.ints import uint64
from chia.wallet.puzzle_drivers import PuzzleInfo, Solver
from chia.wallet.puzzles.load_clvm import load_clvm
from chia.wallet.uncurried_puzzle import UncurriedPuzzle, uncurry_puzzle

NFT_STATE_LAYER_MOD = load_clvm("nft_state_layer.clvm")
NFT_STATE_LAYER_MOD_HASH = NFT_STATE_LAYER_MOD.get_tree_hash()


def match_metadata_layer_puzzle(puzzle: Program) -> Tuple[bool, List[Program]]:
mod, meta_args = puzzle.uncurry()
if mod == NFT_STATE_LAYER_MOD:
return True, list(meta_args.as_iter())
def match_metadata_layer_puzzle(puzzle: UncurriedPuzzle) -> Tuple[bool, List[Program]]:
if puzzle.mod == NFT_STATE_LAYER_MOD:
return True, list(puzzle.args.as_iter())
return False, []


Expand All @@ -31,13 +31,13 @@ def solution_for_metadata_layer(amount: uint64, inner_solution: Program) -> Prog

@dataclass(frozen=True)
class MetadataOuterPuzzle:
_match: Callable[[Program], Optional[PuzzleInfo]]
_match: Callable[[UncurriedPuzzle], Optional[PuzzleInfo]]
_construct: Callable[[PuzzleInfo, Program], Program]
_solve: Callable[[PuzzleInfo, Solver, Program, Program], Program]
_get_inner_puzzle: Callable[[PuzzleInfo, Program], Optional[Program]]
_get_inner_puzzle: Callable[[PuzzleInfo, UncurriedPuzzle], Optional[Program]]
_get_inner_solution: Callable[[PuzzleInfo, Program], Optional[Program]]

def match(self, puzzle: Program) -> Optional[PuzzleInfo]:
def match(self, puzzle: UncurriedPuzzle) -> Optional[PuzzleInfo]:
matched, curried_args = match_metadata_layer_puzzle(puzzle)
if matched:
_, metadata, updater_hash, inner_puzzle = curried_args
Expand All @@ -46,7 +46,7 @@ def match(self, puzzle: Program) -> Optional[PuzzleInfo]:
"metadata": disassemble(metadata), # type: ignore
"updater_hash": "0x" + updater_hash.as_python().hex(),
}
next_constructor = self._match(inner_puzzle)
next_constructor = self._match(uncurry_puzzle(inner_puzzle))
if next_constructor is not None:
constructor_dict["also"] = next_constructor.info
return PuzzleInfo(constructor_dict)
Expand All @@ -63,13 +63,13 @@ def construct(self, constructor: PuzzleInfo, inner_puzzle: Program) -> Program:
inner_puzzle = self._construct(also, inner_puzzle)
return puzzle_for_metadata_layer(constructor["metadata"], constructor["updater_hash"], inner_puzzle)

def get_inner_puzzle(self, constructor: PuzzleInfo, puzzle_reveal: Program) -> Optional[Program]:
def get_inner_puzzle(self, constructor: PuzzleInfo, puzzle_reveal: UncurriedPuzzle) -> Optional[Program]:
matched, curried_args = match_metadata_layer_puzzle(puzzle_reveal)
if matched:
_, _, _, inner_puzzle = curried_args
also = constructor.also()
if also is not None:
deep_inner_puzzle: Optional[Program] = self._get_inner_puzzle(also, inner_puzzle)
deep_inner_puzzle: Optional[Program] = self._get_inner_puzzle(also, uncurry_puzzle(inner_puzzle))
return deep_inner_puzzle
else:
return inner_puzzle
Expand Down
3 changes: 2 additions & 1 deletion chia/wallet/nft_wallet/nft_wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
)
from chia.wallet.trading.offer import OFFER_MOD, OFFER_MOD_HASH, NotarizedPayment, Offer
from chia.wallet.transaction_record import TransactionRecord
from chia.wallet.uncurried_puzzle import uncurry_puzzle
from chia.wallet.util.compute_memos import compute_memos
from chia.wallet.util.debug_spend_bundle import disassemble
from chia.wallet.util.transaction_type import TransactionType
Expand Down Expand Up @@ -523,7 +524,7 @@ def get_puzzle_info(self, nft_id: bytes32) -> PuzzleInfo:
nft_coin: Optional[NFTCoinInfo] = self.get_nft(nft_id)
if nft_coin is None:
raise ValueError("An asset ID was specified that this wallet doesn't track")
puzzle_info: Optional[PuzzleInfo] = match_puzzle(nft_coin.full_puzzle)
puzzle_info: Optional[PuzzleInfo] = match_puzzle(uncurry_puzzle(nft_coin.full_puzzle))
if puzzle_info is None:
raise ValueError("Internal Error: NFT wallet is tracking a non NFT coin")
else:
Expand Down
22 changes: 11 additions & 11 deletions chia/wallet/nft_wallet/ownership_outer_puzzle.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@
from chia.types.blockchain_format.sized_bytes import bytes32
from chia.wallet.puzzle_drivers import PuzzleInfo, Solver
from chia.wallet.puzzles.load_clvm import load_clvm
from chia.wallet.uncurried_puzzle import UncurriedPuzzle, uncurry_puzzle

OWNERSHIP_LAYER_MOD = load_clvm("nft_ownership_layer.clvm")


def match_ownership_layer_puzzle(puzzle: Program) -> Tuple[bool, List[Program]]:
mod, args = puzzle.uncurry()
if mod == OWNERSHIP_LAYER_MOD:
return True, list(args.as_iter())
def match_ownership_layer_puzzle(puzzle: UncurriedPuzzle) -> Tuple[bool, List[Program]]:
if puzzle.mod == OWNERSHIP_LAYER_MOD:
return True, list(puzzle.args.as_iter())
return False, []


Expand All @@ -30,26 +30,26 @@ def solution_for_ownership_layer(inner_solution: Program) -> Program:

@dataclass(frozen=True)
class OwnershipOuterPuzzle:
_match: Callable[[Program], Optional[PuzzleInfo]]
_match: Callable[[UncurriedPuzzle], Optional[PuzzleInfo]]
_construct: Callable[[PuzzleInfo, Program], Program]
_solve: Callable[[PuzzleInfo, Solver, Program, Program], Program]
_get_inner_puzzle: Callable[[PuzzleInfo, Program], Optional[Program]]
_get_inner_puzzle: Callable[[PuzzleInfo, UncurriedPuzzle], Optional[Program]]
_get_inner_solution: Callable[[PuzzleInfo, Program], Optional[Program]]

def match(self, puzzle: Program) -> Optional[PuzzleInfo]:
def match(self, puzzle: UncurriedPuzzle) -> Optional[PuzzleInfo]:
matched, curried_args = match_ownership_layer_puzzle(puzzle)
if matched:
_, current_owner, transfer_program, inner_puzzle = curried_args
owner_bytes: bytes = current_owner.as_python()
tp_match: Optional[PuzzleInfo] = self._match(transfer_program)
tp_match: Optional[PuzzleInfo] = self._match(uncurry_puzzle(transfer_program))
constructor_dict = {
"type": "ownership",
"owner": "()" if owner_bytes == b"" else "0x" + owner_bytes.hex(),
"transfer_program": (
disassemble(transfer_program) if tp_match is None else tp_match.info # type: ignore
),
}
next_constructor = self._match(inner_puzzle)
next_constructor = self._match(uncurry_puzzle(inner_puzzle))
if next_constructor is not None:
constructor_dict["also"] = next_constructor.info
return PuzzleInfo(constructor_dict)
Expand All @@ -70,13 +70,13 @@ def construct(self, constructor: PuzzleInfo, inner_puzzle: Program) -> Program:
transfer_program = self._construct(transfer_program_info, inner_puzzle)
return puzzle_for_ownership_layer(constructor["owner"], transfer_program, inner_puzzle)

def get_inner_puzzle(self, constructor: PuzzleInfo, puzzle_reveal: Program) -> Optional[Program]:
def get_inner_puzzle(self, constructor: PuzzleInfo, puzzle_reveal: UncurriedPuzzle) -> Optional[Program]:
matched, curried_args = match_ownership_layer_puzzle(puzzle_reveal)
if matched:
_, _, _, inner_puzzle = curried_args
also = constructor.also()
if also is not None:
deep_inner_puzzle: Optional[Program] = self._get_inner_puzzle(also, inner_puzzle)
deep_inner_puzzle: Optional[Program] = self._get_inner_puzzle(also, uncurry_puzzle(inner_puzzle))
return deep_inner_puzzle
else:
return inner_puzzle
Expand Down
15 changes: 8 additions & 7 deletions chia/wallet/nft_wallet/singleton_outer_puzzle.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,18 @@
puzzle_for_singleton,
solution_for_singleton,
)
from chia.wallet.uncurried_puzzle import UncurriedPuzzle, uncurry_puzzle


@dataclass(frozen=True)
class SingletonOuterPuzzle:
_match: Callable[[Program], Optional[PuzzleInfo]]
_match: Callable[[UncurriedPuzzle], Optional[PuzzleInfo]]
_construct: Callable[[PuzzleInfo, Program], Program]
_solve: Callable[[PuzzleInfo, Solver, Program, Program], Program]
_get_inner_puzzle: Callable[[PuzzleInfo, Program], Optional[Program]]
_get_inner_puzzle: Callable[[PuzzleInfo, UncurriedPuzzle], Optional[Program]]
_get_inner_solution: Callable[[PuzzleInfo, Program], Optional[Program]]

def match(self, puzzle: Program) -> Optional[PuzzleInfo]:
def match(self, puzzle: UncurriedPuzzle) -> Optional[PuzzleInfo]:
matched, curried_args = match_singleton_puzzle(puzzle)
if matched:
singleton_struct, inner_puzzle = curried_args
Expand All @@ -33,7 +34,7 @@ def match(self, puzzle: Program) -> Optional[PuzzleInfo]:
"launcher_id": "0x" + singleton_struct.as_python()[1].hex(),
"launcher_ph": "0x" + singleton_struct.as_python()[2].hex(),
}
next_constructor = self._match(inner_puzzle)
next_constructor = self._match(uncurry_puzzle(inner_puzzle))
if next_constructor is not None:
constructor_dict["also"] = next_constructor.info
return PuzzleInfo(constructor_dict)
Expand All @@ -50,13 +51,13 @@ def construct(self, constructor: PuzzleInfo, inner_puzzle: Program) -> Program:
launcher_hash = constructor["launcher_ph"] if "launcher_ph" in constructor else SINGLETON_LAUNCHER_HASH
return puzzle_for_singleton(constructor["launcher_id"], inner_puzzle, launcher_hash)

def get_inner_puzzle(self, constructor: PuzzleInfo, puzzle_reveal: Program) -> Optional[Program]:
def get_inner_puzzle(self, constructor: PuzzleInfo, puzzle_reveal: UncurriedPuzzle) -> Optional[Program]:
matched, curried_args = match_singleton_puzzle(puzzle_reveal)
if matched:
_, inner_puzzle = curried_args
also = constructor.also()
if also is not None:
deep_inner_puzzle: Optional[Program] = self._get_inner_puzzle(also, inner_puzzle)
deep_inner_puzzle: Optional[Program] = self._get_inner_puzzle(also, uncurry_puzzle(inner_puzzle))
return deep_inner_puzzle
else:
return inner_puzzle
Expand All @@ -80,7 +81,7 @@ def solve(self, constructor: PuzzleInfo, solver: Solver, inner_puzzle: Program,
also = constructor.also()
if also is not None:
inner_solution = self._solve(also, solver, inner_puzzle, inner_solution)
matched, curried_args = match_singleton_puzzle(parent_spend.puzzle_reveal.to_program())
matched, curried_args = match_singleton_puzzle(uncurry_puzzle(parent_spend.puzzle_reveal.to_program()))
assert matched
_, parent_inner_puzzle = curried_args
return solution_for_singleton(
Expand Down
16 changes: 8 additions & 8 deletions chia/wallet/nft_wallet/transfer_program_puzzle.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@
from chia.wallet.puzzle_drivers import PuzzleInfo, Solver
from chia.wallet.puzzles.load_clvm import load_clvm
from chia.wallet.puzzles.singleton_top_layer_v1_1 import SINGLETON_LAUNCHER_HASH, SINGLETON_MOD_HASH
from chia.wallet.uncurried_puzzle import UncurriedPuzzle

TRANSFER_PROGRAM_MOD = load_clvm("nft_ownership_transfer_program_one_way_claim_with_royalties.clvm")


def match_transfer_program_puzzle(puzzle: Program) -> Tuple[bool, List[Program]]:
mod, args = puzzle.uncurry()
if mod == TRANSFER_PROGRAM_MOD:
return True, list(args.as_iter())
def match_transfer_program_puzzle(puzzle: UncurriedPuzzle) -> Tuple[bool, List[Program]]:
if puzzle.mod == TRANSFER_PROGRAM_MOD:
return True, list(puzzle.args.as_iter())
return False, []


Expand All @@ -39,13 +39,13 @@ def solution_for_transfer_program(

@dataclass(frozen=True)
class TransferProgramPuzzle:
_match: Callable[[Program], Optional[PuzzleInfo]]
_match: Callable[[UncurriedPuzzle], Optional[PuzzleInfo]]
_construct: Callable[[PuzzleInfo, Program], Program]
_solve: Callable[[PuzzleInfo, Solver, Program, Program], Program]
_get_inner_puzzle: Callable[[PuzzleInfo, Program], Optional[Program]]
_get_inner_puzzle: Callable[[PuzzleInfo, UncurriedPuzzle], Optional[Program]]
_get_inner_solution: Callable[[PuzzleInfo, Program], Optional[Program]]

def match(self, puzzle: Program) -> Optional[PuzzleInfo]:
def match(self, puzzle: UncurriedPuzzle) -> Optional[PuzzleInfo]:
matched, curried_args = match_transfer_program_puzzle(puzzle)
if matched:
singleton_struct, royalty_puzzle_hash, percentage = curried_args
Expand All @@ -67,7 +67,7 @@ def construct(self, constructor: PuzzleInfo, inner_puzzle: Program) -> Program:
constructor["launcher_id"], constructor["royalty_address"], constructor["royalty_percentage"]
)

def get_inner_puzzle(self, constructor: PuzzleInfo, puzzle_reveal: Program) -> Optional[Program]:
def get_inner_puzzle(self, constructor: PuzzleInfo, puzzle_reveal: UncurriedPuzzle) -> Optional[Program]:
return None

def get_inner_solution(self, constructor: PuzzleInfo, solution: Program) -> Optional[Program]:
Expand Down
Loading