Reorganize reusable singleton wallet code (#14339)

This commit is contained in:
Adam Kelly 2023-01-27 15:09:13 -08:00 committed by GitHub
parent 4ebd51df83
commit 22dfc83d51
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 99 additions and 84 deletions

View File

@ -47,12 +47,7 @@ from chia.wallet.derive_keys import (
from chia.wallet.did_wallet import did_wallet_puzzles
from chia.wallet.did_wallet.did_info import DIDInfo
from chia.wallet.did_wallet.did_wallet import DIDWallet
from chia.wallet.did_wallet.did_wallet_puzzles import (
DID_INNERPUZ_MOD,
create_fullpuz,
match_did_puzzle,
program_to_metadata,
)
from chia.wallet.did_wallet.did_wallet_puzzles import DID_INNERPUZ_MOD, match_did_puzzle, program_to_metadata
from chia.wallet.nft_wallet import nft_puzzles
from chia.wallet.nft_wallet.nft_info import NFTCoinInfo, NFTInfo
from chia.wallet.nft_wallet.nft_puzzles import get_metadata_and_phs
@ -62,6 +57,7 @@ from chia.wallet.notification_store import Notification
from chia.wallet.outer_puzzles import AssetType
from chia.wallet.puzzle_drivers import PuzzleInfo, Solver
from chia.wallet.puzzles.p2_delegated_puzzle_or_hidden_puzzle import puzzle_hash_for_synthetic_public_key
from chia.wallet.singleton import create_fullpuz
from chia.wallet.trade_record import TradeRecord
from chia.wallet.trading.offer import Offer
from chia.wallet.transaction_record import TransactionRecord

View File

@ -10,6 +10,7 @@ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Set, Tuple
from blspy import AugSchemeMPL, G1Element, G2Element
import chia.wallet.singleton
from chia.full_node.full_node_api import FullNodeAPI
from chia.protocols import wallet_protocol
from chia.protocols.wallet_protocol import CoinState
@ -27,7 +28,7 @@ from chia.wallet.derivation_record import DerivationRecord
from chia.wallet.derive_keys import master_sk_to_wallet_sk_unhardened
from chia.wallet.did_wallet import did_wallet_puzzles
from chia.wallet.did_wallet.did_info import DIDInfo
from chia.wallet.did_wallet.did_wallet_puzzles import create_fullpuz, uncurry_innerpuz
from chia.wallet.did_wallet.did_wallet_puzzles import uncurry_innerpuz
from chia.wallet.lineage_proof import LineageProof
from chia.wallet.puzzles.p2_delegated_puzzle_or_hidden_puzzle import (
DEFAULT_HIDDEN_PUZZLE_HASH,
@ -35,6 +36,7 @@ from chia.wallet.puzzles.p2_delegated_puzzle_or_hidden_puzzle import (
puzzle_for_pk,
puzzle_hash_for_pk,
)
from chia.wallet.singleton import create_fullpuz
from chia.wallet.transaction_record import TransactionRecord
from chia.wallet.util.compute_memos import compute_memos
from chia.wallet.util.transaction_type import TransactionType
@ -371,7 +373,7 @@ class DIDWallet:
response = await peer.call_api(FullNodeAPI.request_puzzle_solution, puzzle_solution_request)
req_puz_sol = response.response
assert req_puz_sol.puzzle is not None
parent_innerpuz = did_wallet_puzzles.get_innerpuzzle_from_puzzle(req_puz_sol.puzzle.to_program())
parent_innerpuz = chia.wallet.singleton.get_innerpuzzle_from_puzzle(req_puz_sol.puzzle.to_program())
if parent_innerpuz:
parent_info = LineageProof(
parent_state.coin.parent_coin_info,
@ -489,7 +491,7 @@ class DIDWallet:
assert children_state.created_height
parent_spend = await wallet_node.fetch_puzzle_solution(children_state.created_height, parent_coin, peer)
assert parent_spend is not None
parent_innerpuz = did_wallet_puzzles.get_innerpuzzle_from_puzzle(
parent_innerpuz = chia.wallet.singleton.get_innerpuzzle_from_puzzle(
parent_spend.puzzle_reveal.to_program()
)
assert parent_innerpuz is not None
@ -525,10 +527,10 @@ class DIDWallet:
self.did_info.origin_coin.name(),
did_wallet_puzzles.metadata_to_program(json.loads(self.did_info.metadata)),
)
return did_wallet_puzzles.create_fullpuz(innerpuz, self.did_info.origin_coin.name())
return chia.wallet.singleton.create_fullpuz(innerpuz, self.did_info.origin_coin.name())
else:
innerpuz = Program.to((8, 0))
return did_wallet_puzzles.create_fullpuz(innerpuz, bytes32([0] * 32))
return chia.wallet.singleton.create_fullpuz(innerpuz, bytes32([0] * 32))
def puzzle_hash_for_pk(self, pubkey: G1Element) -> bytes32:
if self.did_info.origin_coin is None:
@ -542,7 +544,7 @@ class DIDWallet:
origin_coin_name,
did_wallet_puzzles.metadata_to_program(json.loads(self.did_info.metadata)),
)
return did_wallet_puzzles.create_fullpuz_hash(innerpuz_hash, origin_coin_name)
return chia.wallet.singleton.create_fullpuz_hash(innerpuz_hash, origin_coin_name)
async def get_new_puzzle(self) -> Program:
return self.puzzle_for_pk(
@ -590,7 +592,7 @@ class DIDWallet:
# full solution is (corehash parent_info my_amount innerpuz_reveal solution)
innerpuz: Program = self.did_info.current_inner
full_puzzle: Program = did_wallet_puzzles.create_fullpuz(
full_puzzle: Program = chia.wallet.singleton.create_fullpuz(
innerpuz,
self.did_info.origin_coin.name(),
)
@ -608,7 +610,7 @@ class DIDWallet:
]
)
# Create an additional spend to confirm the change on-chain
new_full_puzzle: Program = did_wallet_puzzles.create_fullpuz(
new_full_puzzle: Program = chia.wallet.singleton.create_fullpuz(
new_inner_puzzle,
self.did_info.origin_coin.name(),
)
@ -702,7 +704,7 @@ class DIDWallet:
innersol = Program.to([2, p2_solution, [], [], [], self.did_info.backup_ids])
# full solution is (corehash parent_info my_amount innerpuz_reveal solution)
full_puzzle: Program = did_wallet_puzzles.create_fullpuz(
full_puzzle: Program = chia.wallet.singleton.create_fullpuz(
self.did_info.current_inner,
self.did_info.origin_coin.name(),
)
@ -786,7 +788,7 @@ class DIDWallet:
innersol: Program = Program.to([1, p2_solution])
# full solution is (corehash parent_info my_amount innerpuz_reveal solution)
full_puzzle: Program = did_wallet_puzzles.create_fullpuz(
full_puzzle: Program = chia.wallet.singleton.create_fullpuz(
innerpuz,
self.did_info.origin_coin.name(),
)
@ -821,7 +823,7 @@ class DIDWallet:
# full solution is (corehash parent_info my_amount innerpuz_reveal solution)
innerpuz: Program = self.did_info.current_inner
full_puzzle: Program = did_wallet_puzzles.create_fullpuz(
full_puzzle: Program = chia.wallet.singleton.create_fullpuz(
innerpuz,
self.did_info.origin_coin.name(),
)
@ -900,7 +902,7 @@ class DIDWallet:
innersol = Program.to([1, p2_solution])
# full solution is (corehash parent_info my_amount innerpuz_reveal solution)
full_puzzle: Program = did_wallet_puzzles.create_fullpuz(
full_puzzle: Program = chia.wallet.singleton.create_fullpuz(
innerpuz,
self.did_info.origin_coin.name(),
)
@ -1017,7 +1019,7 @@ class DIDWallet:
# full solution is (parent_info my_amount solution)
assert self.did_info.current_inner is not None
innerpuz: Program = self.did_info.current_inner
full_puzzle: Program = did_wallet_puzzles.create_fullpuz(
full_puzzle: Program = chia.wallet.singleton.create_fullpuz(
innerpuz,
self.did_info.origin_coin.name(),
)
@ -1227,12 +1229,12 @@ class DIDWallet:
return None
origin = coins.copy().pop()
genesis_launcher_puz = did_wallet_puzzles.LAUNCHER_PUZZLE
genesis_launcher_puz = chia.wallet.singleton.LAUNCHER_PUZZLE
launcher_coin = Coin(origin.name(), genesis_launcher_puz.get_tree_hash(), amount)
did_inner: Program = await self.get_new_did_innerpuz(launcher_coin.name())
did_inner_hash = did_inner.get_tree_hash()
did_full_puz = did_wallet_puzzles.create_fullpuz(did_inner, launcher_coin.name())
did_full_puz = chia.wallet.singleton.create_fullpuz(did_inner, launcher_coin.name())
did_puzzle_hash = did_full_puz.get_tree_hash()
announcement_set: Set[Announcement] = set()

View File

@ -11,14 +11,15 @@ 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 (
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
SINGLETON_TOP_LAYER_MOD = load_clvm_maybe_recompile("singleton_top_layer_v1_1.clvm")
SINGLETON_TOP_LAYER_MOD_HASH = SINGLETON_TOP_LAYER_MOD.get_tree_hash()
SINGLETON_TOP_LAYER_MOD_HASH_QUOTED = calculate_hash_of_quoted_mod_hash(SINGLETON_TOP_LAYER_MOD_HASH)
LAUNCHER_PUZZLE = load_clvm_maybe_recompile("singleton_launcher.clvm")
DID_INNERPUZ_MOD = load_clvm_maybe_recompile("did_innerpuz.clvm")
LAUNCHER_PUZZLE_HASH = LAUNCHER_PUZZLE.get_tree_hash()
DID_INNERPUZ_MOD_HASH = DID_INNERPUZ_MOD.get_tree_hash()
INTERMEDIATE_LAUNCHER_MOD = load_clvm_maybe_recompile("nft_intermediate_launcher.clvm")
@ -80,31 +81,6 @@ def get_inner_puzhash_by_p2(
)
def create_fullpuz(innerpuz: Program, launcher_id: bytes32) -> Program:
"""
Create a full puzzle of DID
:param innerpuz: DID inner puzzle
:param launcher_id:
:return: DID full puzzle
"""
# singleton_struct = (MOD_HASH . (LAUNCHER_ID . LAUNCHER_PUZZLE_HASH))
singleton_struct = Program.to((SINGLETON_TOP_LAYER_MOD_HASH, (launcher_id, LAUNCHER_PUZZLE_HASH)))
return SINGLETON_TOP_LAYER_MOD.curry(singleton_struct, innerpuz)
def create_fullpuz_hash(innerpuz_hash: bytes32, launcher_id: bytes32) -> bytes32:
"""
Create a full puzzle of DID
:param innerpuz_hash: DID inner puzzle tree hash
:param launcher_id: launcher coin name
:return: DID full puzzle hash
"""
# singleton_struct = (MOD_HASH . (LAUNCHER_ID . LAUNCHER_PUZZLE_HASH))
singleton_struct = Program.to((SINGLETON_TOP_LAYER_MOD_HASH, (launcher_id, LAUNCHER_PUZZLE_HASH)))
return curry_and_treehash(SINGLETON_TOP_LAYER_MOD_HASH_QUOTED, singleton_struct.get_tree_hash(), innerpuz_hash)
def is_did_innerpuz(inner_f: Program) -> bool:
"""
Check if a puzzle is a DID inner mode
@ -114,15 +90,6 @@ def is_did_innerpuz(inner_f: Program) -> bool:
return inner_f == DID_INNERPUZ_MOD
def is_did_core(inner_f: Program) -> bool:
"""
Check if a puzzle is a singleton mod
:param inner_f: puzzle
:return: Boolean
"""
return inner_f == SINGLETON_TOP_LAYER_MOD
def uncurry_innerpuz(puzzle: Program) -> Optional[Tuple[Program, Program, Program, Program, Program]]:
"""
Uncurry a DID inner puzzle
@ -140,22 +107,6 @@ def uncurry_innerpuz(puzzle: Program) -> Optional[Tuple[Program, Program, Progra
return p2_puzzle, id_list, num_of_backup_ids_needed, singleton_struct, metadata
def get_innerpuzzle_from_puzzle(puzzle: Program) -> Optional[Program]:
"""
Extract the inner puzzle of a singleton
:param puzzle: Singleton puzzle
:return: Inner puzzle
"""
r = puzzle.uncurry()
if r is None:
return None
inner_f, args = r
if not is_did_core(inner_f):
return None
SINGLETON_STRUCT, INNER_PUZZLE = list(args.as_iter())
return INNER_PUZZLE
def create_recovery_message_puzzle(recovering_coin_id: bytes32, newpuz: bytes32, pubkey: G1Element) -> Program:
"""
Create attestment message puzzle
@ -219,9 +170,9 @@ def check_is_did_puzzle(puzzle: Program) -> bool:
"""
r = puzzle.uncurry()
if r is None:
return r
return False
inner_f, args = r
return is_did_core(inner_f)
return is_singleton(inner_f)
def metadata_to_program(metadata: Dict) -> Program:

View File

@ -10,6 +10,7 @@ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Set, Tuple, Type, T
from blspy import AugSchemeMPL, G1Element, G2Element
from clvm.casts import int_from_bytes, int_to_bytes
import chia.wallet.singleton
from chia.protocols.wallet_protocol import CoinState
from chia.server.ws_connection import WSChiaConnection
from chia.types.announcement import Announcement
@ -1284,7 +1285,7 @@ class NFTWallet:
for mint_number in range(mint_number_start, mint_number_end):
# Create the puzzle, solution and coin spend for the intermediate launcher
intermediate_launcher_puz = did_wallet_puzzles.INTERMEDIATE_LAUNCHER_MOD.curry(
did_wallet_puzzles.LAUNCHER_PUZZLE_HASH, mint_number, mint_total
chia.wallet.singleton.LAUNCHER_PUZZLE_HASH, mint_number, mint_total
)
intermediate_launcher_ph = intermediate_launcher_puz.get_tree_hash()
primaries.append(
@ -1310,7 +1311,7 @@ class NFTWallet:
did_announcements.add(std_hash(intermediate_launcher_coin.name() + intermediate_announcement_message))
# Create the launcher coin, and add its id to a list to be asserted in the DID spend
launcher_coin = Coin(intermediate_launcher_coin.name(), did_wallet_puzzles.LAUNCHER_PUZZLE_HASH, amount)
launcher_coin = Coin(intermediate_launcher_coin.name(), chia.wallet.singleton.LAUNCHER_PUZZLE_HASH, amount)
launcher_ids.append(launcher_coin.name())
# Grab the metadata from metadata_list. The index for metadata_list
@ -1335,7 +1336,7 @@ class NFTWallet:
genesis_launcher_solution = Program.to([eve_fullpuz.get_tree_hash(), amount, []])
launcher_cs = CoinSpend(launcher_coin, did_wallet_puzzles.LAUNCHER_PUZZLE, genesis_launcher_solution)
launcher_cs = CoinSpend(launcher_coin, chia.wallet.singleton.LAUNCHER_PUZZLE, genesis_launcher_solution)
launcher_spends.append(launcher_cs)
eve_coin = Coin(launcher_coin.name(), eve_fullpuz.get_tree_hash(), uint64(amount))
@ -1429,7 +1430,7 @@ class NFTWallet:
puzzle_announcements_to_assert=puzzle_assertions,
)
did_inner_sol: Program = Program.to([1, did_p2_solution])
did_full_puzzle: Program = did_wallet_puzzles.create_fullpuz(
did_full_puzzle: Program = chia.wallet.singleton.create_fullpuz(
innerpuz,
did_wallet.did_info.origin_coin.name(),
)

64
chia/wallet/singleton.py Normal file
View File

@ -0,0 +1,64 @@
from __future__ import annotations
from typing import Optional
from chia.types.blockchain_format.program import Program
from chia.types.blockchain_format.sized_bytes import bytes32
from chia.wallet.puzzles.load_clvm import load_clvm_maybe_recompile
from chia.wallet.util.curry_and_treehash import calculate_hash_of_quoted_mod_hash, curry_and_treehash
SINGLETON_TOP_LAYER_MOD = load_clvm_maybe_recompile("singleton_top_layer_v1_1.clvm")
SINGLETON_TOP_LAYER_MOD_HASH = SINGLETON_TOP_LAYER_MOD.get_tree_hash()
SINGLETON_TOP_LAYER_MOD_HASH_QUOTED = calculate_hash_of_quoted_mod_hash(SINGLETON_TOP_LAYER_MOD_HASH)
LAUNCHER_PUZZLE = load_clvm_maybe_recompile("singleton_launcher.clvm")
LAUNCHER_PUZZLE_HASH = LAUNCHER_PUZZLE.get_tree_hash()
def get_innerpuzzle_from_puzzle(puzzle: Program) -> Optional[Program]:
"""
Extract the inner puzzle of a singleton
:param puzzle: Singleton puzzle
:return: Inner puzzle
"""
r = puzzle.uncurry()
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 is_singleton(inner_f: Program) -> bool:
"""
Check if a puzzle is a singleton mod
:param inner_f: puzzle
:return: Boolean
"""
return inner_f == SINGLETON_TOP_LAYER_MOD
def create_fullpuz_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 = Program.to((SINGLETON_TOP_LAYER_MOD_HASH, (launcher_id, LAUNCHER_PUZZLE_HASH)))
return curry_and_treehash(SINGLETON_TOP_LAYER_MOD_HASH_QUOTED, singleton_struct.get_tree_hash(), innerpuz_hash)
def create_fullpuz(innerpuz: Program, 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, LAUNCHER_PUZZLE_HASH)))
return SINGLETON_TOP_LAYER_MOD.curry(singleton_struct, innerpuz)

View File

@ -54,7 +54,7 @@ from chia.wallet.derive_keys import (
)
from chia.wallet.did_wallet.did_info import DIDInfo
from chia.wallet.did_wallet.did_wallet import DIDWallet
from chia.wallet.did_wallet.did_wallet_puzzles import DID_INNERPUZ_MOD, create_fullpuz, match_did_puzzle
from chia.wallet.did_wallet.did_wallet_puzzles import DID_INNERPUZ_MOD, match_did_puzzle
from chia.wallet.key_val_store import KeyValStore
from chia.wallet.nft_wallet.nft_info import NFTWalletInfo
from chia.wallet.nft_wallet.nft_puzzles import get_metadata_and_phs, get_new_owner_did
@ -65,6 +65,7 @@ from chia.wallet.outer_puzzles import AssetType
from chia.wallet.puzzle_drivers import PuzzleInfo
from chia.wallet.puzzles.cat_loader import CAT_MOD, CAT_MOD_HASH
from chia.wallet.settings.user_settings import UserSettings
from chia.wallet.singleton import create_fullpuz
from chia.wallet.trade_manager import TradeManager
from chia.wallet.transaction_record import TransactionRecord
from chia.wallet.uncurried_puzzle import uncurry_puzzle

View File

@ -20,7 +20,7 @@ from chia.util.bech32m import decode_puzzle_hash, encode_puzzle_hash
from chia.util.condition_tools import conditions_dict_for_solution
from chia.util.ints import uint16, uint32, uint64
from chia.wallet.did_wallet.did_wallet import DIDWallet
from chia.wallet.did_wallet.did_wallet_puzzles import create_fullpuz
from chia.wallet.singleton import create_fullpuz
from chia.wallet.util.address_type import AddressType
from chia.wallet.util.wallet_types import WalletType
from chia.wallet.wallet import CHIP_0002_SIGN_MESSAGE_PREFIX