def get_name_puzzle_conditions_rust(generator: BlockGenerator, max_cost: int, *, cost_per_byte: int, safe_mode: bool) -> NPCResult: block_program, block_program_args = setup_generator_args(generator) max_cost -= len(bytes(generator.program)) * cost_per_byte if max_cost < 0: return NPCResult(uint16(Err.INVALID_BLOCK_COST.value), [], uint64(0)) flags = STRICT_MODE if safe_mode else 0 err, result, clvm_cost = GENERATOR_MOD.run_as_generator( max_cost, flags, block_program, block_program_args) if err is not None: 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))
def test_get_name_puzzle_conditions(self): # this tests that extra block or coin data doesn't confuse `get_name_puzzle_conditions` gen = block_generator() cost, r = run_generator(gen, max_cost=MAX_COST) print(r) npc_result = get_name_puzzle_conditions(gen, max_cost=MAX_COST, cost_per_byte=COST_PER_BYTE, safe_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)]) CONDITIONS = [ (ConditionOpcode.CREATE_COIN, [cond_1]), ] npc = NPC( coin_name=bytes32.fromhex( "e8538c2d14f2a7defae65c5c97f5d4fae7ee64acef7fec9d28ad847a0880fd03" ), puzzle_hash=bytes32.fromhex( "9dcf97a184f32623d11a73124ceb99a5709b083721e878a16d78f596718ba7b2" ), conditions=CONDITIONS, ) assert npc_result.npc_list == [npc]
def get_name_puzzle_conditions( generator: BlockGenerator, max_cost: int, *, cost_per_byte: int, mempool_mode: bool ) -> NPCResult: block_program, block_program_args = setup_generator_args(generator) max_cost -= len(bytes(generator.program)) * cost_per_byte if max_cost < 0: return NPCResult(uint16(Err.INVALID_BLOCK_COST.value), [], uint64(0)) 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) if err is not None: 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)) except BaseException as e: log.debug(f"get_name_puzzle_condition failed: {e}") return NPCResult(uint16(Err.GENERATOR_RUNTIME_ERROR.value), [], uint64(0))
def get_name_puzzle_conditions( block_program: SerializedProgram, safe_mode: bool ) -> Tuple[Optional[str], Optional[List[NPC]], Optional[uint64]]: # TODO: allow generator mod to take something (future) # TODO: write more tests block_program_args = SerializedProgram.from_bytes(b"\x80") try: if safe_mode: cost, result = GENERATOR_MOD.run_safe_with_cost( block_program, block_program_args) else: cost, result = GENERATOR_MOD.run_with_cost(block_program, block_program_args) npc_list: List[NPC] = [] opcodes: Set[bytes] = set(item.value for item in ConditionOpcode) for res in result.as_iter(): conditions_list: List[ConditionWithArgs] = [] spent_coin_parent_id: bytes32 = res.first().first().as_atom() spent_coin_puzzle_hash: bytes32 = res.first().rest().first( ).as_atom() spent_coin_amount: uint64 = uint64( res.first().rest().rest().first().as_int()) spent_coin: Coin = Coin(spent_coin_parent_id, spent_coin_puzzle_hash, spent_coin_amount) for cond in res.rest().first().as_iter(): if cond.first().as_atom() in opcodes: opcode: ConditionOpcode = ConditionOpcode( cond.first().as_atom()) elif not safe_mode: opcode = ConditionOpcode.UNKNOWN else: return "Unknown operator in safe mode.", None, None conditions_list.append( ConditionWithArgs(opcode, cond.rest().as_atom_list())) conditions_dict = conditions_by_opcode(conditions_list) if conditions_dict is None: conditions_dict = {} npc_list.append( NPC(spent_coin.name(), spent_coin.puzzle_hash, [(a, b) for a, b in conditions_dict.items()])) return None, npc_list, uint64(cost) except Exception: tb = traceback.format_exc() return tb, None, None
def get_name_puzzle_conditions(block_program: SerializedProgram, safe_mode: bool): # TODO: allow generator mod to take something (future) # TODO: write more tests block_program_args = SerializedProgram.from_bytes(b"\x80") try: if safe_mode: cost, result = GENERATOR_MOD.run_safe_with_cost( block_program, block_program_args) else: cost, result = GENERATOR_MOD.run_with_cost(block_program, block_program_args) npc_list = [] opcodes = set(item.value for item in ConditionOpcode) for res in result.as_iter(): conditions_list = [] name = std_hash( bytes(res.first().first().as_atom() + res.first().rest().first().as_atom() + res.first().rest().rest().first().as_atom())) puzzle_hash = bytes32(res.first().rest().first().as_atom()) for cond in res.rest().first().as_iter(): if cond.first().as_atom() in opcodes: opcode = ConditionOpcode(cond.first().as_atom()) elif not safe_mode: opcode = ConditionOpcode.UNKNOWN else: return "Unknown operator in safe mode.", None, None if len(list(cond.as_iter())) > 1: cond_var_list = [] for cond_1 in cond.rest().as_iter(): cond_var_list.append(cond_1.as_atom()) cvl = ConditionVarPair(opcode, cond_var_list) else: cvl = ConditionVarPair(opcode, []) conditions_list.append(cvl) conditions_dict = conditions_by_opcode(conditions_list) if conditions_dict is None: conditions_dict = {} npc_list.append( NPC(name, puzzle_hash, [(a, b) for a, b in conditions_dict.items()])) return None, npc_list, uint64(cost) except Exception: tb = traceback.format_exc() return tb, None, None
def get_name_puzzle_conditions(generator: BlockGenerator, max_cost: int, safe_mode: bool) -> NPCResult: try: block_program, block_program_args = setup_generator_args(generator) if safe_mode: cost, result = GENERATOR_MOD.run_safe_with_cost( max_cost, block_program, block_program_args) else: cost, result = GENERATOR_MOD.run_with_cost(max_cost, block_program, block_program_args) npc_list: List[NPC] = [] opcodes: Set[bytes] = set(item.value for item in ConditionOpcode) for res in result.as_iter(): conditions_list: List[ConditionWithArgs] = [] spent_coin_parent_id: bytes32 = res.first().first().as_atom() spent_coin_puzzle_hash: bytes32 = res.first().rest().first( ).as_atom() spent_coin_amount: uint64 = uint64( res.first().rest().rest().first().as_int()) spent_coin: Coin = Coin(spent_coin_parent_id, spent_coin_puzzle_hash, spent_coin_amount) for cond in res.rest().first().as_iter(): if cond.first().as_atom() in opcodes: opcode: ConditionOpcode = ConditionOpcode( cond.first().as_atom()) elif not safe_mode: opcode = ConditionOpcode.UNKNOWN else: return NPCResult(uint16(Err.GENERATOR_RUNTIME_ERROR.value), [], uint64(0)) cvl = ConditionWithArgs(opcode, cond.rest().as_atom_list()) conditions_list.append(cvl) conditions_dict = conditions_by_opcode(conditions_list) if conditions_dict is None: conditions_dict = {} npc_list.append( NPC(spent_coin.name(), spent_coin.puzzle_hash, [(a, b) for a, b in conditions_dict.items()])) return NPCResult(None, npc_list, uint64(cost)) except Exception: return NPCResult(uint16(Err.GENERATOR_RUNTIME_ERROR.value), [], uint64(0))
def run_generator( block_generator: BlockGenerator, constants: ConsensusConstants, max_cost: int, height: uint32 ) -> List[CAT]: if height >= DEFAULT_CONSTANTS.SOFT_FORK_HEIGHT: # conditions must use integers in canonical encoding (i.e. no redundant # leading zeros) # the division operator may not be used with negative operands flags = COND_CANON_INTS | NO_NEG_DIV else: flags = 0 _, result = block_generator.program.run_with_cost(max_cost, flags, block_generator.generator_refs) coin_spends = result.first() cat_list: List[CAT] = [] for spend in coin_spends.as_iter(): parent, puzzle, amount, solution = spend.as_iter() matched, curried_args = match_cat_puzzle(puzzle) if not matched: continue _, tail_hash, _ = curried_args memo = "" result = puzzle.run(solution) conds: Dict[ConditionOpcode, List[ConditionWithArgs]] = {} for condition in result.as_python(): op = ConditionOpcode(condition[0]) if op not in conds: conds[op] = [] if condition[0] != ConditionOpcode.CREATE_COIN or len(condition) < 4: conds[op].append(ConditionWithArgs(op, [i for i in condition[1:3]])) continue # If only 3 elements (opcode + 2 args), there is no memo, this is ph, amount if type(condition[3]) != list: # If it's not a list, it's not the correct format conds[op].append(ConditionWithArgs(op, [i for i in condition[1:3]])) continue conds[op].append(ConditionWithArgs(op, [i for i in condition[1:3]] + [condition[3][0]])) # special retirement address if condition[3][0].hex() != "0000000000000000000000000000000000000000000000000000000000000000": continue if len(condition[3]) >= 2: try: memo = condition[3][1].decode("utf-8", errors="strict") except UnicodeError: pass # ignore this error which should leave memo as empty string # technically there could be more such create_coin ops in the list but our wallet does not # so leaving it for the future break puzzle_hash = puzzle.get_tree_hash() coin = Coin(parent.atom, puzzle_hash, int_from_bytes(amount.atom)) cat_list.append( CAT( tail_hash=bytes(tail_hash).hex()[2:], memo=memo, npc=NPC(coin.name(), puzzle_hash, [(op, cond) for op, cond in conds.items()]), ) ) return cat_list
def get_name_puzzle_conditions_python(generator: BlockGenerator, max_cost: int, *, cost_per_byte: int, safe_mode: bool) -> NPCResult: """ This executes the generator program and returns the coins and their conditions. If the cost of the program (size, CLVM execution and conditions) exceed max_cost, the function fails. In order to accurately take the size of the program into account when calculating cost, cost_per_byte must be specified. safe_mode determines whether the clvm program and conditions are executed in strict mode or not. When in safe/strict mode, unknow operations or conditions are considered failures. This is the mode when accepting transactions into the mempool. """ block_program, block_program_args = setup_generator_args(generator) max_cost -= len(bytes(generator.program)) * cost_per_byte if max_cost < 0: return NPCResult(uint16(Err.INVALID_BLOCK_COST.value), [], uint64(0)) if safe_mode: clvm_cost, result = GENERATOR_MOD.run_safe_with_cost( max_cost, block_program, block_program_args) else: clvm_cost, result = GENERATOR_MOD.run_with_cost( max_cost, block_program, block_program_args) max_cost -= clvm_cost if max_cost < 0: return NPCResult(uint16(Err.INVALID_BLOCK_COST.value), [], uint64(0)) npc_list: List[NPC] = [] for res in result.first().as_iter(): conditions_list: List[ConditionWithArgs] = [] if len(res.first().atom) != 32: raise ValidationError(Err.INVALID_CONDITION) spent_coin_parent_id: bytes32 = res.first().as_atom() res = res.rest() if len(res.first().atom) != 32: raise ValidationError(Err.INVALID_CONDITION) spent_coin_puzzle_hash: bytes32 = res.first().as_atom() res = res.rest() spent_coin_amount: uint64 = uint64(sanitize_int( res.first(), safe_mode)) res = res.rest() spent_coin: Coin = Coin(spent_coin_parent_id, spent_coin_puzzle_hash, spent_coin_amount) for cond in res.first().as_iter(): cost, cvl = parse_condition(cond, safe_mode) max_cost -= cost if max_cost < 0: return NPCResult(uint16(Err.INVALID_BLOCK_COST.value), [], uint64(0)) if cvl is not None: conditions_list.append(cvl) conditions_dict = conditions_by_opcode(conditions_list) if conditions_dict is None: conditions_dict = {} npc_list.append( NPC(spent_coin.name(), spent_coin.puzzle_hash, [(a, b) for a, b in conditions_dict.items()])) return NPCResult(None, npc_list, uint64(clvm_cost))
def get_name_puzzle_conditions(generator: BlockGenerator, max_cost: int, *, cost_per_byte: int, mempool_mode: bool, height: Optional[uint32] = None) -> NPCResult: block_program, block_program_args = setup_generator_args(generator) size_cost = len(bytes(generator.program)) * cost_per_byte max_cost -= size_cost if max_cost < 0: return NPCResult(uint16(Err.INVALID_BLOCK_COST.value), [], uint64(0)) # in mempool mode, the height doesn't matter, because it's always strict. # But otherwise, height must be specified to know which rules to apply assert mempool_mode or height is not None # mempool mode also has these rules apply assert (MEMPOOL_MODE & COND_CANON_INTS) != 0 assert (MEMPOOL_MODE & NO_NEG_DIV) != 0 if mempool_mode: flags = MEMPOOL_MODE elif unwrap(height) >= DEFAULT_CONSTANTS.SOFT_FORK_HEIGHT: # conditions must use integers in canonical encoding (i.e. no redundant # leading zeros) # the division operator may not be used with negative operands flags = COND_CANON_INTS | NO_NEG_DIV else: flags = 0 try: 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)) 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]]) npc_list.append( NPC(r.coin_id, r.puzzle_hash, [(op, cond) for op, cond in conditions.items()])) return NPCResult(None, npc_list, uint64(result.cost + size_cost)) except BaseException as e: log.debug(f"get_name_puzzle_condition failed: {e}") return NPCResult(uint16(Err.GENERATOR_RUNTIME_ERROR.value), [], uint64(0))