mirror of
https://github.com/Chia-Network/chia-blockchain.git
synced 2024-09-20 08:05:33 +03:00
e5b94d34a0
* Rename chialisp file extensions * Add pre-commit check for .clvm files * Delete sha256tree files * remove hash files in favor of central dictionary * Add check for missing files like hex and hash entries * Enhance clsp pre-commit check * Actually check hash matches * Update tools/manage_clvm.py Co-authored-by: Kyle Altendorf <sda@fstab.net> * Update tools/manage_clvm.py Co-authored-by: Kyle Altendorf <sda@fstab.net> * Fix Windows file writing * Fix setup.py package_data fields * Load hash dict at runtime * Move away from exception pattern * Bad equality check * Minor fixes * remove trailing whitespace fix --------- Co-authored-by: Kyle Altendorf <sda@fstab.net>
200 lines
6.4 KiB
Python
200 lines
6.4 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import Dict, Iterator, List, Optional, Tuple
|
|
|
|
from blspy import G1Element
|
|
|
|
from chia.types.blockchain_format.coin import Coin
|
|
from chia.types.blockchain_format.program import Program
|
|
from chia.types.blockchain_format.sized_bytes import bytes32
|
|
from chia.types.coin_spend import CoinSpend
|
|
from chia.types.condition_opcodes import ConditionOpcode
|
|
from chia.util.ints import uint64
|
|
from chia.wallet.puzzles.load_clvm import load_clvm_maybe_recompile
|
|
from chia.wallet.singleton import (
|
|
SINGLETON_LAUNCHER_PUZZLE_HASH,
|
|
SINGLETON_TOP_LAYER_MOD,
|
|
SINGLETON_TOP_LAYER_MOD_HASH,
|
|
is_singleton,
|
|
)
|
|
from chia.wallet.util.curry_and_treehash import calculate_hash_of_quoted_mod_hash, curry_and_treehash
|
|
|
|
DID_INNERPUZ_MOD = load_clvm_maybe_recompile("did_innerpuz.clsp")
|
|
DID_INNERPUZ_MOD_HASH = DID_INNERPUZ_MOD.get_tree_hash()
|
|
INTERMEDIATE_LAUNCHER_MOD = load_clvm_maybe_recompile("nft_intermediate_launcher.clsp")
|
|
|
|
|
|
def create_innerpuz(
|
|
p2_puzzle: Program,
|
|
recovery_list: List[bytes32],
|
|
num_of_backup_ids_needed: uint64,
|
|
launcher_id: bytes32,
|
|
metadata: Program = Program.to([]),
|
|
recovery_list_hash: bytes32 = None,
|
|
) -> Program:
|
|
"""
|
|
Create DID inner puzzle
|
|
:param p2_puzzle: Standard P2 puzzle
|
|
:param recovery_list: A list of DIDs used for the recovery
|
|
:param num_of_backup_ids_needed: Need how many DIDs for the recovery
|
|
:param launcher_id: ID of the launch coin
|
|
:param metadata: DID customized metadata
|
|
:param recovery_list_hash: Recovery list hash
|
|
:return: DID inner puzzle
|
|
"""
|
|
backup_ids_hash = Program(Program.to(recovery_list)).get_tree_hash()
|
|
if recovery_list_hash is not None:
|
|
backup_ids_hash = recovery_list_hash
|
|
singleton_struct = Program.to((SINGLETON_TOP_LAYER_MOD_HASH, (launcher_id, SINGLETON_LAUNCHER_PUZZLE_HASH)))
|
|
return DID_INNERPUZ_MOD.curry(p2_puzzle, backup_ids_hash, num_of_backup_ids_needed, singleton_struct, metadata)
|
|
|
|
|
|
def get_inner_puzhash_by_p2(
|
|
p2_puzhash: bytes32,
|
|
recovery_list: List[bytes32],
|
|
num_of_backup_ids_needed: uint64,
|
|
launcher_id: bytes32,
|
|
metadata: Program = Program.to([]),
|
|
) -> bytes32:
|
|
"""
|
|
Calculate DID inner puzzle hash based on a P2 puzzle hash
|
|
:param p2_puzhash: P2 puzzle hash
|
|
:param recovery_list: A list of DIDs used for the recovery
|
|
:param num_of_backup_ids_needed: Need how many DIDs for the recovery
|
|
:param launcher_id: ID of the launch coin
|
|
:param metadata: DID customized metadata
|
|
:return: DID inner puzzle hash
|
|
"""
|
|
|
|
backup_ids_hash = Program(Program.to(recovery_list)).get_tree_hash()
|
|
singleton_struct = Program.to((SINGLETON_TOP_LAYER_MOD_HASH, (launcher_id, SINGLETON_LAUNCHER_PUZZLE_HASH)))
|
|
|
|
quoted_mod_hash = calculate_hash_of_quoted_mod_hash(DID_INNERPUZ_MOD_HASH)
|
|
|
|
return curry_and_treehash(
|
|
quoted_mod_hash,
|
|
p2_puzhash,
|
|
Program.to(backup_ids_hash).get_tree_hash(),
|
|
Program.to(num_of_backup_ids_needed).get_tree_hash(),
|
|
Program.to(singleton_struct).get_tree_hash(),
|
|
metadata.get_tree_hash(),
|
|
)
|
|
|
|
|
|
def is_did_innerpuz(inner_f: Program) -> bool:
|
|
"""
|
|
Check if a puzzle is a DID inner mode
|
|
:param inner_f: puzzle
|
|
:return: Boolean
|
|
"""
|
|
return inner_f == DID_INNERPUZ_MOD
|
|
|
|
|
|
def uncurry_innerpuz(puzzle: Program) -> Optional[Tuple[Program, Program, Program, Program, Program]]:
|
|
"""
|
|
Uncurry a DID inner puzzle
|
|
:param puzzle: DID puzzle
|
|
:return: Curried parameters
|
|
"""
|
|
r = puzzle.uncurry()
|
|
if r is None:
|
|
return r
|
|
inner_f, args = r
|
|
if not is_did_innerpuz(inner_f):
|
|
return None
|
|
|
|
p2_puzzle, id_list, num_of_backup_ids_needed, singleton_struct, metadata = list(args.as_iter())
|
|
return p2_puzzle, id_list, num_of_backup_ids_needed, singleton_struct, metadata
|
|
|
|
|
|
def create_recovery_message_puzzle(recovering_coin_id: bytes32, newpuz: bytes32, pubkey: G1Element) -> Program:
|
|
"""
|
|
Create attestment message puzzle
|
|
:param recovering_coin_id: ID of the DID coin needs to recover
|
|
:param newpuz: New wallet puzzle hash
|
|
:param pubkey: New wallet pubkey
|
|
:return: Message puzzle
|
|
"""
|
|
return Program.to(
|
|
(
|
|
1,
|
|
[
|
|
[ConditionOpcode.CREATE_COIN_ANNOUNCEMENT, recovering_coin_id],
|
|
[ConditionOpcode.AGG_SIG_UNSAFE, bytes(pubkey), newpuz],
|
|
],
|
|
)
|
|
)
|
|
|
|
|
|
def create_spend_for_message(
|
|
parent_of_message: bytes32, recovering_coin: bytes32, newpuz: bytes32, pubkey: G1Element
|
|
) -> CoinSpend:
|
|
"""
|
|
Create a CoinSpend for a atestment
|
|
:param parent_of_message: Parent coin ID
|
|
:param recovering_coin: ID of the DID coin needs to recover
|
|
:param newpuz: New wallet puzzle hash
|
|
:param pubkey: New wallet pubkey
|
|
:return: CoinSpend
|
|
"""
|
|
puzzle = create_recovery_message_puzzle(recovering_coin, newpuz, pubkey)
|
|
coin = Coin(parent_of_message, puzzle.get_tree_hash(), uint64(0))
|
|
solution = Program.to([])
|
|
coinsol = CoinSpend(coin, puzzle, solution)
|
|
return coinsol
|
|
|
|
|
|
def match_did_puzzle(mod: Program, curried_args: Program) -> Optional[Iterator[Program]]:
|
|
"""
|
|
Given a puzzle test if it's a DID, if it is, return the curried arguments
|
|
:param puzzle: Puzzle
|
|
:return: Curried parameters
|
|
"""
|
|
try:
|
|
if mod == SINGLETON_TOP_LAYER_MOD:
|
|
mod, curried_args = curried_args.rest().first().uncurry()
|
|
if mod == DID_INNERPUZ_MOD:
|
|
return curried_args.as_iter()
|
|
except Exception:
|
|
import traceback
|
|
|
|
print(f"exception: {traceback.format_exc()}")
|
|
return None
|
|
|
|
|
|
def check_is_did_puzzle(puzzle: Program) -> bool:
|
|
"""
|
|
Check if a puzzle is a DID puzzle
|
|
:param puzzle: Puzzle
|
|
:return: Boolean
|
|
"""
|
|
r = puzzle.uncurry()
|
|
if r is None:
|
|
return False
|
|
inner_f, args = r
|
|
return is_singleton(inner_f)
|
|
|
|
|
|
def metadata_to_program(metadata: Dict) -> Program:
|
|
"""
|
|
Convert the metadata dict to a Chialisp program
|
|
:param metadata: User defined metadata
|
|
:return: Chialisp program
|
|
"""
|
|
kv_list = []
|
|
for key, value in metadata.items():
|
|
kv_list.append((key, value))
|
|
return Program.to(kv_list)
|
|
|
|
|
|
def program_to_metadata(program: Program) -> Dict:
|
|
"""
|
|
Convert a program to a metadata dict
|
|
:param program: Chialisp program contains the metadata
|
|
:return: Metadata dict
|
|
"""
|
|
metadata = {}
|
|
for key, val in program.as_python():
|
|
metadata[str(key, "utf-8")] = str(val, "utf-8")
|
|
return metadata
|