diff --git a/chia/full_node/mempool_check_conditions.py b/chia/full_node/mempool_check_conditions.py index ea15c0dae8f3..96e71b60fe69 100644 --- a/chia/full_node/mempool_check_conditions.py +++ b/chia/full_node/mempool_check_conditions.py @@ -3,6 +3,7 @@ from typing import Dict, List, Optional from clvm_rs import STRICT_MODE as MEMPOOL_MODE +from clvm.casts import int_from_bytes, int_to_bytes from chia.consensus.cost_calculator import NPCResult from chia.full_node.generator import create_generator_args, setup_generator_args from chia.types.blockchain_format.program import NIL @@ -10,12 +11,12 @@ from chia.types.condition_with_args import ConditionWithArgs from chia.types.generator_types import BlockGenerator from chia.types.name_puzzle_condition import NPC -from chia.util.clvm import int_from_bytes from chia.util.condition_tools import ConditionOpcode from chia.util.errors import Err from chia.util.ints import uint32, uint64, uint16 from chia.wallet.puzzles.generator_loader import GENERATOR_FOR_SINGLE_COIN_MOD from chia.wallet.puzzles.rom_bootstrap_generator import get_generator +from chia.consensus.cost_calculator import conditions_cost GENERATOR_MOD = get_generator() @@ -87,6 +88,26 @@ def mempool_assert_relative_time_exceeds( return None +def add_int_cond( + conds: Dict[ConditionOpcode, List[ConditionWithArgs]], + op: ConditionOpcode, + arg: int, +): + if op not in conds: + conds[op] = [] + conds[op].append(ConditionWithArgs(op, [int_to_bytes(arg)])) + + +def add_cond( + conds: Dict[ConditionOpcode, List[ConditionWithArgs]], + op: ConditionOpcode, + args: List[bytes], +): + if op not in conds: + conds[op] = [] + conds[op].append(ConditionWithArgs(op, args)) + + def get_name_puzzle_conditions( generator: BlockGenerator, max_cost: int, *, cost_per_byte: int, mempool_mode: bool ) -> NPCResult: @@ -97,20 +118,51 @@ def get_name_puzzle_conditions( flags = MEMPOOL_MODE if mempool_mode else 0 try: - err, result, clvm_cost = GENERATOR_MOD.run_as_generator(max_cost, flags, block_program, block_program_args) + err, result = GENERATOR_MOD.run_as_generator(max_cost, flags, block_program, block_program_args) + if err is not None: + assert err != 0 return NPCResult(uint16(err), [], uint64(0)) - else: - npc_list = [] - for r in result: - conditions = [] - for c in r.conditions: - cwa = [] - for cond_list in c[1]: - cwa.append(ConditionWithArgs(ConditionOpcode(bytes([cond_list.opcode])), cond_list.vars)) - conditions.append((ConditionOpcode(bytes([c[0]])), cwa)) - npc_list.append(NPC(r.coin_name, r.puzzle_hash, conditions)) - return NPCResult(None, npc_list, uint64(clvm_cost)) + + condition_cost = 0 + first = True + npc_list = [] + for r in result.spends: + conditions: Dict[ConditionOpcode, List[ConditionWithArgs]] = {} + if r.height_relative is not None: + add_int_cond(conditions, ConditionOpcode.ASSERT_HEIGHT_RELATIVE, r.height_relative) + if r.seconds_relative > 0: + add_int_cond(conditions, ConditionOpcode.ASSERT_SECONDS_RELATIVE, r.seconds_relative) + for cc in r.create_coin: + if cc[2] == b"": + add_cond(conditions, ConditionOpcode.CREATE_COIN, [cc[0], int_to_bytes(cc[1])]) + else: + add_cond(conditions, ConditionOpcode.CREATE_COIN, [cc[0], int_to_bytes(cc[1]), cc[2]]) + for sig in r.agg_sig_me: + add_cond(conditions, ConditionOpcode.AGG_SIG_ME, [sig[0], sig[1]]) + + # all conditions that aren't tied to a specific spent coin, we roll into the first one + if first: + first = False + if result.reserve_fee > 0: + add_int_cond(conditions, ConditionOpcode.RESERVE_FEE, result.reserve_fee) + if result.height_absolute > 0: + add_int_cond(conditions, ConditionOpcode.ASSERT_HEIGHT_ABSOLUTE, result.height_absolute) + if result.seconds_absolute > 0: + add_int_cond(conditions, ConditionOpcode.ASSERT_SECONDS_ABSOLUTE, result.seconds_absolute) + for sig in result.agg_sig_unsafe: + add_cond(conditions, ConditionOpcode.AGG_SIG_UNSAFE, [sig[0], sig[1]]) + + condition_cost += conditions_cost(conditions) + npc_list.append(NPC(r.coin_id, r.puzzle_hash, [(op, cond) for op, cond in conditions.items()])) + + # this is a temporary hack. The NPCResult clvm_cost field is not + # supposed to include conditions cost # but the result from run_generator2() does + # include that cost. So, until we change which cost we include in NPCResult, + # subtract the conditions cost. The pure CLVM cost is what will remain. + clvm_cost = result.cost - condition_cost + return NPCResult(None, npc_list, uint64(clvm_cost)) + except BaseException as e: log.debug(f"get_name_puzzle_condition failed: {e}") return NPCResult(uint16(Err.GENERATOR_RUNTIME_ERROR.value), [], uint64(0)) diff --git a/chia/types/blockchain_format/program.py b/chia/types/blockchain_format/program.py index 8ed71ada0228..1a1c0d5b41c4 100644 --- a/chia/types/blockchain_format/program.py +++ b/chia/types/blockchain_format/program.py @@ -1,17 +1,18 @@ import io from typing import List, Set, Tuple, Optional, Any -from clvm import KEYWORD_FROM_ATOM, KEYWORD_TO_ATOM, SExp +from clvm import SExp from clvm import run_program as default_run_program from clvm.casts import int_from_bytes from clvm.EvalError import EvalError -from clvm.operators import OP_REWRITE, OPERATOR_LOOKUP +from clvm.operators import OPERATOR_LOOKUP from clvm.serialize import sexp_from_stream, sexp_to_stream -from clvm_rs import STRICT_MODE as MEMPOOL_MODE, deserialize_and_run_program2, serialized_length, run_generator +from clvm_rs import STRICT_MODE as MEMPOOL_MODE, run_chia_program, serialized_length, run_generator2 from clvm_tools.curry import curry, uncurry from chia.types.blockchain_format.sized_bytes import bytes32 from chia.util.hash import std_hash +from chia.util.ints import uint16 from chia.util.byte_types import hexstr_to_bytes from .tree_hash import sha256_treehash @@ -243,7 +244,9 @@ def run_mempool_with_cost(self, max_cost: int, *args) -> Tuple[int, Program]: def run_with_cost(self, max_cost: int, *args) -> Tuple[int, Program]: return self._run(max_cost, 0, *args) - def run_as_generator(self, max_cost: int, flags: int, *args) -> Tuple[Optional[int], List[Any], int]: + # returns an optional error code and an optional PySpendBundleConditions (from clvm_rs) + # exactly one of those will hold a value + def run_as_generator(self, max_cost: int, flags: int, *args) -> Tuple[Optional[uint16], Optional[Any]]: serialized_args = b"" if len(args) > 1: # when we have more than one argument, serialize them into a list @@ -254,19 +257,12 @@ def run_as_generator(self, max_cost: int, flags: int, *args) -> Tuple[Optional[i else: serialized_args += _serialize(args[0]) - native_opcode_names_by_opcode = dict( - ("op_%s" % OP_REWRITE.get(k, k), op) for op, k in KEYWORD_FROM_ATOM.items() if k not in "qa." - ) - err, npc_list, cost = run_generator( + return run_generator2( self._buf, serialized_args, - KEYWORD_TO_ATOM["q"][0], - KEYWORD_TO_ATOM["a"][0], - native_opcode_names_by_opcode, max_cost, flags, ) - return None if err == 0 else err, npc_list, cost def _run(self, max_cost: int, flags, *args) -> Tuple[int, Program]: # when multiple arguments are passed, concatenate them into a serialized @@ -283,16 +279,9 @@ def _run(self, max_cost: int, flags, *args) -> Tuple[int, Program]: else: serialized_args += _serialize(args[0]) - # TODO: move this ugly magic into `clvm` "dialects" - native_opcode_names_by_opcode = dict( - ("op_%s" % OP_REWRITE.get(k, k), op) for op, k in KEYWORD_FROM_ATOM.items() if k not in "qa." - ) - cost, ret = deserialize_and_run_program2( + cost, ret = run_chia_program( self._buf, serialized_args, - KEYWORD_TO_ATOM["q"][0], - KEYWORD_TO_ATOM["a"][0], - native_opcode_names_by_opcode, max_cost, flags, ) diff --git a/setup.py b/setup.py index 1ac9718daed8..2d016a15cb48 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ "chiabip158==1.0", # bip158-style wallet filters "chiapos==1.0.7", # proof of space "clvm==0.9.7", - "clvm_rs==0.1.16", + "clvm_rs==0.1.17", "clvm_tools==0.4.3", "aiohttp==3.7.4", # HTTP server for full node rpc "aiosqlite==0.17.0", # asyncio wrapper for sqlite, to store blocks diff --git a/tests/core/full_node/test_mempool.py b/tests/core/full_node/test_mempool.py index c23898f22b1d..32795c775ea6 100644 --- a/tests/core/full_node/test_mempool.py +++ b/tests/core/full_node/test_mempool.py @@ -1872,7 +1872,7 @@ def test_create_coin_different_parent(self): assert c.conditions == [ ( opcode.value, - [ConditionWithArgs(opcode, [puzzle_hash.encode("ascii"), bytes([10]), b""])], + [ConditionWithArgs(opcode, [puzzle_hash.encode("ascii"), bytes([10])])], ) ] @@ -1886,11 +1886,11 @@ def test_create_coin_different_puzzhash(self): assert len(npc_result.npc_list) == 1 opcode = ConditionOpcode.CREATE_COIN assert ( - ConditionWithArgs(opcode, [puzzle_hash_1.encode("ascii"), bytes([5]), b""]) + ConditionWithArgs(opcode, [puzzle_hash_1.encode("ascii"), bytes([5])]) in npc_result.npc_list[0].conditions[0][1] ) assert ( - ConditionWithArgs(opcode, [puzzle_hash_2.encode("ascii"), bytes([5]), b""]) + ConditionWithArgs(opcode, [puzzle_hash_2.encode("ascii"), bytes([5])]) in npc_result.npc_list[0].conditions[0][1] ) @@ -1903,11 +1903,11 @@ def test_create_coin_different_amounts(self): assert len(npc_result.npc_list) == 1 opcode = ConditionOpcode.CREATE_COIN assert ( - ConditionWithArgs(opcode, [puzzle_hash.encode("ascii"), bytes([5]), b""]) + ConditionWithArgs(opcode, [puzzle_hash.encode("ascii"), bytes([5])]) in npc_result.npc_list[0].conditions[0][1] ) assert ( - ConditionWithArgs(opcode, [puzzle_hash.encode("ascii"), bytes([4]), b""]) + ConditionWithArgs(opcode, [puzzle_hash.encode("ascii"), bytes([4])]) in npc_result.npc_list[0].conditions[0][1] ) diff --git a/tests/generator/test_rom.py b/tests/generator/test_rom.py index 4a14fcd1d64c..ba31fde6c3b9 100644 --- a/tests/generator/test_rom.py +++ b/tests/generator/test_rom.py @@ -103,7 +103,7 @@ def test_get_name_puzzle_conditions(self): npc_result = get_name_puzzle_conditions(gen, max_cost=MAX_COST, cost_per_byte=COST_PER_BYTE, mempool_mode=False) assert npc_result.error is None assert npc_result.clvm_cost == EXPECTED_COST - cond_1 = ConditionWithArgs(ConditionOpcode.CREATE_COIN, [bytes([0] * 31 + [1]), int_to_bytes(500), b""]) + cond_1 = ConditionWithArgs(ConditionOpcode.CREATE_COIN, [bytes([0] * 31 + [1]), int_to_bytes(500)]) CONDITIONS = [ (ConditionOpcode.CREATE_COIN, [cond_1]), ]