mirror of
https://github.com/Chia-Network/chia-blockchain.git
synced 2024-09-21 00:24:37 +03:00
pivot from string to clvm for dict entries
This commit is contained in:
parent
ca57b578f5
commit
20aa9b3476
@ -32,7 +32,8 @@ from chia.wallet.derive_keys import (
|
||||
match_address_to_sk,
|
||||
)
|
||||
from chia.wallet.did_wallet.did_wallet import DIDWallet
|
||||
from chia.wallet.outer_puzzles import AssetType, PuzzleInfo
|
||||
from chia.wallet.outer_puzzles import AssetType
|
||||
from chia.wallet.puzzle_drivers import PuzzleInfo
|
||||
from chia.wallet.rl_wallet.rl_wallet import RLWallet
|
||||
from chia.wallet.trade_record import TradeRecord
|
||||
from chia.wallet.trading.offer import Offer
|
||||
@ -925,7 +926,7 @@ class WalletRpcApi:
|
||||
driver_dict: Dict[bytes32, PuzzleInfo] = {}
|
||||
if driver_dict_str is None:
|
||||
for key in offer:
|
||||
if len(key) == 32:
|
||||
if len(key) == 64:
|
||||
asset_id = bytes32.from_hexstr(key)
|
||||
driver_dict[asset_id] = PuzzleInfo({"type": AssetType.CAT, "tail": asset_id})
|
||||
else:
|
||||
|
71
chia/wallet/cat_wallet/cat_outer_puzzle.py
Normal file
71
chia/wallet/cat_wallet/cat_outer_puzzle.py
Normal file
@ -0,0 +1,71 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, Optional
|
||||
|
||||
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.util.ints import uint64
|
||||
from chia.wallet.cat_wallet.cat_utils import (
|
||||
CAT_MOD,
|
||||
SpendableCAT,
|
||||
construct_cat_puzzle,
|
||||
match_cat_puzzle,
|
||||
unsigned_spend_bundle_for_spendable_cats,
|
||||
)
|
||||
from chia.wallet.lineage_proof import LineageProof
|
||||
from chia.wallet.puzzle_drivers import PuzzleInfo, Solver
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class CATOuterPuzzle:
|
||||
_match: Any
|
||||
_asset_id: Any
|
||||
_construct: Any
|
||||
_solve: Any
|
||||
|
||||
def match(self, puzzle: Program) -> Optional[PuzzleInfo]:
|
||||
matched, curried_args = match_cat_puzzle(puzzle)
|
||||
if matched:
|
||||
_, tail_hash, inner_puzzle = curried_args
|
||||
constructor_dict = {
|
||||
"type": "CAT",
|
||||
"tail": "0x" + tail_hash.as_python().hex(),
|
||||
}
|
||||
next_constructor = self._match(inner_puzzle)
|
||||
if next_constructor is not None:
|
||||
constructor_dict["and"] = next_constructor.info
|
||||
return PuzzleInfo(constructor_dict)
|
||||
else:
|
||||
return None
|
||||
|
||||
def asset_id(self, constructor: PuzzleInfo) -> Optional[bytes32]:
|
||||
return bytes32(constructor["tail"])
|
||||
|
||||
def construct(self, constructor: PuzzleInfo, inner_puzzle: Program) -> Program:
|
||||
if constructor.also() is not None:
|
||||
inner_puzzle = self._construct(constructor.also(), inner_puzzle)
|
||||
return construct_cat_puzzle(CAT_MOD, constructor["tail"], inner_puzzle)
|
||||
|
||||
def solve(self, constructor: PuzzleInfo, solver: Solver, inner_puzzle: Program, inner_solution: Program) -> Program:
|
||||
tail_hash: bytes32 = constructor["tail"]
|
||||
coin_bytes: bytes = solver["coin"]
|
||||
coin: Coin = Coin(bytes32(coin_bytes[0:32]), bytes32(coin_bytes[32:64]), uint64.from_bytes(coin_bytes[64:72]))
|
||||
parent_spend: CoinSpend = CoinSpend.from_bytes(solver["parent_spend"])
|
||||
parent_coin: Coin = parent_spend.coin
|
||||
if constructor.also() is not None:
|
||||
inner_puzzle = self._construct(constructor.also(), inner_puzzle)
|
||||
inner_solution = self._solve(constructor.also(), solver, inner_puzzle, inner_solution)
|
||||
matched, curried_args = match_cat_puzzle(parent_spend.puzzle_reveal.to_program())
|
||||
assert matched
|
||||
_, _, parent_inner_puzzle = curried_args
|
||||
spendable_cat = SpendableCAT(
|
||||
coin,
|
||||
tail_hash,
|
||||
inner_puzzle,
|
||||
inner_solution,
|
||||
lineage_proof=LineageProof(
|
||||
parent_coin.parent_coin_info, parent_inner_puzzle.get_tree_hash(), parent_coin.amount
|
||||
),
|
||||
)
|
||||
return unsigned_spend_bundle_for_spendable_cats(CAT_MOD, [spendable_cat]).coin_spends[0].solution.to_program()
|
@ -37,7 +37,8 @@ from chia.wallet.cat_wallet.lineage_store import CATLineageStore
|
||||
from chia.wallet.coin_selection import select_coins
|
||||
from chia.wallet.derivation_record import DerivationRecord
|
||||
from chia.wallet.lineage_proof import LineageProof
|
||||
from chia.wallet.outer_puzzles import AssetType, PuzzleInfo
|
||||
from chia.wallet.outer_puzzles import AssetType
|
||||
from chia.wallet.puzzle_drivers import PuzzleInfo
|
||||
from chia.wallet.payment import Payment
|
||||
from chia.wallet.puzzles.tails import ALL_LIMITATIONS_PROGRAMS
|
||||
from chia.wallet.puzzles.p2_delegated_puzzle_or_hidden_puzzle import (
|
||||
@ -218,7 +219,7 @@ class CATWallet:
|
||||
return await cls.create_wallet_for_cat(
|
||||
wallet_state_manager,
|
||||
wallet,
|
||||
puzzle_driver.info["tail"].hex(),
|
||||
puzzle_driver["tail"].hex(),
|
||||
name,
|
||||
in_transaction,
|
||||
)
|
||||
@ -811,8 +812,8 @@ class CATWallet:
|
||||
|
||||
def match_puzzle_info(self, puzzle_driver: PuzzleInfo) -> bool:
|
||||
if (
|
||||
puzzle_driver.info["type"] == AssetType.CAT
|
||||
and puzzle_driver.info["tail"] == bytes.fromhex(self.get_asset_id())
|
||||
AssetType(puzzle_driver.type()) == AssetType.CAT
|
||||
and puzzle_driver["tail"] == bytes.fromhex(self.get_asset_id())
|
||||
and "and" not in puzzle_driver.info
|
||||
):
|
||||
return True
|
||||
|
@ -1,91 +1,16 @@
|
||||
from enum import Enum
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
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.wallet.cat_wallet.cat_utils import (
|
||||
CAT_MOD,
|
||||
SpendableCAT,
|
||||
construct_cat_puzzle,
|
||||
match_cat_puzzle,
|
||||
unsigned_spend_bundle_for_spendable_cats,
|
||||
)
|
||||
from chia.wallet.lineage_proof import LineageProof
|
||||
from chia.wallet.cat_wallet.cat_outer_puzzle import CATOuterPuzzle
|
||||
from chia.wallet.puzzle_drivers import PuzzleInfo, Solver
|
||||
|
||||
|
||||
class AssetType(Enum):
|
||||
CAT = "CAT"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class PuzzleInfo:
|
||||
info: Dict[str, Any]
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Solver:
|
||||
info: Dict[str, Any]
|
||||
|
||||
|
||||
class CATOuterPuzzle:
|
||||
@classmethod
|
||||
def match(cls, puzzle: Program) -> Optional[PuzzleInfo]:
|
||||
matched, curried_args = match_cat_puzzle(puzzle)
|
||||
if matched:
|
||||
_, tail_hash, inner_puzzle = curried_args
|
||||
constructor_dict = {
|
||||
"type": AssetType.CAT,
|
||||
"tail": bytes32(tail_hash.as_python()),
|
||||
}
|
||||
next_constructor = match_puzzle(inner_puzzle)
|
||||
if next_constructor is not None:
|
||||
constructor_dict["and"] = next_constructor.info
|
||||
return PuzzleInfo(constructor_dict)
|
||||
else:
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def asset_id(cls, constructor: PuzzleInfo) -> Optional[bytes32]:
|
||||
return bytes32(constructor.info["tail"])
|
||||
|
||||
@classmethod
|
||||
def construct(cls, constructor: PuzzleInfo, inner_puzzle: Program) -> Program:
|
||||
if "and" in constructor.info:
|
||||
inner_puzzle = construct_puzzle(constructor.info["and"], inner_puzzle)
|
||||
return construct_cat_puzzle(CAT_MOD, constructor.info["tail"], inner_puzzle)
|
||||
|
||||
@classmethod
|
||||
def solve(cls, constructor: PuzzleInfo, solver: Solver, inner_puzzle: Program, inner_solution: Program) -> Program:
|
||||
tail_hash: bytes32 = constructor.info["tail"]
|
||||
coin: Coin = solver.info["coin"]
|
||||
parent_spend: CoinSpend = solver.info["parent_spend"]
|
||||
parent_coin: Coin = parent_spend.coin
|
||||
if "and" in constructor.info:
|
||||
inner_puzzle = construct_puzzle(PuzzleInfo(constructor.info["and"]), inner_puzzle)
|
||||
inner_solution = solve_puzzle(PuzzleInfo(constructor.info["and"]), solver, inner_puzzle, inner_solution)
|
||||
matched, curried_args = match_cat_puzzle(parent_spend.puzzle_reveal.to_program())
|
||||
assert matched
|
||||
_, _, parent_inner_puzzle = curried_args
|
||||
spendable_cat = SpendableCAT(
|
||||
coin,
|
||||
tail_hash,
|
||||
inner_puzzle,
|
||||
inner_solution,
|
||||
lineage_proof=LineageProof(
|
||||
parent_coin.parent_coin_info, parent_inner_puzzle.get_tree_hash(), parent_coin.amount
|
||||
),
|
||||
)
|
||||
return unsigned_spend_bundle_for_spendable_cats(CAT_MOD, [spendable_cat]).coin_spends[0].solution.to_program()
|
||||
|
||||
|
||||
driver_lookup: Dict[AssetType, Any] = {
|
||||
AssetType.CAT: CATOuterPuzzle,
|
||||
}
|
||||
|
||||
|
||||
def match_puzzle(puzzle: Program) -> Optional[PuzzleInfo]:
|
||||
for driver in driver_lookup.values():
|
||||
potential_info: Optional[PuzzleInfo] = driver.match(puzzle)
|
||||
@ -95,14 +20,21 @@ def match_puzzle(puzzle: Program) -> Optional[PuzzleInfo]:
|
||||
|
||||
|
||||
def construct_puzzle(constructor: PuzzleInfo, inner_puzzle: Program) -> Program:
|
||||
return driver_lookup[constructor.info["type"]].construct(constructor, inner_puzzle) # type: ignore
|
||||
return driver_lookup[AssetType(constructor.type())].construct(constructor, inner_puzzle) # type: ignore
|
||||
|
||||
|
||||
def solve_puzzle(constructor: PuzzleInfo, solver: Solver, inner_puzzle: Program, inner_solution: Program) -> Program:
|
||||
return driver_lookup[constructor.info["type"]].solve( # type: ignore
|
||||
return driver_lookup[AssetType(constructor.type())].solve( # type: ignore
|
||||
constructor, solver, inner_puzzle, inner_solution
|
||||
)
|
||||
|
||||
|
||||
def create_asset_id(constructor: PuzzleInfo) -> bytes32:
|
||||
return driver_lookup[constructor.info["type"]].asset_id(constructor) # type: ignore
|
||||
return driver_lookup[AssetType(constructor.type())].asset_id(constructor) # type: ignore
|
||||
|
||||
|
||||
function_args = [match_puzzle, construct_puzzle, solve_puzzle, create_asset_id]
|
||||
|
||||
driver_lookup: Dict[AssetType, Any] = {
|
||||
AssetType.CAT: CATOuterPuzzle(*function_args),
|
||||
}
|
||||
|
52
chia/wallet/puzzle_drivers.py
Normal file
52
chia/wallet/puzzle_drivers.py
Normal file
@ -0,0 +1,52 @@
|
||||
from clvm.casts import int_from_bytes
|
||||
from clvm_tools.binutils import assemble, type_for_atom
|
||||
from dataclasses import dataclass
|
||||
from ir.Type import Type
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class PuzzleInfo:
|
||||
info: Dict[str, Any]
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
if "type" not in self.info:
|
||||
raise ValueError("A type is required to initialize a puzzle driver")
|
||||
|
||||
def __getitem__(self, item: str) -> Any:
|
||||
value = self.info[decode_info_value(PuzzleInfo, item)]
|
||||
return decode_info_value(PuzzleInfo, value)
|
||||
|
||||
def type(self) -> str:
|
||||
return str(self.info["type"])
|
||||
|
||||
def also(self) -> Optional["PuzzleInfo"]:
|
||||
if "also" in self.info:
|
||||
return PuzzleInfo(self.info["also"])
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Solver:
|
||||
info: Dict[str, Any]
|
||||
|
||||
def __getitem__(self, item: str) -> Any:
|
||||
value = self.info[decode_info_value(Solver, item)]
|
||||
return decode_info_value(Solver, value)
|
||||
|
||||
|
||||
def decode_info_value(cls: Any, value: Any) -> Any:
|
||||
if isinstance(value, dict):
|
||||
return cls(value)
|
||||
elif isinstance(value, list):
|
||||
return [decode_info_value(cls, v) for v in value]
|
||||
else:
|
||||
atom: bytes = assemble(value).as_atom() # type: ignore
|
||||
typ = type_for_atom(atom)
|
||||
if typ == Type.QUOTES:
|
||||
return bytes(atom).decode("utf8")
|
||||
elif typ == Type.INT:
|
||||
return int_from_bytes(atom)
|
||||
else:
|
||||
return atom
|
@ -12,7 +12,8 @@ from chia.types.spend_bundle import SpendBundle
|
||||
from chia.util.db_wrapper import DBWrapper
|
||||
from chia.util.hash import std_hash
|
||||
from chia.util.ints import uint32, uint64
|
||||
from chia.wallet.outer_puzzles import AssetType, PuzzleInfo
|
||||
from chia.wallet.outer_puzzles import AssetType
|
||||
from chia.wallet.puzzle_drivers import PuzzleInfo
|
||||
from chia.wallet.payment import Payment
|
||||
from chia.wallet.trade_record import TradeRecord
|
||||
from chia.wallet.trading.offer import Offer, NotarizedPayment
|
||||
@ -302,13 +303,13 @@ class TradeManager:
|
||||
elif wallet.type() == WalletType.CAT:
|
||||
key = bytes32(bytes.fromhex(wallet.get_asset_id()))
|
||||
memos = [p2_ph]
|
||||
if key in driver_dict and driver_dict[key].info["type"] != AssetType.CAT:
|
||||
if key in driver_dict and AssetType(driver_dict[key].type()) != AssetType.CAT:
|
||||
raise ValueError(
|
||||
f"driver_dict specified {driver_dict[key].info['type']},"
|
||||
f"driver_dict specified {AssetType(driver_dict[key].type())},"
|
||||
f"was expecting {AssetType.CAT}"
|
||||
)
|
||||
else:
|
||||
driver_dict[key] = PuzzleInfo({"type": AssetType.CAT, "tail": key})
|
||||
driver_dict[key] = PuzzleInfo({"type": AssetType.CAT.value, "tail": "0x" + key.hex()})
|
||||
else:
|
||||
raise ValueError(f"Offers are not implemented for {wallet.type()}")
|
||||
else:
|
||||
@ -327,13 +328,15 @@ class TradeManager:
|
||||
# ATTENTION: new wallets
|
||||
if wallet.type() == WalletType.CAT:
|
||||
asset_id = bytes32(bytes.fromhex(wallet.get_asset_id()))
|
||||
if asset_id in driver_dict and driver_dict[asset_id].info["type"] != AssetType.CAT:
|
||||
if asset_id in driver_dict and AssetType(driver_dict[asset_id].type()) != AssetType.CAT:
|
||||
raise ValueError(
|
||||
f"driver_dict specified {driver_dict[asset_id].info['type']},"
|
||||
f"driver_dict specified {AssetType(driver_dict[asset_id].type())},"
|
||||
f"was expecting {AssetType.CAT}"
|
||||
)
|
||||
else:
|
||||
driver_dict[asset_id] = PuzzleInfo({"type": AssetType.CAT, "tail": asset_id})
|
||||
driver_dict[asset_id] = PuzzleInfo(
|
||||
{"type": AssetType.CAT.value, "tail": "0x" + asset_id.hex()}
|
||||
)
|
||||
elif amount == 0:
|
||||
raise ValueError("You cannot offer nor request 0 amount of something")
|
||||
|
||||
|
@ -16,13 +16,12 @@ from chia.wallet.util.puzzle_compression import (
|
||||
lowest_best_version,
|
||||
)
|
||||
from chia.wallet.outer_puzzles import (
|
||||
PuzzleInfo,
|
||||
Solver,
|
||||
create_asset_id,
|
||||
construct_puzzle,
|
||||
match_puzzle,
|
||||
solve_puzzle,
|
||||
)
|
||||
from chia.wallet.puzzle_drivers import PuzzleInfo, Solver
|
||||
from chia.wallet.puzzles.load_clvm import load_clvm
|
||||
from chia.wallet.payment import Payment
|
||||
|
||||
@ -303,8 +302,11 @@ class Offer:
|
||||
self.driver_dict[asset_id],
|
||||
Solver(
|
||||
{
|
||||
"coin": coin,
|
||||
"parent_spend": parent_spend,
|
||||
"coin": "0x"
|
||||
+ coin.parent_coin_info.hex()
|
||||
+ coin.puzzle_hash.hex()
|
||||
+ bytes(coin.amount).hex(),
|
||||
"parent_spend": "0x" + bytes(parent_spend).hex(),
|
||||
}
|
||||
),
|
||||
OFFER_MOD,
|
||||
|
@ -37,7 +37,8 @@ from chia.wallet.cat_wallet.cat_constants import DEFAULT_CATS
|
||||
from chia.wallet.derivation_record import DerivationRecord
|
||||
from chia.wallet.derive_keys import master_sk_to_wallet_sk, master_sk_to_wallet_sk_unhardened
|
||||
from chia.wallet.key_val_store import KeyValStore
|
||||
from chia.wallet.outer_puzzles import AssetType, PuzzleInfo
|
||||
from chia.wallet.outer_puzzles import AssetType
|
||||
from chia.wallet.puzzle_drivers import PuzzleInfo
|
||||
from chia.wallet.puzzles.cat_loader import CAT_MOD
|
||||
from chia.wallet.rl_wallet.rl_wallet import RLWallet
|
||||
from chia.wallet.settings.user_settings import UserSettings
|
||||
@ -1149,9 +1150,9 @@ class WalletStateManager:
|
||||
return None
|
||||
|
||||
async def create_wallet_for_puzzle_info(self, puzzle_driver: PuzzleInfo, name=None, in_transaction=False):
|
||||
if puzzle_driver.info["type"] in self.asset_to_wallet_map:
|
||||
if AssetType(puzzle_driver.type()) in self.asset_to_wallet_map:
|
||||
async with self.lock:
|
||||
await self.asset_to_wallet_map[puzzle_driver.info["type"]].create_from_puzzle_info(
|
||||
await self.asset_to_wallet_map[AssetType(puzzle_driver.type())].create_from_puzzle_info(
|
||||
self,
|
||||
self.main_wallet,
|
||||
puzzle_driver,
|
||||
|
@ -17,7 +17,8 @@ from chia.wallet.cat_wallet.cat_utils import (
|
||||
SpendableCAT,
|
||||
unsigned_spend_bundle_for_spendable_cats,
|
||||
)
|
||||
from chia.wallet.outer_puzzles import AssetType, PuzzleInfo
|
||||
from chia.wallet.outer_puzzles import AssetType
|
||||
from chia.wallet.puzzle_drivers import PuzzleInfo
|
||||
from chia.wallet.payment import Payment
|
||||
from chia.wallet.trading.offer import Offer, NotarizedPayment
|
||||
from tests.clvm.benchmark_costs import cost_of_spend_bundle
|
||||
@ -180,8 +181,12 @@ class TestOfferLifecycle:
|
||||
blue_coins: List[Coin] = all_coins["blue"]
|
||||
|
||||
driver_dict: Dict[bytes32, PuzzleInfo] = {
|
||||
str_to_tail_hash("red"): PuzzleInfo({"type": AssetType.CAT, "tail": str_to_tail_hash("red")}),
|
||||
str_to_tail_hash("blue"): PuzzleInfo({"type": AssetType.CAT, "tail": str_to_tail_hash("blue")}),
|
||||
str_to_tail_hash("red"): PuzzleInfo(
|
||||
{"type": AssetType.CAT.value, "tail": "0x" + str_to_tail_hash("red").hex()}
|
||||
),
|
||||
str_to_tail_hash("blue"): PuzzleInfo(
|
||||
{"type": AssetType.CAT.value, "tail": "0x" + str_to_tail_hash("blue").hex()}
|
||||
),
|
||||
}
|
||||
|
||||
# Create an XCH Offer for RED
|
||||
|
Loading…
Reference in New Issue
Block a user