diff --git a/benchmarks/mempool-long-lived.py b/benchmarks/mempool-long-lived.py index db89afb495e1..3a1275e15dd0 100644 --- a/benchmarks/mempool-long-lived.py +++ b/benchmarks/mempool-long-lived.py @@ -11,7 +11,7 @@ from chia.consensus.default_constants import DEFAULT_CONSTANTS from chia.full_node.mempool_manager import MempoolManager from chia.types.blockchain_format.coin import Coin -from chia.types.blockchain_format.program import Program +from chia.types.blockchain_format.serialized_program import SerializedProgram from chia.types.blockchain_format.sized_bytes import bytes32 from chia.types.coin_record import CoinRecord from chia.types.coin_spend import CoinSpend @@ -44,7 +44,7 @@ def is_transaction_block(self) -> bool: return self.timestamp is not None -IDENTITY_PUZZLE = Program.to(1) +IDENTITY_PUZZLE = SerializedProgram.to(1) IDENTITY_PUZZLE_HASH = IDENTITY_PUZZLE.get_tree_hash() @@ -62,7 +62,7 @@ def make_spend_bundle(coin: Coin, height: int) -> SpendBundle: int_to_bytes(coin.amount // 2 - height * 10), ], ] - spend = CoinSpend(coin, IDENTITY_PUZZLE, Program.to(conditions)) + spend = CoinSpend(coin, IDENTITY_PUZZLE, SerializedProgram.to(conditions)) return SpendBundle([spend], G2Element()) diff --git a/chia/_tests/core/data_layer/test_data_store.py b/chia/_tests/core/data_layer/test_data_store.py index 240a98896607..f670c895d03b 100644 --- a/chia/_tests/core/data_layer/test_data_store.py +++ b/chia/_tests/core/data_layer/test_data_store.py @@ -261,7 +261,7 @@ async def test_build_a_tree( example = await create_example(data_store, store_id) await _debug_dump(db=data_store.db_wrapper, description="final") - actual = await data_store.get_tree_as_program(store_id=store_id) + actual = await data_store.get_tree_as_nodes(store_id=store_id) # print("actual ", actual.as_python()) # print("expected", example.expected.as_python()) assert actual == example.expected @@ -747,30 +747,28 @@ async def test_autoinsert_balances_gaps(data_store: DataStore, store_id: bytes32 async def test_delete_from_left_both_terminal(data_store: DataStore, store_id: bytes32) -> None: await add_01234567_example(data_store=data_store, store_id=store_id) - expected = Program.to( - ( - ( - ( - (b"\x00", b"\x10\x00"), - (b"\x01", b"\x11\x01"), - ), - ( - (b"\x02", b"\x12\x02"), - (b"\x03", b"\x13\x03"), - ), + expected = InternalNode.from_child_nodes( + left=InternalNode.from_child_nodes( + left=InternalNode.from_child_nodes( + left=TerminalNode.from_key_value(key=b"\x00", value=b"\x10\x00"), + right=TerminalNode.from_key_value(key=b"\x01", value=b"\x11\x01"), ), - ( - (b"\x05", b"\x15\x05"), - ( - (b"\x06", b"\x16\x06"), - (b"\x07", b"\x17\x07"), - ), + right=InternalNode.from_child_nodes( + left=TerminalNode.from_key_value(key=b"\x02", value=b"\x12\x02"), + right=TerminalNode.from_key_value(key=b"\x03", value=b"\x13\x03"), + ), + ), + right=InternalNode.from_child_nodes( + left=TerminalNode.from_key_value(key=b"\x05", value=b"\x15\x05"), + right=InternalNode.from_child_nodes( + left=TerminalNode.from_key_value(key=b"\x06", value=b"\x16\x06"), + right=TerminalNode.from_key_value(key=b"\x07", value=b"\x17\x07"), ), ), ) await data_store.delete(key=b"\x04", store_id=store_id, status=Status.COMMITTED) - result = await data_store.get_tree_as_program(store_id=store_id) + result = await data_store.get_tree_as_nodes(store_id=store_id) assert result == expected @@ -779,28 +777,26 @@ async def test_delete_from_left_both_terminal(data_store: DataStore, store_id: b async def test_delete_from_left_other_not_terminal(data_store: DataStore, store_id: bytes32) -> None: await add_01234567_example(data_store=data_store, store_id=store_id) - expected = Program.to( - ( - ( - ( - (b"\x00", b"\x10\x00"), - (b"\x01", b"\x11\x01"), - ), - ( - (b"\x02", b"\x12\x02"), - (b"\x03", b"\x13\x03"), - ), + expected = InternalNode.from_child_nodes( + left=InternalNode.from_child_nodes( + left=InternalNode.from_child_nodes( + left=TerminalNode.from_key_value(key=b"\x00", value=b"\x10\x00"), + right=TerminalNode.from_key_value(key=b"\x01", value=b"\x11\x01"), ), - ( - (b"\x06", b"\x16\x06"), - (b"\x07", b"\x17\x07"), + right=InternalNode.from_child_nodes( + left=TerminalNode.from_key_value(key=b"\x02", value=b"\x12\x02"), + right=TerminalNode.from_key_value(key=b"\x03", value=b"\x13\x03"), ), ), + right=InternalNode.from_child_nodes( + left=TerminalNode.from_key_value(key=b"\x06", value=b"\x16\x06"), + right=TerminalNode.from_key_value(key=b"\x07", value=b"\x17\x07"), + ), ) await data_store.delete(key=b"\x04", store_id=store_id, status=Status.COMMITTED) await data_store.delete(key=b"\x05", store_id=store_id, status=Status.COMMITTED) - result = await data_store.get_tree_as_program(store_id=store_id) + result = await data_store.get_tree_as_nodes(store_id=store_id) assert result == expected @@ -809,30 +805,28 @@ async def test_delete_from_left_other_not_terminal(data_store: DataStore, store_ async def test_delete_from_right_both_terminal(data_store: DataStore, store_id: bytes32) -> None: await add_01234567_example(data_store=data_store, store_id=store_id) - expected = Program.to( - ( - ( - ( - (b"\x00", b"\x10\x00"), - (b"\x01", b"\x11\x01"), - ), - (b"\x02", b"\x12\x02"), + expected = InternalNode.from_child_nodes( + left=InternalNode.from_child_nodes( + left=InternalNode.from_child_nodes( + left=TerminalNode.from_key_value(key=b"\x00", value=b"\x10\x00"), + right=TerminalNode.from_key_value(key=b"\x01", value=b"\x11\x01"), ), - ( - ( - (b"\x04", b"\x14\x04"), - (b"\x05", b"\x15\x05"), - ), - ( - (b"\x06", b"\x16\x06"), - (b"\x07", b"\x17\x07"), - ), + right=TerminalNode.from_key_value(key=b"\x02", value=b"\x12\x02"), + ), + right=InternalNode.from_child_nodes( + left=InternalNode.from_child_nodes( + left=TerminalNode.from_key_value(key=b"\x04", value=b"\x14\x04"), + right=TerminalNode.from_key_value(key=b"\x05", value=b"\x15\x05"), + ), + right=InternalNode.from_child_nodes( + left=TerminalNode.from_key_value(key=b"\x06", value=b"\x16\x06"), + right=TerminalNode.from_key_value(key=b"\x07", value=b"\x17\x07"), ), ), ) await data_store.delete(key=b"\x03", store_id=store_id, status=Status.COMMITTED) - result = await data_store.get_tree_as_program(store_id=store_id) + result = await data_store.get_tree_as_nodes(store_id=store_id) assert result == expected @@ -841,28 +835,26 @@ async def test_delete_from_right_both_terminal(data_store: DataStore, store_id: async def test_delete_from_right_other_not_terminal(data_store: DataStore, store_id: bytes32) -> None: await add_01234567_example(data_store=data_store, store_id=store_id) - expected = Program.to( - ( - ( - (b"\x00", b"\x10\x00"), - (b"\x01", b"\x11\x01"), + expected = InternalNode.from_child_nodes( + left=InternalNode.from_child_nodes( + left=TerminalNode.from_key_value(key=b"\x00", value=b"\x10\x00"), + right=TerminalNode.from_key_value(key=b"\x01", value=b"\x11\x01"), + ), + right=InternalNode.from_child_nodes( + left=InternalNode.from_child_nodes( + left=TerminalNode.from_key_value(key=b"\x04", value=b"\x14\x04"), + right=TerminalNode.from_key_value(key=b"\x05", value=b"\x15\x05"), ), - ( - ( - (b"\x04", b"\x14\x04"), - (b"\x05", b"\x15\x05"), - ), - ( - (b"\x06", b"\x16\x06"), - (b"\x07", b"\x17\x07"), - ), + right=InternalNode.from_child_nodes( + left=TerminalNode.from_key_value(key=b"\x06", value=b"\x16\x06"), + right=TerminalNode.from_key_value(key=b"\x07", value=b"\x17\x07"), ), ), ) await data_store.delete(key=b"\x03", store_id=store_id, status=Status.COMMITTED) await data_store.delete(key=b"\x02", store_id=store_id, status=Status.COMMITTED) - result = await data_store.get_tree_as_program(store_id=store_id) + result = await data_store.get_tree_as_nodes(store_id=store_id) assert result == expected diff --git a/chia/_tests/core/data_layer/util.py b/chia/_tests/core/data_layer/util.py index a83fbebc5e91..0532582bd946 100644 --- a/chia/_tests/core/data_layer/util.py +++ b/chia/_tests/core/data_layer/util.py @@ -8,7 +8,7 @@ from dataclasses import dataclass from typing import IO, TYPE_CHECKING, Any, Dict, Iterator, List, Literal, Optional, Union, overload -from chia.data_layer.data_layer_util import NodeType, Side, Status +from chia.data_layer.data_layer_util import InternalNode, Node, NodeType, Side, Status, TerminalNode from chia.data_layer.data_store import DataStore from chia.types.blockchain_format.program import Program from chia.types.blockchain_format.sized_bytes import bytes32 @@ -47,21 +47,19 @@ async def general_insert( @dataclass(frozen=True) class Example: - expected: Program + expected: Node terminal_nodes: List[bytes32] async def add_0123_example(data_store: DataStore, store_id: bytes32) -> Example: - expected = Program.to( - ( - ( - (b"\x00", b"\x10\x00"), - (b"\x01", b"\x11\x01"), - ), - ( - (b"\x02", b"\x12\x02"), - (b"\x03", b"\x13\x03"), - ), + expected = InternalNode.from_child_nodes( + left=InternalNode.from_child_nodes( + left=TerminalNode.from_key_value(key=b"\x00", value=b"\x10\x00"), + right=TerminalNode.from_key_value(key=b"\x01", value=b"\x11\x01"), + ), + right=InternalNode.from_child_nodes( + left=TerminalNode.from_key_value(key=b"\x02", value=b"\x12\x02"), + right=TerminalNode.from_key_value(key=b"\x03", value=b"\x13\x03"), ), ) @@ -76,27 +74,25 @@ async def add_0123_example(data_store: DataStore, store_id: bytes32) -> Example: async def add_01234567_example(data_store: DataStore, store_id: bytes32) -> Example: - expected = Program.to( - ( - ( - ( - (b"\x00", b"\x10\x00"), - (b"\x01", b"\x11\x01"), - ), - ( - (b"\x02", b"\x12\x02"), - (b"\x03", b"\x13\x03"), - ), + expected = InternalNode.from_child_nodes( + left=InternalNode.from_child_nodes( + InternalNode.from_child_nodes( + left=TerminalNode.from_key_value(key=b"\x00", value=b"\x10\x00"), + right=TerminalNode.from_key_value(key=b"\x01", value=b"\x11\x01"), + ), + InternalNode.from_child_nodes( + left=TerminalNode.from_key_value(key=b"\x02", value=b"\x12\x02"), + right=TerminalNode.from_key_value(key=b"\x03", value=b"\x13\x03"), + ), + ), + right=InternalNode.from_child_nodes( + InternalNode.from_child_nodes( + left=TerminalNode.from_key_value(key=b"\x04", value=b"\x14\x04"), + right=TerminalNode.from_key_value(key=b"\x05", value=b"\x15\x05"), ), - ( - ( - (b"\x04", b"\x14\x04"), - (b"\x05", b"\x15\x05"), - ), - ( - (b"\x06", b"\x16\x06"), - (b"\x07", b"\x17\x07"), - ), + InternalNode.from_child_nodes( + left=TerminalNode.from_key_value(key=b"\x06", value=b"\x16\x06"), + right=TerminalNode.from_key_value(key=b"\x07", value=b"\x17\x07"), ), ), ) diff --git a/chia/_tests/generator/test_compression.py b/chia/_tests/generator/test_compression.py index a6b0ba6449c0..884741a5d87e 100644 --- a/chia/_tests/generator/test_compression.py +++ b/chia/_tests/generator/test_compression.py @@ -7,8 +7,8 @@ import pytest from chia_rs import ALLOW_BACKREFS -from clvm import SExp from clvm.serialize import sexp_from_stream +from clvm.SExp import SExp from clvm_tools import binutils from chia._tests.core.make_block_generator import make_spend_bundle diff --git a/chia/_tests/generator/test_rom.py b/chia/_tests/generator/test_rom.py index a4ab6333d514..232db48332b3 100644 --- a/chia/_tests/generator/test_rom.py +++ b/chia/_tests/generator/test_rom.py @@ -2,6 +2,7 @@ from typing import List, Tuple +from clvm.CLVMObject import CLVMStorage from clvm_tools import binutils from clvm_tools.clvmc import compile_clvm_text @@ -84,7 +85,7 @@ def run_generator(self: BlockGenerator) -> Tuple[int, Program]: return GENERATOR_MOD.run_with_cost(MAX_COST, [self.program, args]) -def as_atom_list(prg: Program) -> List[bytes]: +def as_atom_list(prg: CLVMStorage) -> List[bytes]: """ Pretend `prg` is a list of atoms. Return the corresponding python list of atoms. diff --git a/chia/_tests/wallet/cat_wallet/test_offer_lifecycle.py b/chia/_tests/wallet/cat_wallet/test_offer_lifecycle.py index 4fbffdf4915c..8e8f26204c89 100644 --- a/chia/_tests/wallet/cat_wallet/test_offer_lifecycle.py +++ b/chia/_tests/wallet/cat_wallet/test_offer_lifecycle.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Any, Dict, List, Optional, cast +from typing import Any, Dict, List, Optional import pytest from chia_rs import G2Element @@ -32,8 +32,7 @@ # Some methods mapping strings to CATs def str_to_tail(tail_str: str) -> Program: - # TODO: Remove cast when we improve typing - return cast(Program, Program.to([3, [], [1, tail_str], []])) + return Program.to([3, [], [1, tail_str], []]) def str_to_tail_hash(tail_str: str) -> bytes32: @@ -118,10 +117,10 @@ def generate_secure_bundle( offered_amount: uint64, tail_str: Optional[str] = None, ) -> SpendBundle: - announcement_assertions = [a.to_program() for a in announcements] + announcement_assertions: List[Program] = [a.to_program() for a in announcements] selected_coin_amount = sum([c.amount for c in selected_coins]) non_primaries = [] if len(selected_coins) < 2 else selected_coins[1:] - inner_solution: List[List[Any]] = [ + inner_solution: List[Any] = [ [51, Offer.ph(), offered_amount], # Offered coin [51, acs_ph, uint64(selected_coin_amount - offered_amount)], # Change *announcement_assertions, diff --git a/chia/_tests/wallet/dao_wallet/test_dao_clvm.py b/chia/_tests/wallet/dao_wallet/test_dao_clvm.py index af0e8f56ee89..7840d143681c 100644 --- a/chia/_tests/wallet/dao_wallet/test_dao_clvm.py +++ b/chia/_tests/wallet/dao_wallet/test_dao_clvm.py @@ -316,11 +316,11 @@ def test_proposal() -> None: treasury_inner, ) treasury_puzhash = treasury.get_tree_hash() - apa_msg = singleton_id + apa_msg_bytes = singleton_id timer_apa = std_hash(timer_puzhash + singleton_id) assert conds[ConditionOpcode.ASSERT_PUZZLE_ANNOUNCEMENT][0].vars[0] == timer_apa - assert conds[ConditionOpcode.ASSERT_PUZZLE_ANNOUNCEMENT][1].vars[0] == std_hash(treasury_puzhash + apa_msg) + assert conds[ConditionOpcode.ASSERT_PUZZLE_ANNOUNCEMENT][1].vars[0] == std_hash(treasury_puzhash + apa_msg_bytes) # close a failed proposal full_proposal = proposal_curry_one.curry( @@ -347,8 +347,8 @@ def test_proposal() -> None: ] ) conds = conditions_dict_for_solution(full_proposal, solution, INFINITE_COST) - apa_msg = int_to_bytes(0) - assert conds[ConditionOpcode.ASSERT_PUZZLE_ANNOUNCEMENT][1].vars[0] == std_hash(treasury_puzhash + apa_msg) + apa_msg_int = int_to_bytes(0) + assert conds[ConditionOpcode.ASSERT_PUZZLE_ANNOUNCEMENT][1].vars[0] == std_hash(treasury_puzhash + apa_msg_int) assert conds[ConditionOpcode.ASSERT_PUZZLE_ANNOUNCEMENT][0].vars[0] == timer_apa finished_puz = DAO_FINISHED_STATE.curry(singleton_struct, DAO_FINISHED_STATE_HASH) @@ -373,7 +373,7 @@ def test_proposal() -> None: ] ) conds = conditions_dict_for_solution(full_proposal, solution, INFINITE_COST) - assert conds[ConditionOpcode.ASSERT_PUZZLE_ANNOUNCEMENT][0].vars[0] == std_hash(treasury_puzhash + apa_msg) + assert conds[ConditionOpcode.ASSERT_PUZZLE_ANNOUNCEMENT][0].vars[0] == std_hash(treasury_puzhash + apa_msg_int) assert conds[ConditionOpcode.CREATE_COIN][0].vars[0] == finished_puz.get_tree_hash() diff --git a/chia/_tests/wallet/nft_wallet/test_ownership_outer_puzzle.py b/chia/_tests/wallet/nft_wallet/test_ownership_outer_puzzle.py index a4091853aa25..067a0b5cdad1 100644 --- a/chia/_tests/wallet/nft_wallet/test_ownership_outer_puzzle.py +++ b/chia/_tests/wallet/nft_wallet/test_ownership_outer_puzzle.py @@ -27,8 +27,8 @@ def test_ownership_outer_puzzle() -> None: """ ) transfer_program_default: Program = puzzle_for_transfer_program(bytes32([1] * 32), bytes32([2] * 32), uint16(5000)) - ownership_puzzle: Program = puzzle_for_ownership_layer(owner, transfer_program, ACS) - ownership_puzzle_empty: Program = puzzle_for_ownership_layer(NIL, transfer_program, ACS) + ownership_puzzle: Program = puzzle_for_ownership_layer(owner, Program.to(transfer_program), ACS) + ownership_puzzle_empty: Program = puzzle_for_ownership_layer(NIL, Program.to(transfer_program), ACS) ownership_puzzle_default: Program = puzzle_for_ownership_layer(owner, transfer_program_default, ACS) ownership_driver: Optional[PuzzleInfo] = match_puzzle(uncurry_puzzle(ownership_puzzle)) ownership_driver_empty: Optional[PuzzleInfo] = match_puzzle(uncurry_puzzle(ownership_puzzle_empty)) diff --git a/chia/_tests/wallet/test_signer_protocol.py b/chia/_tests/wallet/test_signer_protocol.py index 9b293c5f2aa7..df0f2ff28849 100644 --- a/chia/_tests/wallet/test_signer_protocol.py +++ b/chia/_tests/wallet/test_signer_protocol.py @@ -144,7 +144,7 @@ async def test_p2dohp_wallet_signer_protocol(wallet_environments: WalletTestFram # Now test that we can partially sign a transaction ACS: Program = Program.to(1) - ACS_PH: Program = Program.to(1).get_tree_hash() + ACS_PH = Program.to(1).get_tree_hash() not_our_private_key: PrivateKey = PrivateKey.from_bytes(bytes([1] * 32)) not_our_pubkey: G1Element = not_our_private_key.get_g1() not_our_message: bytes = b"not our message" diff --git a/chia/_tests/wallet/test_singleton_lifecycle_fast.py b/chia/_tests/wallet/test_singleton_lifecycle_fast.py index 685778e5b1c7..99274e66b328 100644 --- a/chia/_tests/wallet/test_singleton_lifecycle_fast.py +++ b/chia/_tests/wallet/test_singleton_lifecycle_fast.py @@ -102,8 +102,7 @@ def solve_launcher(solver: Solver, puzzle_db: PuzzleDB, args: List[Program], kwa destination_puzzle_hash = from_kwargs(kwargs, "destination_puzzle_hash", bytes32) metadata = from_kwargs(kwargs, "metadata", List[Tuple[str, str]]) solution = Program.to([destination_puzzle_hash, launcher_amount, metadata]) - # TODO: Remove cast when we improve typing - return cast(Program, solution) + return solution def solve_anyone_can_spend(solver: Solver, puzzle_db: PuzzleDB, args: List[Program], kwargs: Dict[str, Any]) -> Program: @@ -113,8 +112,7 @@ def solve_anyone_can_spend(solver: Solver, puzzle_db: PuzzleDB, args: List[Progr """ conditions = from_kwargs(kwargs, "conditions", List[Program]) solution = Program.to(conditions) - # TODO: Remove cast when we improve typing - return cast(Program, solution) + return solution def solve_anyone_can_spend_with_padding( @@ -123,8 +121,7 @@ def solve_anyone_can_spend_with_padding( """This is the puzzle `(a (q . 1) 3)`. It's only for testing.""" conditions = from_kwargs(kwargs, "conditions", List[Program]) solution = Program.to((0, conditions)) - # TODO: Remove cast when we improve typing - return cast(Program, solution) + return solution def solve_singleton(solver: Solver, puzzle_db: PuzzleDB, args: List[Program], kwargs: Dict[str, Any]) -> Program: @@ -137,8 +134,7 @@ def solve_singleton(solver: Solver, puzzle_db: PuzzleDB, args: List[Program], kw lineage_proof = from_kwargs(kwargs, "lineage_proof", Program) coin_amount = from_kwargs(kwargs, "coin_amount", uint64) solution = inner_solution.to([lineage_proof, coin_amount, inner_solution.rest()]) - # TODO: Remove cast when we improve typing - return cast(Program, solution) + return solution def solve_pool_member(solver: Solver, puzzle_db: PuzzleDB, args: List[Program], kwargs: Dict[str, Any]) -> Program: @@ -149,14 +145,12 @@ def solve_pool_member(solver: Solver, puzzle_db: PuzzleDB, args: List[Program], to_waiting_room = pool_member_spend_type == "to-waiting-room" if to_waiting_room: key_value_list = from_kwargs(kwargs, "key_value_list", Program) - # TODO: Remove cast when we improve typing - return cast(Program, Program.to([0, 1, 0, 0, key_value_list])) + return Program.to([0, 1, 0, 0, key_value_list]) # it's an "absorb_pool_reward" type pool_reward_amount = from_kwargs(kwargs, "pool_reward_amount", uint64) pool_reward_height = from_kwargs(kwargs, "pool_reward_height", int) solution = Program.to([0, pool_reward_amount, pool_reward_height]) - # TODO: Remove cast when we improve typing - return cast(Program, solution) + return solution def solve_pool_waiting_room( @@ -170,14 +164,12 @@ def solve_pool_waiting_room( if exit_waiting_room: key_value_list = from_kwargs(kwargs, "key_value_list", List[Tuple[str, str]]) destination_puzzle_hash = from_kwargs(kwargs, "destination_puzzle_hash", bytes32) - # TODO: Remove cast when we improve typing - return cast(Program, Program.to([0, 1, key_value_list, destination_puzzle_hash])) + return Program.to([0, 1, key_value_list, destination_puzzle_hash]) # it's an "absorb_pool_reward" type pool_reward_amount = from_kwargs(kwargs, "pool_reward_amount", uint64) pool_reward_height = from_kwargs(kwargs, "pool_reward_height", int) solution = Program.to([0, 0, pool_reward_amount, pool_reward_height]) - # TODO: Remove cast when we improve typing - return cast(Program, solution) + return solution def solve_p2_singleton(solver: Solver, puzzle_db: PuzzleDB, args: List[Program], kwargs: Dict[str, Any]) -> Program: @@ -190,8 +182,7 @@ def solve_p2_singleton(solver: Solver, puzzle_db: PuzzleDB, args: List[Program], singleton_inner_puzzle_hash = from_kwargs(kwargs, "singleton_inner_puzzle_hash", bytes32) p2_singleton_coin_name = from_kwargs(kwargs, "p2_singleton_coin_name", bytes) solution = Program.to([singleton_inner_puzzle_hash, p2_singleton_coin_name]) - # TODO: Remove cast when we improve typing - return cast(Program, solution) + return solution raise ValueError("can't solve `delayed-spend` yet") @@ -228,8 +219,7 @@ def inner_puzzle_for_puzzle(self, puzzle: Program) -> Program: template, args = puzzle.uncurry() assert bytes(template) == bytes(SINGLETON_MOD) _, inner_puzzle = list(args.as_iter()) - # TODO: Remove cast when we improve typing - return cast(Program, inner_puzzle) + return inner_puzzle def coin_spend_for_conditions(self, puzzle_db: PuzzleDB, **kwargs: object) -> CoinSpend: coin = self.current_state @@ -274,8 +264,7 @@ def adaptor_for_singleton_inner_puzzle(puzzle: Program) -> Program: """ # this is pretty slow and lame program = binutils.assemble("(a (q . %s) 3)" % binutils.disassemble(puzzle)) - # TODO: Remove cast when we improve typing - return cast(Program, Program.to(program)) + return Program.to(program) def launcher_conditions_and_spend_bundle( @@ -323,8 +312,7 @@ def singleton_puzzle_hash(launcher_id: bytes32, launcher_puzzle_hash: bytes32, i def solution_for_singleton_puzzle(lineage_proof: Program, my_amount: int, inner_solution: Program) -> Program: - # TODO: Remove cast when we improve typing - return cast(Program, Program.to([lineage_proof, my_amount, inner_solution])) + return Program.to([lineage_proof, my_amount, inner_solution]) def p2_singleton_puzzle_for_launcher( @@ -391,16 +379,14 @@ def lineage_proof_for_coin_spend(coin_spend: CoinSpend) -> Program: inner_puzzle_hash = None if coin.puzzle_hash == LAUNCHER_PUZZLE_HASH: - # TODO: Remove cast when we improve typing - return cast(Program, Program.to([parent_name, amount])) + return Program.to([parent_name, amount]) full_puzzle = Program.from_bytes(bytes(coin_spend.puzzle_reveal)) _, args = full_puzzle.uncurry() _, __, ___, inner_puzzle = list(args.as_iter()) inner_puzzle_hash = inner_puzzle.get_tree_hash() - # TODO: Remove cast when we improve typing - return cast(Program, Program.to([parent_name, inner_puzzle_hash, amount])) + return Program.to([parent_name, inner_puzzle_hash, amount]) def create_throwaway_pubkey(seed: bytes) -> G1Element: diff --git a/chia/data_layer/data_layer_util.py b/chia/data_layer/data_layer_util.py index 026236626a92..4dfccc39d10c 100644 --- a/chia/data_layer/data_layer_util.py +++ b/chia/data_layer/data_layer_util.py @@ -26,10 +26,7 @@ def internal_hash(left_hash: bytes32, right_hash: bytes32) -> bytes32: - # ignoring hint error here for: - # https://github.com/Chia-Network/clvm/pull/102 - # https://github.com/Chia-Network/clvm/pull/106 - return Program.to((left_hash, right_hash)).get_tree_hash_precalc(left_hash, right_hash) # type: ignore[no-any-return] # noqa: E501 + return Program.to((left_hash, right_hash)).get_tree_hash_precalc(left_hash, right_hash) def calculate_internal_hash(hash: bytes32, other_hash_side: Side, other_hash: bytes32) -> bytes32: @@ -176,6 +173,7 @@ class CommitState(IntEnum): Node = Union["TerminalNode", "InternalNode"] +@final @dataclass(frozen=True) class TerminalNode: hash: bytes32 @@ -183,11 +181,16 @@ class TerminalNode: key: bytes value: bytes + # left for now for interface back-compat even though it is constant atom: None = field(init=False, default=None) - @property - def pair(self) -> Tuple[bytes32, bytes32]: - return Program.to(self.key), Program.to(self.value) + @classmethod + def from_key_value(cls, key: bytes, value: bytes) -> TerminalNode: + return cls( + hash=leaf_hash(key=key, value=value), + key=key, + value=value, + ) @classmethod def from_row(cls, row: aiosqlite.Row) -> TerminalNode: @@ -252,9 +255,7 @@ def sibling_hashes(self) -> List[bytes32]: return [layer.other_hash for layer in self.layers] def as_program(self) -> Program: - # https://github.com/Chia-Network/clvm/pull/102 - # https://github.com/Chia-Network/clvm/pull/106 - return Program.to([self.sibling_sides_integer(), self.sibling_hashes()]) # type: ignore[no-any-return] + return Program.to([self.sibling_sides_integer(), self.sibling_hashes()]) def valid(self) -> bool: existing_hash = self.node_hash @@ -275,6 +276,7 @@ def valid(self) -> bool: return True +@final @dataclass(frozen=True) class InternalNode: hash: bytes32 @@ -282,8 +284,18 @@ class InternalNode: left_hash: bytes32 right_hash: bytes32 - pair: Optional[Tuple[Node, Node]] = None - atom: None = None + left: Optional[Node] = None + right: Optional[Node] = None + + @classmethod + def from_child_nodes(cls, left: Node, right: Node) -> InternalNode: + return cls( + hash=internal_hash(left_hash=left.hash, right_hash=right.hash), + left_hash=left.hash, + right_hash=right.hash, + left=left, + right=right, + ) @classmethod def from_row(cls, row: aiosqlite.Row) -> InternalNode: diff --git a/chia/data_layer/data_store.py b/chia/data_layer/data_store.py index 16abc3669bd4..f5e53eabaddc 100644 --- a/chia/data_layer/data_store.py +++ b/chia/data_layer/data_store.py @@ -1733,7 +1733,7 @@ async def get_node(self, node_hash: bytes32) -> Node: node = row_to_node(row=row) return node - async def get_tree_as_program(self, store_id: bytes32) -> Program: + async def get_tree_as_nodes(self, store_id: bytes32) -> Node: async with self.db_wrapper.reader() as reader: root = await self.get_tree_root(store_id=store_id) # TODO: consider actual proper behavior @@ -1757,16 +1757,12 @@ async def get_tree_as_program(self, store_id: bytes32) -> Program: hash_to_node: Dict[bytes32, Node] = {} for node in reversed(nodes): if isinstance(node, InternalNode): - node = replace(node, pair=(hash_to_node[node.left_hash], hash_to_node[node.right_hash])) + node = replace(node, left=hash_to_node[node.left_hash], right=hash_to_node[node.right_hash]) hash_to_node[node.hash] = node root_node = hash_to_node[root_node.hash] - # TODO: Remove ignore when done. - # https://github.com/Chia-Network/clvm/pull/102 - # https://github.com/Chia-Network/clvm/pull/106 - program: Program = Program.to(root_node) - return program + return root_node async def get_proof_of_inclusion_by_hash( self, diff --git a/chia/pools/pool_puzzles.py b/chia/pools/pool_puzzles.py index ebc0cbe62a6f..7421cd635399 100644 --- a/chia/pools/pool_puzzles.py +++ b/chia/pools/pool_puzzles.py @@ -129,7 +129,8 @@ def get_delayed_puz_info_from_launcher_spend(coinsol: CoinSpend) -> Tuple[uint64 if key.atom == b"t": seconds = uint64(value.as_int()) if key.atom == b"h": - delayed_puzzle_hash = bytes32(value.as_atom()) + assert value.atom is not None + delayed_puzzle_hash = bytes32(value.atom) assert seconds is not None assert delayed_puzzle_hash is not None return seconds, delayed_puzzle_hash @@ -377,10 +378,7 @@ def get_inner_puzzle_from_puzzle(full_puzzle: Program) -> Optional[Program]: _, inner_puzzle = list(args.as_iter()) if not is_pool_singleton_inner_puzzle(inner_puzzle): return None - # ignoring hint error here for: - # https://github.com/Chia-Network/clvm/pull/102 - # https://github.com/Chia-Network/clvm/pull/106 - return inner_puzzle # type: ignore[no-any-return] + return inner_puzzle def pool_state_from_extra_data(extra_data: Program) -> Optional[PoolState]: diff --git a/chia/rpc/wallet_rpc_api.py b/chia/rpc/wallet_rpc_api.py index 1f33d7143899..6d9159c6b76b 100644 --- a/chia/rpc/wallet_rpc_api.py +++ b/chia/rpc/wallet_rpc_api.py @@ -2247,7 +2247,7 @@ async def did_get_info(self, request: Dict[str, Any]) -> EndpointResult: if curried_args is None: return {"success": False, "error": "The coin is not a DID."} p2_puzzle, recovery_list_hash, num_verification, singleton_struct, metadata = curried_args - launcher_id = singleton_struct.rest().first().as_atom() + launcher_id = bytes32(singleton_struct.rest().first().as_atom()) uncurried_p2 = uncurry_puzzle(p2_puzzle) (public_key,) = uncurried_p2.args.as_iter() memos = compute_memos(SpendBundle([coin_spend], G2Element())) @@ -2258,7 +2258,7 @@ async def did_get_info(self, request: Dict[str, Any]) -> EndpointResult: hints.append(memo.hex()) return { "success": True, - "did_id": encode_puzzle_hash(bytes32(launcher_id), AddressType.DID.hrp(self.service.config)), + "did_id": encode_puzzle_hash(launcher_id, AddressType.DID.hrp(self.service.config)), "latest_coin": coin_state.coin.name().hex(), "p2_address": encode_puzzle_hash(p2_puzzle.get_tree_hash(), AddressType.XCH.hrp(self.service.config)), "public_key": public_key.as_atom().hex(), diff --git a/chia/types/blockchain_format/program.py b/chia/types/blockchain_format/program.py index ab49f47fa999..864936a16fc1 100644 --- a/chia/types/blockchain_format/program.py +++ b/chia/types/blockchain_format/program.py @@ -1,13 +1,14 @@ from __future__ import annotations import io -from typing import Any, Callable, Dict, Optional, Set, Tuple, Type +from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, Set, Tuple, Type, TypeVar from chia_rs import ALLOW_BACKREFS, run_chia_program, tree_hash -from clvm import SExp from clvm.casts import int_from_bytes +from clvm.CLVMObject import CLVMStorage from clvm.EvalError import EvalError from clvm.serialize import sexp_from_stream, sexp_to_stream +from clvm.SExp import SExp from chia.types.blockchain_format.sized_bytes import bytes32 from chia.util.byte_types import hexstr_to_bytes @@ -18,20 +19,24 @@ INFINITE_COST = 11000000000 +T_CLVMStorage = TypeVar("T_CLVMStorage", bound=CLVMStorage) +T_Program = TypeVar("T_Program", bound="Program") + + class Program(SExp): """ A thin wrapper around s-expression data intended to be invoked with "eval". """ @classmethod - def parse(cls, f) -> Program: + def parse(cls: Type[T_Program], f) -> T_Program: return sexp_from_stream(f, cls.to) def stream(self, f): sexp_to_stream(self, f) @classmethod - def from_bytes(cls, blob: bytes) -> Program: + def from_bytes(cls: Type[T_Program], blob: bytes) -> T_Program: # this runs the program "1", which just returns the first argument. # the first argument is the buffer we want to parse. This effectively # leverages the rust parser and LazyNode, making it a lot faster to @@ -42,10 +47,10 @@ def from_bytes(cls, blob: bytes) -> Program: 50, ALLOW_BACKREFS, ) - return Program.to(ret) + return cls.to(ret) @classmethod - def fromhex(cls, hexstr: str) -> Program: + def fromhex(cls: Type[T_Program], hexstr: str) -> T_Program: return cls.from_bytes(hexstr_to_bytes(hexstr)) @classmethod @@ -85,7 +90,7 @@ def at(self, position: str) -> Program: raise ValueError(f"`at` got illegal character `{c}`. Only `f` & `r` allowed") return v - def replace(self, **kwargs) -> Program: + def replace(self: T_Program, **kwargs: Any) -> T_Program: """ Create a new program replacing the given paths (using `at` syntax). Example: @@ -118,15 +123,15 @@ def get_tree_hash_precalc(self, *args: bytes32) -> bytes32: def get_tree_hash(self) -> bytes32: return bytes32(tree_hash(bytes(self))) - def _run(self, max_cost: int, flags: int, args: object) -> Tuple[int, Program]: + def _run(self, max_cost: int, flags: int, args: Any) -> Tuple[int, Program]: prog_args = Program.to(args) cost, r = run_chia_program(self.as_bin(), prog_args.as_bin(), max_cost, flags) return cost, Program.to(r) - def run_with_cost(self, max_cost: int, args: object) -> Tuple[int, Program]: + def run_with_cost(self, max_cost: int, args: Any) -> Tuple[int, Program]: return self._run(max_cost, 0, args) - def run(self, args: object) -> Program: + def run(self, args: Any) -> Program: cost, r = self.run_with_cost(INFINITE_COST, args) return r @@ -153,7 +158,7 @@ def curry(self, *args) -> Program: return Program.to([2, (1, self), fixed_args]) def uncurry(self) -> Tuple[Program, Program]: - def match(o: SExp, expected: bytes) -> None: + def match(o: CLVMStorage, expected: bytes) -> None: if o.atom != expected: raise ValueError(f"expected: {expected.hex()}") @@ -161,6 +166,9 @@ def match(o: SExp, expected: bytes) -> None: # (2 (1 . ) ) ev, quoted_inner, args_list = self.as_iter() match(ev, b"\x02") + if TYPE_CHECKING: + # this being False is presently handled in the TypeError exception handler below + assert quoted_inner.pair is not None match(quoted_inner.pair[0], b"\x01") mod = quoted_inner.pair[1] args = [] @@ -168,6 +176,9 @@ def match(o: SExp, expected: bytes) -> None: # (4 (1 . ) ) cons, quoted_arg, rest = args_list.as_iter() match(cons, b"\x04") + if TYPE_CHECKING: + # this being False is presently handled in the TypeError exception handler below + assert quoted_arg.pair is not None match(quoted_arg.pair[0], b"\x01") args.append(quoted_arg.pair[1]) args_list = rest @@ -208,7 +219,8 @@ def _tree_hash(node: SExp, precalculated: Set[bytes32]) -> bytes32: right = _tree_hash(node.rest(), precalculated) s = b"\2" + left + right else: - atom = node.as_atom() + # node.listp() is False so must be an atom + atom: bytes = node.as_atom() # type: ignore[assignment] if atom in precalculated: return bytes32(atom) s = b"\1" + atom @@ -218,10 +230,12 @@ def _tree_hash(node: SExp, precalculated: Set[bytes32]) -> bytes32: NIL = Program.from_bytes(b"\x80") -def _sexp_replace(sexp: SExp, to_sexp: Callable[[Any], SExp], **kwargs) -> SExp: +# real return type is more like Union[T_Program, CastableType] when considering corner and terminal cases +def _sexp_replace(sexp: T_CLVMStorage, to_sexp: Callable[[Any], T_Program], **kwargs: Any) -> T_Program: # if `kwargs == {}` then `return sexp` unchanged if len(kwargs) == 0: - return sexp + # yes, the terminal case is hinted incorrectly for now + return sexp # type: ignore[return-value] if "" in kwargs: if len(kwargs) > 1: @@ -232,7 +246,7 @@ def _sexp_replace(sexp: SExp, to_sexp: Callable[[Any], SExp], **kwargs) -> SExp: # Now split `kwargs` into two groups: those # that start with `f` and those that start with `r` - args_by_prefix: Dict[str, SExp] = {} + args_by_prefix: Dict[str, Dict[str, Any]] = {} for k, v in kwargs.items(): c = k[0] if c not in "fr": diff --git a/chia/types/blockchain_format/tree_hash.py b/chia/types/blockchain_format/tree_hash.py index 1e59e9b7c23e..120462a6bc9e 100644 --- a/chia/types/blockchain_format/tree_hash.py +++ b/chia/types/blockchain_format/tree_hash.py @@ -8,17 +8,21 @@ from __future__ import annotations -from typing import Callable, List, Optional, Set +from typing import Callable, List, Optional, Set, Union -from clvm import CLVMObject +from clvm.CLVMObject import CLVMStorage +from clvm.SExp import SExp from chia.types.blockchain_format.sized_bytes import bytes32 from chia.util.hash import std_hash -Op = Callable[[List["CLVMObject"], List["Op"], Set[bytes32]], None] +ValueType = Union[bytes, CLVMStorage] +ValueStackType = List[ValueType] +Op = Callable[[ValueStackType, "OpStackType", Set[bytes32]], None] +OpStackType = List[Op] -def sha256_treehash(sexp: CLVMObject, precalculated: Optional[Set[bytes32]] = None) -> bytes32: +def sha256_treehash(sexp: CLVMStorage, precalculated: Optional[Set[bytes32]] = None) -> bytes32: """ Hash values in `precalculated` are presumed to have been hashed already. """ @@ -26,8 +30,9 @@ def sha256_treehash(sexp: CLVMObject, precalculated: Optional[Set[bytes32]] = No if precalculated is None: precalculated = set() - def handle_sexp(sexp_stack: List[CLVMObject], op_stack: List[Op], precalculated: Set[bytes32]) -> None: - sexp = sexp_stack.pop() + def handle_sexp(sexp_stack: ValueStackType, op_stack: OpStackType, precalculated: Set[bytes32]) -> None: + # just trusting it is right, otherwise we get an attribute error + sexp: SExp = sexp_stack.pop() # type: ignore[assignment] if sexp.pair: p0, p1 = sexp.pair sexp_stack.append(p0) @@ -37,26 +42,31 @@ def handle_sexp(sexp_stack: List[CLVMObject], op_stack: List[Op], precalculated: op_stack.append(roll) op_stack.append(handle_sexp) else: - if sexp.atom in precalculated: - r = sexp.atom + # not a pair, so an atom + atom: bytes = sexp.atom # type: ignore[assignment] + if atom in precalculated: + r = atom else: - r = std_hash(b"\1" + sexp.atom) + r = std_hash(b"\1" + atom) sexp_stack.append(r) - def handle_pair(sexp_stack: List[CLVMObject], op_stack: List[Op], precalculated: Set[bytes32]) -> None: - p0 = sexp_stack.pop() - p1 = sexp_stack.pop() + def handle_pair(sexp_stack: ValueStackType, op_stack: OpStackType, precalculated: Set[bytes32]) -> None: + # just trusting it is right, otherwise we get a type error + p0: bytes = sexp_stack.pop() # type: ignore[assignment] + p1: bytes = sexp_stack.pop() # type: ignore[assignment] sexp_stack.append(std_hash(b"\2" + p0 + p1)) - def roll(sexp_stack: List[CLVMObject], op_stack: List[Op], precalculated: Set[bytes32]) -> None: + def roll(sexp_stack: ValueStackType, op_stack: OpStackType, precalculated: Set[bytes32]) -> None: p0 = sexp_stack.pop() p1 = sexp_stack.pop() sexp_stack.append(p0) sexp_stack.append(p1) - sexp_stack = [sexp] + sexp_stack: ValueStackType = [sexp] op_stack: List[Op] = [handle_sexp] while len(op_stack) > 0: op = op_stack.pop() op(sexp_stack, op_stack, precalculated) - return bytes32(sexp_stack[0]) + # just trusting it is right, otherwise we get some error, probably + result: bytes = sexp_stack[0] # type: ignore[assignment] + return bytes32(result) diff --git a/chia/types/coin_spend.py b/chia/types/coin_spend.py index 17668c1fb3d9..7adecc6876ce 100644 --- a/chia/types/coin_spend.py +++ b/chia/types/coin_spend.py @@ -76,7 +76,7 @@ def compute_additions_with_cost( continue cost += ConditionCost.CREATE_COIN.value puzzle_hash = next(atoms).as_atom() - amount = next(atoms).as_int() + amount = uint64(next(atoms).as_int()) ret.append(Coin(parent_id, puzzle_hash, uint64(amount))) return ret, cost diff --git a/chia/wallet/cat_wallet/cat_outer_puzzle.py b/chia/wallet/cat_wallet/cat_outer_puzzle.py index d90161a02546..6e111286ae51 100644 --- a/chia/wallet/cat_wallet/cat_outer_puzzle.py +++ b/chia/wallet/cat_wallet/cat_outer_puzzle.py @@ -1,7 +1,7 @@ from __future__ import annotations from dataclasses import dataclass -from typing import Callable, List, Optional +from typing import Any, Callable, Dict, List, Optional from chia.types.blockchain_format.coin import Coin from chia.types.blockchain_format.program import Program @@ -33,9 +33,9 @@ def match(self, puzzle: UncurriedPuzzle) -> Optional[PuzzleInfo]: if args is None: return None _, tail_hash, inner_puzzle = args - constructor_dict = { + constructor_dict: Dict[str, Any] = { "type": "CAT", - "tail": "0x" + tail_hash.atom.hex(), + "tail": "0x" + tail_hash.as_atom().hex(), } next_constructor = self._match(uncurry_puzzle(inner_puzzle)) if next_constructor is not None: diff --git a/chia/wallet/cat_wallet/cat_utils.py b/chia/wallet/cat_wallet/cat_utils.py index e8cf4101e8f9..484dd3289437 100644 --- a/chia/wallet/cat_wallet/cat_utils.py +++ b/chia/wallet/cat_wallet/cat_utils.py @@ -25,10 +25,7 @@ def empty_program() -> Program: - # ignoring hint error here for: - # https://github.com/Chia-Network/clvm/pull/102 - # https://github.com/Chia-Network/clvm/pull/106 - return Program.to([]) # type: ignore[no-any-return] + return Program.to([]) # information needed to spend a cc @@ -102,9 +99,7 @@ def subtotals_for_deltas(deltas: List[int]) -> List[int]: def next_info_for_spendable_cat(spendable_cat: SpendableCAT) -> Program: c = spendable_cat.coin list = [c.parent_coin_info, spendable_cat.inner_puzzle.get_tree_hash(), c.amount] - # ignoring hint error here for: - # https://github.com/Chia-Network/clvm/pull/102 - return Program.to(list) # type: ignore[no-any-return] + return Program.to(list) # This should probably return UnsignedSpendBundle if that type ever exists diff --git a/chia/wallet/conditions.py b/chia/wallet/conditions.py index 3197b25fc153..635ec22c50b4 100644 --- a/chia/wallet/conditions.py +++ b/chia/wallet/conditions.py @@ -2,7 +2,7 @@ from abc import ABC, abstractmethod from dataclasses import dataclass, fields, replace -from typing import Any, Dict, Iterable, List, Optional, Set, Tuple, Type, TypeVar, Union, cast, final, get_type_hints +from typing import Any, Dict, Iterable, List, Optional, Set, Tuple, Type, TypeVar, Union, final, get_type_hints from chia_rs import G1Element from clvm.casts import int_from_bytes, int_to_bytes @@ -775,8 +775,7 @@ class UnknownCondition(Condition): args: List[Program] def to_program(self) -> Program: - # TODO: Remove cast when we have proper hinting for this - return cast(Program, self.opcode.cons(Program.to(self.args))) + return self.opcode.cons(Program.to(self.args)) @classmethod def from_program(cls, program: Program) -> UnknownCondition: diff --git a/chia/wallet/dao_wallet/dao_utils.py b/chia/wallet/dao_wallet/dao_utils.py index efe188783f57..214a0c5180ed 100644 --- a/chia/wallet/dao_wallet/dao_utils.py +++ b/chia/wallet/dao_wallet/dao_utils.py @@ -652,7 +652,7 @@ def match_treasury_puzzle(mod: Program, curried_args: Program) -> Optional[Itera if mod == SINGLETON_MOD: mod, curried_args = curried_args.rest().first().uncurry() if mod == DAO_TREASURY_MOD: - return curried_args.first().as_iter() # type: ignore[no-any-return] + return curried_args.first().as_iter() except ValueError: # pragma: no cover # We just pass here to prevent spamming logs with error messages when WSM checks incoming coins pass @@ -688,7 +688,7 @@ def match_finished_puzzle(mod: Program, curried_args: Program) -> Optional[Itera if mod == SINGLETON_MOD: mod, curried_args = curried_args.rest().first().uncurry() if mod == DAO_FINISHED_STATE: - return curried_args.as_iter() # type: ignore[no-any-return] + return curried_args.as_iter() except ValueError: # pragma: no cover # We just pass here to prevent spamming logs with error messages when WSM checks incoming coins pass diff --git a/chia/wallet/dao_wallet/dao_wallet.py b/chia/wallet/dao_wallet/dao_wallet.py index 6e226b9b5759..fd246c5a28e3 100644 --- a/chia/wallet/dao_wallet/dao_wallet.py +++ b/chia/wallet/dao_wallet/dao_wallet.py @@ -1278,7 +1278,7 @@ async def create_proposal_close_spend( if cond.rest().first().as_atom() == cat_launcher.get_tree_hash(): cat_wallet: CATWallet = self.wallet_state_manager.wallets[self.dao_info.cat_wallet_id] cat_tail_hash = cat_wallet.cat_info.limitations_program_hash - mint_amount = cond.rest().rest().first().as_int() + mint_amount = uint64(cond.rest().rest().first().as_int()) new_cat_puzhash = bytes32(cond.rest().rest().rest().first().first().as_atom()) eve_puzzle = curry_cat_eve(new_cat_puzhash) if genesis_id is None: diff --git a/chia/wallet/did_wallet/did_wallet.py b/chia/wallet/did_wallet/did_wallet.py index 290039582c61..bcf418da7fde 100644 --- a/chia/wallet/did_wallet/did_wallet.py +++ b/chia/wallet/did_wallet/did_wallet.py @@ -377,7 +377,7 @@ async def coin_added(self, coin: Coin, _: uint32, peer: WSChiaConnection, parent p2_puzzle, recovery_list_hash, num_verification, singleton_struct, metadata = did_curried_args did_data = DIDCoinData( p2_puzzle=p2_puzzle, - recovery_list_hash=recovery_list_hash.atom, + recovery_list_hash=bytes32(recovery_list_hash.as_atom()), num_verification=uint16(num_verification.as_int()), singleton_struct=singleton_struct, metadata=metadata, diff --git a/chia/wallet/did_wallet/did_wallet_puzzles.py b/chia/wallet/did_wallet/did_wallet_puzzles.py index 19e9a6c2ea53..2308cdf5cb85 100644 --- a/chia/wallet/did_wallet/did_wallet_puzzles.py +++ b/chia/wallet/did_wallet/did_wallet_puzzles.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Dict, Iterator, List, Optional, Tuple, Union, cast +from typing import Dict, Iterator, List, Optional, Tuple, Union from chia_rs import G1Element @@ -44,7 +44,7 @@ def create_innerpuz( num_of_backup_ids_needed: uint64, launcher_id: bytes32, metadata: Program = Program.to([]), - recovery_list_hash: Optional[bytes32] = None, + recovery_list_hash: Optional[Program] = None, ) -> Program: """ Create DID inner puzzle @@ -58,7 +58,7 @@ def create_innerpuz( Note: Receiving a standard P2 puzzle hash wouldn't calculate a valid puzzle, but that can be useful if calling `.get_tree_hash_precalc()` on it. """ - backup_ids_hash = Program.to(recovery_list).get_tree_hash() + backup_ids_hash: Union[Program, bytes32] = 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))) @@ -145,8 +145,7 @@ def create_recovery_message_puzzle(recovering_coin_id: bytes32, newpuz: bytes32, ], ) ) - # TODO: Remove cast when we have proper hinting for this - return cast(Program, puzzle) + return puzzle def create_spend_for_message( @@ -176,8 +175,7 @@ def match_did_puzzle(mod: Program, curried_args: Program) -> Optional[Iterator[P if mod == SINGLETON_TOP_LAYER_MOD: mod, curried_args = curried_args.rest().first().uncurry() if mod == DID_INNERPUZ_MOD: - # TODO: Remove cast when we have clvm type hinting for this - return cast(Iterator[Program], curried_args.as_iter()) + return curried_args.as_iter() except Exception: import traceback @@ -207,8 +205,7 @@ def metadata_to_program(metadata: Dict[str, str]) -> Program: kv_list = [] for key, value in metadata.items(): kv_list.append((key, value)) - # TODO: Remove cast when we have proper hinting for this - return cast(Program, Program.to(kv_list)) + return Program.to(kv_list) def did_program_to_metadata(program: Program) -> Dict[str, str]: diff --git a/chia/wallet/lineage_proof.py b/chia/wallet/lineage_proof.py index 95c8a55f4210..59a3477315ec 100644 --- a/chia/wallet/lineage_proof.py +++ b/chia/wallet/lineage_proof.py @@ -2,7 +2,7 @@ from dataclasses import dataclass from enum import Enum -from typing import Any, Dict, List, Optional, cast +from typing import Any, Dict, List, Optional from chia.types.blockchain_format.program import Program from chia.types.blockchain_format.sized_bytes import bytes32 @@ -52,8 +52,7 @@ def to_program(self) -> Program: final_list.append(self.inner_puzzle_hash) if self.amount is not None: final_list.append(self.amount) - # TODO: Remove cast when we improve typing - return cast(Program, Program.to(final_list)) + return Program.to(final_list) def is_none(self) -> bool: return all([self.parent_name is None, self.inner_puzzle_hash is None, self.amount is None]) diff --git a/chia/wallet/nft_wallet/metadata_outer_puzzle.py b/chia/wallet/nft_wallet/metadata_outer_puzzle.py index 9accc8993fc2..88037e7dd993 100644 --- a/chia/wallet/nft_wallet/metadata_outer_puzzle.py +++ b/chia/wallet/nft_wallet/metadata_outer_puzzle.py @@ -28,7 +28,7 @@ def puzzle_for_metadata_layer(metadata: Program, updater_hash: bytes32, inner_pu def solution_for_metadata_layer(amount: uint64, inner_solution: Program) -> Program: - return Program.to([inner_solution, amount]) # type: ignore + return Program.to([inner_solution, amount]) @dataclass(frozen=True) diff --git a/chia/wallet/nft_wallet/nft_puzzles.py b/chia/wallet/nft_wallet/nft_puzzles.py index 111114d4bc10..4acdbac52cbf 100644 --- a/chia/wallet/nft_wallet/nft_puzzles.py +++ b/chia/wallet/nft_wallet/nft_puzzles.py @@ -1,9 +1,8 @@ from __future__ import annotations import logging -from typing import Any, Dict, List, Optional, Tuple, cast +from typing import Any, Dict, List, Literal, Optional, Tuple, Union -from clvm.casts import int_from_bytes from clvm_tools.binutils import disassemble from chia.types.blockchain_format.program import Program @@ -237,8 +236,7 @@ def create_ownership_layer_transfer_solution( log.debug("Condition list raw: %r", condition_list) solution = Program.to([[solution_for_conditions(condition_list)]]) log.debug("Generated transfer solution: %s", solution) - # TODO: Remove cast when we improve typing - return cast(Program, solution) + return solution def get_metadata_and_phs(unft: UncurriedNFT, solution: SerializedProgram) -> Tuple[Program, bytes32]: @@ -255,14 +253,17 @@ def get_metadata_and_phs(unft: UncurriedNFT, solution: SerializedProgram) -> Tup # metadata update metadata = update_metadata(metadata, condition) metadata = Program.to(metadata) - elif condition_code == 51 and int_from_bytes(condition.rest().rest().first().atom) == 1: - # destination puzhash - if puzhash_for_derivation is not None: - # ignore duplicated create coin conditions - continue - memo = bytes32(condition.as_python()[-1][0]) - puzhash_for_derivation = memo - log.debug("Got back puzhash from solution: %s", puzhash_for_derivation) + elif condition_code == 51: + atom = condition.rest().rest().first().as_int() + + if atom == 1: + # destination puzhash + if puzhash_for_derivation is not None: + # ignore duplicated create coin conditions + continue + memo = bytes32(condition.at("rrrff").as_atom()) + puzhash_for_derivation = memo + log.debug("Got back puzhash from solution: %s", puzhash_for_derivation) assert puzhash_for_derivation return metadata, puzhash_for_derivation @@ -275,22 +276,30 @@ def recurry_nft_puzzle(unft: UncurriedNFT, solution: Program, new_inner_puzzle: for condition in conditions.as_iter(): if condition.first().as_int() == -10: # this is the change owner magic condition - new_did_id = condition.at("rf").atom + atom = condition.at("rf").atom + if atom is None or atom == b"": + new_did_id = None + else: + new_did_id = bytes32(atom) elif condition.first().as_int() == 51: new_puzhash = condition.at("rf").atom # assert new_puzhash and new_did_id - log.debug(f"Found NFT puzzle details: {new_did_id} {new_puzhash}") + log.debug(f"Found NFT puzzle details: {new_did_id!r} {new_puzhash!r}") assert unft.transfer_program new_ownership_puzzle = construct_ownership_layer(new_did_id, unft.transfer_program, new_inner_puzzle) return new_ownership_puzzle -def get_new_owner_did(unft: UncurriedNFT, solution: Program) -> Optional[bytes32]: +def get_new_owner_did(unft: UncurriedNFT, solution: Program) -> Union[None, Literal[b""], bytes32]: conditions = unft.p2_puzzle.run(unft.get_innermost_solution(solution)) - new_did_id = None + new_did_id: Union[None, Literal[b""], bytes32] = None for condition in conditions.as_iter(): if condition.first().as_int() == -10: # this is the change owner magic condition - new_did_id = condition.at("rf").atom + atom = condition.at("rf").as_atom() + if atom == b"": + new_did_id = b"" + else: + new_did_id = bytes32(atom) return new_did_id diff --git a/chia/wallet/nft_wallet/nft_wallet.py b/chia/wallet/nft_wallet/nft_wallet.py index 8683dabb39a8..d0daebba3e30 100644 --- a/chia/wallet/nft_wallet/nft_wallet.py +++ b/chia/wallet/nft_wallet/nft_wallet.py @@ -219,7 +219,7 @@ async def puzzle_solution_received(self, coin: Coin, data: NFTCoinData, peer: WS child_puzzle: Program = nft_puzzles.create_full_puzzle( singleton_id, Program.to(metadata), - bytes32(uncurried_nft.metadata_updater_hash.atom), + bytes32(uncurried_nft.metadata_updater_hash.as_atom()), inner_puzzle, ) self.log.debug( diff --git a/chia/wallet/nft_wallet/ownership_outer_puzzle.py b/chia/wallet/nft_wallet/ownership_outer_puzzle.py index 9ae16dbc866c..b320e16e30ab 100644 --- a/chia/wallet/nft_wallet/ownership_outer_puzzle.py +++ b/chia/wallet/nft_wallet/ownership_outer_puzzle.py @@ -29,7 +29,7 @@ def puzzle_for_ownership_layer( def solution_for_ownership_layer(inner_solution: Program) -> Program: - return Program.to([inner_solution]) # type: ignore + return Program.to([inner_solution]) @dataclass(frozen=True) diff --git a/chia/wallet/nft_wallet/singleton_outer_puzzle.py b/chia/wallet/nft_wallet/singleton_outer_puzzle.py index dd20083e3db1..1188caba9f18 100644 --- a/chia/wallet/nft_wallet/singleton_outer_puzzle.py +++ b/chia/wallet/nft_wallet/singleton_outer_puzzle.py @@ -1,7 +1,7 @@ from __future__ import annotations from dataclasses import dataclass -from typing import Callable, Optional +from typing import Any, Callable, Dict, Optional from chia.types.blockchain_format.coin import Coin from chia.types.blockchain_format.program import Program @@ -31,10 +31,15 @@ def match(self, puzzle: UncurriedPuzzle) -> Optional[PuzzleInfo]: matched, curried_args = match_singleton_puzzle(puzzle) if matched: singleton_struct, inner_puzzle = curried_args - launcher_struct = singleton_struct.pair[1].pair + pair = singleton_struct.pair + assert pair is not None + launcher_struct = pair[1].pair + assert launcher_struct is not None launcher_id = launcher_struct[0].atom + assert launcher_id is not None launcher_ph = launcher_struct[1].atom - constructor_dict = { + assert launcher_ph is not None + constructor_dict: Dict[str, Any] = { "type": "singleton", "launcher_id": "0x" + launcher_id.hex(), "launcher_ph": "0x" + launcher_ph.hex(), diff --git a/chia/wallet/nft_wallet/transfer_program_puzzle.py b/chia/wallet/nft_wallet/transfer_program_puzzle.py index 2576d07d7867..7764a1833381 100644 --- a/chia/wallet/nft_wallet/transfer_program_puzzle.py +++ b/chia/wallet/nft_wallet/transfer_program_puzzle.py @@ -39,7 +39,7 @@ def solution_for_transfer_program( new_did_inner_hash: bytes32, trade_prices_list: Program, ) -> Program: - return Program.to([conditions, current_owner, [new_did, trade_prices_list, new_did_inner_hash]]) # type: ignore + return Program.to([conditions, current_owner, [new_did, trade_prices_list, new_did_inner_hash]]) @dataclass(frozen=True) @@ -79,4 +79,4 @@ def get_inner_solution(self, constructor: PuzzleInfo, solution: Program) -> Opti return None def solve(self, constructor: PuzzleInfo, solver: Solver, inner_puzzle: Program, inner_solution: Program) -> Program: - return Program.to(None) # type: ignore + return Program.to(None) diff --git a/chia/wallet/nft_wallet/uncurry_nft.py b/chia/wallet/nft_wallet/uncurry_nft.py index 591e7ddd6253..facb1ed3caab 100644 --- a/chia/wallet/nft_wallet/uncurry_nft.py +++ b/chia/wallet/nft_wallet/uncurry_nft.py @@ -144,11 +144,11 @@ def uncurry(cls: Type[_T_UncurriedNFT], mod: Program, curried_args: Program) -> edition_number = kv_pair.rest() if kv_pair.first().as_atom() == b"st": edition_total = kv_pair.rest() - current_did = None + current_did: Optional[bytes32] = None transfer_program = None transfer_program_args = None - royalty_address = None - royalty_percentage = None + royalty_address: Optional[bytes32] = None + royalty_percentage: Optional[uint16] = None nft_inner_puzzle_mod = None mod, ol_args = inner_puzzle.uncurry() supports_did = False @@ -204,7 +204,7 @@ def uncurry(cls: Type[_T_UncurriedNFT], mod: Program, curried_args: Program) -> def get_innermost_solution(self, solution: Program) -> Program: state_layer_inner_solution: Program = solution.at("rrff") if self.supports_did: - return state_layer_inner_solution.first() # type: ignore + return state_layer_inner_solution.first() else: return state_layer_inner_solution diff --git a/chia/wallet/puzzles/clawback/drivers.py b/chia/wallet/puzzles/clawback/drivers.py index 896f0b06b7b3..d6c234f83a9c 100644 --- a/chia/wallet/puzzles/clawback/drivers.py +++ b/chia/wallet/puzzles/clawback/drivers.py @@ -129,8 +129,16 @@ def match_clawback_puzzle( # Check if the inner puzzle is a P2 puzzle if MOD != uncurried.mod: return None + if not isinstance(inner_puzzle, SerializedProgram): + inner_puzzle = SerializedProgram.from_program(inner_puzzle) + if not isinstance(inner_solution, SerializedProgram): + inner_solution = SerializedProgram.from_program(inner_solution) # Fetch Remark condition - conditions = conditions_for_solution(inner_puzzle, inner_solution, DEFAULT_CONSTANTS.MAX_BLOCK_COST_CLVM // 8) + conditions = conditions_for_solution( + inner_puzzle, + inner_solution, + DEFAULT_CONSTANTS.MAX_BLOCK_COST_CLVM // 8, + ) metadata: Optional[ClawbackMetadata] = None new_puzhash: Set[bytes32] = set() if conditions is not None: diff --git a/chia/wallet/puzzles/p2_delegated_conditions.py b/chia/wallet/puzzles/p2_delegated_conditions.py index 7f81903975db..1ef2c50669df 100644 --- a/chia/wallet/puzzles/p2_delegated_conditions.py +++ b/chia/wallet/puzzles/p2_delegated_conditions.py @@ -7,8 +7,6 @@ from __future__ import annotations -from typing import cast - from chia.types.blockchain_format.program import Program from .load_clvm import load_clvm_maybe_recompile @@ -21,5 +19,4 @@ def puzzle_for_pk(public_key: Program) -> Program: def solution_for_conditions(conditions: Program) -> Program: - # TODO: Remove cast when we improve typing - return cast(Program, conditions.to([conditions])) + return conditions.to([conditions]) diff --git a/chia/wallet/puzzles/p2_puzzle_hash.py b/chia/wallet/puzzles/p2_puzzle_hash.py index fa4b08024d94..a540ccaec6d2 100644 --- a/chia/wallet/puzzles/p2_puzzle_hash.py +++ b/chia/wallet/puzzles/p2_puzzle_hash.py @@ -7,8 +7,6 @@ from __future__ import annotations -from typing import cast - from chia.types.blockchain_format.program import Program from chia.types.blockchain_format.sized_bytes import bytes32 @@ -27,5 +25,4 @@ def puzzle_for_inner_puzzle(inner_puzzle: Program) -> Program: def solution_for_inner_puzzle_and_inner_solution(inner_puzzle: Program, inner_puzzle_solution: Program) -> Program: - # TODO: Remove cast when we improve typing - return cast(Program, Program.to([inner_puzzle, inner_puzzle_solution])) + return Program.to([inner_puzzle, inner_puzzle_solution]) diff --git a/chia/wallet/puzzles/singleton_top_layer.py b/chia/wallet/puzzles/singleton_top_layer.py index cd08b496543d..596d6fa92284 100644 --- a/chia/wallet/puzzles/singleton_top_layer.py +++ b/chia/wallet/puzzles/singleton_top_layer.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Iterator, List, Optional, Tuple, cast +from typing import Iterator, List, Optional, Tuple from chia.types.blockchain_format.coin import Coin from chia.types.blockchain_format.program import Program @@ -175,8 +175,7 @@ def generate_launcher_coin(coin: Coin, amount: uint64) -> Coin: # Wrap inner puzzles that are not singleton specific to strip away "truths" def adapt_inner_to_singleton(inner_puzzle: Program) -> Program: # (a (q . inner_puzzle) (r 1)) - # TODO: Remove cast when we improve typing - return cast(Program, Program.to([2, (1, inner_puzzle), [6, 1]])) + return Program.to([2, (1, inner_puzzle), [6, 1]]) def adapt_inner_puzzle_hash_to_singleton(inner_puzzle_hash: bytes32) -> bytes32: @@ -240,8 +239,7 @@ def solution_for_singleton(lineage_proof: LineageProof, amount: uint64, inner_so parent_info = [lineage_proof.parent_name, lineage_proof.amount] else: parent_info = [lineage_proof.parent_name, lineage_proof.inner_puzzle_hash, lineage_proof.amount] - # TODO: Remove cast when we improve typing - return cast(Program, Program.to([parent_info, amount, inner_solution])) + return Program.to([parent_info, amount, inner_solution]) # Create a coin that a singleton can claim @@ -258,14 +256,12 @@ def pay_to_singleton_or_delay_puzzle(launcher_id: bytes32, delay_time: uint64, d # Solution for EITHER p2_singleton or the claiming spend case for p2_singleton_or_delayed_puzhash def solution_for_p2_singleton(p2_singleton_coin: Coin, singleton_inner_puzhash: bytes32) -> Program: - # TODO: Remove cast when we improve typing - return cast(Program, Program.to([singleton_inner_puzhash, p2_singleton_coin.name()])) + return Program.to([singleton_inner_puzhash, p2_singleton_coin.name()]) # Solution for the delayed spend case for p2_singleton_or_delayed_puzhash def solution_for_p2_delayed_puzzle(output_amount: uint64) -> Program: - # TODO: Remove cast when we improve typing - return cast(Program, Program.to([output_amount, []])) + return Program.to([output_amount, []]) # Get announcement conditions for singleton solution and full CoinSpend for the claimed coin diff --git a/chia/wallet/util/compute_hints.py b/chia/wallet/util/compute_hints.py index 6962c60589b7..de982e8aac57 100644 --- a/chia/wallet/util/compute_hints.py +++ b/chia/wallet/util/compute_hints.py @@ -49,7 +49,10 @@ def compute_spend_hints_and_additions( continue cost += ConditionCost.CREATE_COIN.value - coin: Coin = Coin(cs.coin.name(), bytes32(condition.at("rf").atom), uint64(condition.at("rrf").as_int())) + rf = condition.at("rf").atom + assert rf is not None + + coin: Coin = Coin(cs.coin.name(), bytes32(rf), uint64(condition.at("rrf").as_int())) hint: Optional[bytes32] = None if ( condition.at("rrr") != Program.to(None) # There's more than two arguments diff --git a/chia/wallet/util/debug_spend_bundle.py b/chia/wallet/util/debug_spend_bundle.py index 9183387c2b77..9a9cdc3e5bb9 100644 --- a/chia/wallet/util/debug_spend_bundle.py +++ b/chia/wallet/util/debug_spend_bundle.py @@ -3,7 +3,7 @@ from typing import List from chia_rs import AugSchemeMPL -from clvm import KEYWORD_FROM_ATOM +from clvm.operators import KEYWORD_FROM_ATOM from clvm_tools.binutils import disassemble as bu_disassemble from chia.consensus.default_constants import DEFAULT_CONSTANTS diff --git a/chia/wallet/wallet.py b/chia/wallet/wallet.py index ec47f55db2aa..270cbf892a7e 100644 --- a/chia/wallet/wallet.py +++ b/chia/wallet/wallet.py @@ -223,7 +223,7 @@ def make_solution( def add_condition_to_solution(self, condition: Program, solution: Program) -> Program: python_program = solution.as_python() python_program[1].append(condition) - return cast(Program, Program.to(python_program)) + return Program.to(python_program) async def select_coins( self, diff --git a/chia/wallet/wallet_state_manager.py b/chia/wallet/wallet_state_manager.py index 035d7c3d3233..1c5fc9137aa3 100644 --- a/chia/wallet/wallet_state_manager.py +++ b/chia/wallet/wallet_state_manager.py @@ -1448,7 +1448,7 @@ async def handle_nft( """ wallet_identifier = None # DID ID determines which NFT wallet should process the NFT - new_did_id = None + new_did_id: Optional[bytes32] = None old_did_id = None # P2 puzzle hash determines if we should ignore the NFT uncurried_nft: UncurriedNFT = nft_data.uncurried_nft @@ -1458,12 +1458,14 @@ async def handle_nft( nft_data.parent_coin_spend.solution, ) if uncurried_nft.supports_did: - new_did_id = get_new_owner_did(uncurried_nft, nft_data.parent_coin_spend.solution.to_program()) + _new_did_id = get_new_owner_did(uncurried_nft, nft_data.parent_coin_spend.solution.to_program()) old_did_id = uncurried_nft.owner_did - if new_did_id is None: + if _new_did_id is None: new_did_id = old_did_id - if new_did_id == b"": + elif _new_did_id == b"": new_did_id = None + else: + new_did_id = _new_did_id self.log.debug( "Handling NFT: %s, old DID:%s, new DID:%s, old P2:%s, new P2:%s", nft_data.parent_coin_spend, diff --git a/setup.py b/setup.py index 3d7e4cabdc18..77dd7e0ad7fe 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ "chiavdf==1.1.4", # timelord and vdf verification "chiabip158==1.5.1", # bip158-style wallet filters "chiapos==2.0.4", # proof of space - "clvm==0.9.9", + "clvm==0.9.10", "clvm_tools==0.4.9", # Currying, Program.to, other conveniences "chia_rs==0.8.0", "clvm-tools-rs==0.1.40", # Rust implementation of clvm_tools' compiler