chia-blockchain/chia/wallet/did_wallet/did_wallet_puzzles.py

95 lines
3.2 KiB
Python

from clvm_tools import binutils
from chia.types.blockchain_format.sized_bytes import bytes32
from chia.types.blockchain_format.program import Program
from typing import List, Optional, Tuple
from blspy import G1Element
from chia.types.blockchain_format.coin import Coin
from chia.types.coin_solution import CoinSolution
from chia.util.ints import uint64
from chia.wallet.puzzles.load_clvm import load_clvm
from chia.types.condition_opcodes import ConditionOpcode
DID_CORE_MOD = load_clvm("singleton_top_layer.clvm")
DID_INNERPUZ_MOD = load_clvm("did_innerpuz.clvm")
def create_innerpuz(pubkey: bytes, identities: List[bytes], num_of_backup_ids_needed: uint64) -> Program:
backup_ids_hash = Program(Program.to(identities)).get_tree_hash()
return DID_INNERPUZ_MOD.curry(DID_CORE_MOD.get_tree_hash(), pubkey, backup_ids_hash, num_of_backup_ids_needed)
def create_fullpuz(innerpuz, genesis_puzhash) -> Program:
mod_hash = DID_CORE_MOD.get_tree_hash()
return DID_CORE_MOD.curry(mod_hash, genesis_puzhash, innerpuz)
def get_pubkey_from_innerpuz(innerpuz: Program) -> G1Element:
ret = uncurry_innerpuz(innerpuz)
if ret is not None:
pubkey_program = ret[0]
else:
raise ValueError("Unable to extract pubkey")
pubkey = G1Element.from_bytes(pubkey_program.as_atom())
return pubkey
def is_did_innerpuz(inner_f: Program):
"""
You may want to generalize this if different `CC_MOD` templates are supported.
"""
return inner_f == DID_INNERPUZ_MOD
def is_did_core(inner_f: Program):
return inner_f == DID_CORE_MOD
def uncurry_innerpuz(puzzle: Program) -> Optional[Tuple[Program, Program]]:
"""
Take a puzzle and return `None` if it's not a `CC_MOD` cc, or
a triple of `mod_hash, genesis_coin_checker, inner_puzzle` if it is.
"""
r = puzzle.uncurry()
if r is None:
return r
inner_f, args = r
if not is_did_innerpuz(inner_f):
return None
core_mod, pubkey, id_list, num_of_backup_ids_needed = list(args.as_iter())
return pubkey, id_list
def get_innerpuzzle_from_puzzle(puzzle: Program) -> Optional[Program]:
r = puzzle.uncurry()
if r is None:
return None
inner_f, args = r
if not is_did_core(inner_f):
return None
mod_hash, genesis_id, inner_puzzle = list(args.as_iter())
return inner_puzzle
def create_recovery_message_puzzle(recovering_coin: bytes32, newpuz: bytes32, pubkey: G1Element):
puzstring = f"(q . ((0x{ConditionOpcode.CREATE_COIN_ANNOUNCEMENT.hex()} 0x{recovering_coin.hex()}) (0x{ConditionOpcode.AGG_SIG_UNSAFE.hex()} 0x{bytes(pubkey).hex()} 0x{newpuz.hex()})))" # noqa
puz = binutils.assemble(puzstring)
return Program.to(puz)
def create_spend_for_message(parent_of_message, recovering_coin, newpuz, pubkey):
puzzle = create_recovery_message_puzzle(recovering_coin, newpuz, pubkey)
coin = Coin(parent_of_message, puzzle.get_tree_hash(), uint64(0))
solution = Program.to([])
coinsol = CoinSolution(coin, puzzle, solution)
return coinsol
# inspect puzzle and check it is a DID puzzle
def check_is_did_puzzle(puzzle: Program):
r = puzzle.uncurry()
if r is None:
return r
inner_f, args = r
return is_did_core(inner_f)