Files
chia-blockchain/chia/wallet/singleton.py
Matt HauffandGitHub 40db4635a8 [LABS-245] Enable PEP604 Ruff rules (#20269)
* Enable PEP604 Ruff rules

* Fix harcoded signature in test

* Hack CLVMStreamable test with note to fast follow
2025-11-18 12:34:00 -08:00

112 lines
3.9 KiB
Python

from __future__ import annotations
from chia_puzzles_py.programs import (
SINGLETON_LAUNCHER,
SINGLETON_LAUNCHER_HASH,
SINGLETON_TOP_LAYER_V1_1,
SINGLETON_TOP_LAYER_V1_1_HASH,
)
from chia_rs import CoinSpend
from chia_rs.sized_bytes import bytes32
from chia.types.blockchain_format.coin import Coin
from chia.types.blockchain_format.program import Program, uncurry
from chia.types.blockchain_format.serialized_program import SerializedProgram
from chia.wallet.util.compute_additions import compute_additions
from chia.wallet.util.curry_and_treehash import (
calculate_hash_of_quoted_mod_hash,
curry_and_treehash,
shatree_atom,
shatree_pair,
)
SINGLETON_TOP_LAYER_MOD = Program.from_bytes(SINGLETON_TOP_LAYER_V1_1)
SINGLETON_TOP_LAYER_MOD_HASH = bytes32(SINGLETON_TOP_LAYER_V1_1_HASH)
SINGLETON_TOP_LAYER_MOD_HASH_TREE_HASH = shatree_atom(SINGLETON_TOP_LAYER_MOD_HASH)
SINGLETON_TOP_LAYER_MOD_HASH_QUOTED = calculate_hash_of_quoted_mod_hash(SINGLETON_TOP_LAYER_MOD_HASH)
SINGLETON_LAUNCHER_PUZZLE = Program.from_bytes(SINGLETON_LAUNCHER)
SINGLETON_LAUNCHER_PUZZLE_HASH = bytes32(SINGLETON_LAUNCHER_HASH)
SINGLETON_LAUNCHER_PUZZLE_HASH_TREE_HASH = shatree_atom(SINGLETON_LAUNCHER_PUZZLE_HASH)
def get_inner_puzzle_from_singleton(puzzle: Program | SerializedProgram) -> Program | None:
"""
Extract the inner puzzle of a singleton
:param puzzle: Singleton puzzle
:return: Inner puzzle
"""
r = uncurry(puzzle)
if r is None:
return None
inner_f, args = r
if not is_singleton(inner_f):
return None
_SINGLETON_STRUCT, INNER_PUZZLE = list(args.as_iter())
return Program(INNER_PUZZLE)
def get_singleton_id_from_puzzle(puzzle: Program | SerializedProgram) -> bytes32 | None:
"""
Extract the singleton ID from a singleton puzzle
:param puzzle: Singleton puzzle
:return: Inner puzzle
"""
r = uncurry(puzzle)
if r is None:
return None # pragma: no cover
inner_f, args = r
if not is_singleton(inner_f):
return None
SINGLETON_STRUCT, _INNER_PUZZLE = list(args.as_iter())
return bytes32(Program(SINGLETON_STRUCT).rest().first().as_atom())
def is_singleton(inner_f: Program | SerializedProgram) -> bool:
"""
Check if a puzzle is a singleton mod
:param inner_f: puzzle
:return: Boolean
"""
return inner_f == SINGLETON_TOP_LAYER_MOD
def create_singleton_puzzle_hash(innerpuz_hash: bytes32, launcher_id: bytes32) -> bytes32:
"""
Return Hash ID of the whole Singleton Puzzle
:param innerpuz_hash: Singleton inner puzzle tree hash
:param launcher_id: launcher coin name
:return: Singleton full puzzle hash
"""
# singleton_struct = (MOD_HASH . (LAUNCHER_ID . LAUNCHER_PUZZLE_HASH))
singleton_struct = shatree_pair(
SINGLETON_TOP_LAYER_MOD_HASH_TREE_HASH,
shatree_pair(shatree_atom(launcher_id), SINGLETON_LAUNCHER_PUZZLE_HASH_TREE_HASH),
)
return curry_and_treehash(SINGLETON_TOP_LAYER_MOD_HASH_QUOTED, singleton_struct, innerpuz_hash)
def create_singleton_puzzle(innerpuz: Program | SerializedProgram, launcher_id: bytes32) -> Program:
"""
Create a full Singleton puzzle
:param innerpuz: Singleton inner puzzle
:param launcher_id:
:return: Singleton full puzzle
"""
# singleton_struct = (MOD_HASH . (LAUNCHER_ID . LAUNCHER_PUZZLE_HASH))
singleton_struct = Program.to((SINGLETON_TOP_LAYER_MOD_HASH, (launcher_id, SINGLETON_LAUNCHER_PUZZLE_HASH)))
return SINGLETON_TOP_LAYER_MOD.curry(singleton_struct, innerpuz)
def get_most_recent_singleton_coin_from_coin_spend(coin_sol: CoinSpend) -> Coin | None:
additions: list[Coin] = compute_additions(coin_sol)
for coin in additions:
if coin.amount % 2 == 1:
return coin
return None # pragma: no cover
def get_singleton_struct_for_id(id: bytes32) -> Program:
singleton_struct: Program = Program.to((SINGLETON_TOP_LAYER_MOD_HASH, (id, SINGLETON_LAUNCHER_PUZZLE_HASH)))
return singleton_struct