def match_cat_puzzle(puzzle: Program) -> Tuple[bool, Iterator[Program]]: """ Given a puzzle test if it's a CAT and, if it is, return the curried arguments """ mod, curried_args = puzzle.uncurry() if mod == CAT_MOD: return True, curried_args.as_iter() else: return False, iter(())
def create_compressed_generator( original_generator: CompressorArg, compressed_cse_list: List[List[Union[List[uint64], List[Union[bytes, None, Program]]]]], ) -> BlockGenerator: """ Bind the generator block program template to a particular reference block, template bytes offsets, and SpendBundle. """ start = original_generator.start end = original_generator.end program = DECOMPRESS_BLOCK.curry(DECOMPRESS_PUZZLE, DECOMPRESS_CSE_WITH_PREFIX, Program.to(start), Program.to(end), compressed_cse_list) generator_arg = GeneratorArg(original_generator.block_height, original_generator.generator) return BlockGenerator(program, [generator_arg])
def match_limitations_program( limitations_program: Program ) -> Tuple[Optional[LimitationsProgram], List[Program]]: uncurried_mod, curried_args = limitations_program.uncurry() for key, lp in ALL_LIMITATIONS_PROGRAMS.items(): matched, args = lp.match(uncurried_mod, curried_args) if matched: return lp, args return None, []
def get_innerpuzzle_from_puzzle(puzzle: Program) -> Optional[Program]: r = puzzle.uncurry() if r is None: return None inner_f, args = r if not is_did_core(inner_f): return None mod_hash, genesis_id, inner_puzzle = list(args.as_iter()) return inner_puzzle
def make_solution( self, condition_dic: Dict[ConditionOpcode, List[ConditionWithArgs]]) -> Program: ret = [] for con_list in condition_dic.values(): for cvp in con_list: ret.append([cvp.opcode.value] + cvp.vars) return solution_for_conditions(Program.to(ret))
def test_make_generator_args(self): generator_ref_list = [gen1] gen_args = create_generator_args(generator_ref_list) gen_args_as_program = Program.from_bytes(bytes(gen_args)) # First Argument to the block generator is the first template generator arg2 = gen_args_as_program.first() print(arg2) assert arg2 == bytes(gen1)
def get_output_amount_for_puzzle_and_solution(puzzle: Program, solution: Program) -> int: error, conditions, cost = conditions_dict_for_solution( puzzle, solution, INFINITE_COST) total = 0 if conditions: for _ in conditions.get(ConditionOpcode.CREATE_COIN, []): total += Program.to(_.vars[1]).as_int() return total
def coin_solution_for_lock_coin( prev_coin: Coin, subtotal: int, coin: Coin, ) -> CoinSolution: puzzle_reveal = LOCK_INNER_PUZZLE.curry(prev_coin.as_list(), subtotal) coin = Coin(coin.name(), puzzle_reveal.get_tree_hash(), uint64(0)) coin_solution = CoinSolution(coin, puzzle_reveal, Program.to(0)) return coin_solution
def get_innerpuzzle_from_puzzle(puzzle: Program) -> Optional[Program]: r = puzzle.uncurry() if r is None: return None inner_f, args = r if not is_did_core(inner_f): return None SINGLETON_STRUCT, INNER_PUZZLE = list(args.as_iter()) return INNER_PUZZLE
def lineage_proof_for_cc_parent(parent_coin: Coin, parent_inner_puzzle_hash: bytes32) -> Program: return Program.to(( 1, [ parent_coin.parent_coin_info, parent_inner_puzzle_hash, parent_coin.amount ], ))
def solve(self, puzzle_db: PuzzleDB, puzzle: Program, **kwargs: Any) -> Program: """ The legal values and types for `kwargs` depends on the underlying solver that's invoked. The `kwargs` are passed through to any inner solvers that may need to be called. """ puzzle_hash = puzzle.get_tree_hash() puzzle_args = [] if puzzle_hash not in self.solvers_by_puzzle_hash: puzzle_template, args = puzzle.uncurry() puzzle_args = list(args.as_iter()) puzzle_hash = puzzle_template.get_tree_hash() solver_f = self.solvers_by_puzzle_hash.get(puzzle_hash) if solver_f: return solver_f(self, puzzle_db, puzzle_args, kwargs) raise ValueError("can't solve")
def to_program(self) -> Program: final_list: List[Any] = [] if self.parent_name is not None: final_list.append(self.parent_name) if self.inner_puzzle_hash is not None: final_list.append(self.inner_puzzle_hash) if self.amount is not None: final_list.append(self.amount) return Program.to(final_list)
def solve_anyone_can_spend(solver: Solver, puzzle_db: PuzzleDB, args: List[Program], kwargs: Dict) -> Program: """ This is the anyone-can-spend puzzle `1`. Note that farmers can easily steal this coin, so don't use it except for testing. """ conditions = from_kwargs(kwargs, "conditions", List[Program]) solution = Program.to(conditions) return solution
def solve_pool_member(solver: Solver, puzzle_db: PuzzleDB, args: List[Program], kwargs: Dict) -> Program: pool_member_spend_type = from_kwargs(kwargs, "pool_member_spend_type") allowable = ["to-waiting-room", "claim-p2-nft"] if pool_member_spend_type not in allowable: raise ValueError( "`pool_member_spend_type` must be one of %s for POOL_MEMBER puzzle" % "/".join(allowable)) to_waiting_room = pool_member_spend_type == "to-waiting-room" if to_waiting_room: key_value_list = from_kwargs(kwargs, "key_value_list", List[Tuple[str, Program]]) 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", int) pool_reward_height = from_kwargs(kwargs, "pool_reward_height", int) solution = Program.to([0, pool_reward_amount, pool_reward_height]) return solution
async def test_invalid_puzzle_announcement(self): announce = Announcement(EASY_PUZZLE_HASH, b"test_bad") conditions = Program.to( assemble( f"(({ConditionOpcode.CREATE_PUZZLE_ANNOUNCEMENT[0]} 'test')" f"({ConditionOpcode.ASSERT_PUZZLE_ANNOUNCEMENT[0]} 0x{announce.name().hex()}))" ) ) await check_conditions(conditions, expected_err=Err.ASSERT_ANNOUNCE_CONSUMED_FAILED)
def claim_p2_singleton( p2_singleton_coin: Coin, singleton_inner_puzhash: bytes32, launcher_id: bytes32, ) -> Tuple[Program, Program, CoinSolution]: assertion = Program.to([ ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT, std_hash(p2_singleton_coin.name() + b"$") ]) announcement = Program.to( [ConditionOpcode.CREATE_PUZZLE_ANNOUNCEMENT, p2_singleton_coin.name()]) claim_coinsol = CoinSolution( p2_singleton_coin, pay_to_singleton_puzzle(launcher_id), solution_for_p2_singleton(p2_singleton_coin, singleton_inner_puzhash), ) return assertion, announcement, claim_coinsol
def test_block_program_zero_with_curry(self): self.maxDiff = None cse1 = binutils.assemble( "(((0x0000000000000000000000000000000000000000000000000000000000000000 0x0186a0) (0xb081963921826355dcb6c355ccf9c2637c18adf7d38ee44d803ea9ca41587e48c913d8d46896eb830aeadfc13144a8eac3 (() (q (51 0x6b7a83babea1eec790c947db4464ab657dbe9b887fe9acc247062847b8c2a8a9 0x0186a0)) ()))))" ) # noqa cse2 = binutils.assemble(""" ( ((0x0000000000000000000000000000000000000000000000000000000000000000 0x0186a0) (0xb081963921826355dcb6c355ccf9c2637c18adf7d38ee44d803ea9ca41587e48c913d8d46896eb830aeadfc13144a8eac3 (() (q (51 0x6b7a83babea1eec790c947db4464ab657dbe9b887fe9acc247062847b8c2a8a9 0x0186a0)) ())) ) ((0x0000000000000000000000000000000000000000000000000000000000000001 0x0186a0) (0xb0a6207f5173ec41491d9f2c1b8fff5579e13703077e0eaca8fe587669dcccf51e9209a6b65576845ece5f7c2f3229e7e3 (() (q (51 0x24254a3efc3ebfac9979bbe0d615e2eda043aa329905f65b63846fa24149e2b6 0x0186a0)) ()))) ) """) # noqa start = 2 + 44 end = start + 238 # (mod (decompress_puzzle decompress_coin_solution_entry start end compressed_cses deserialize generator_list reserved_arg) # cost, out = DECOMPRESS_BLOCK.run_with_cost([DECOMPRESS_PUZZLE, DECOMPRESS_CSE, start, Program.to(end), cse0, DESERIALIZE_MOD, bytes(original_generator)]) p = DECOMPRESS_BLOCK.curry(DECOMPRESS_PUZZLE, DECOMPRESS_CSE_WITH_PREFIX, start, Program.to(end)) cost, out = p.run_with_cost( [cse2, DESERIALIZE_MOD, bytes(original_generator)]) print() print(p) print(out) p_with_cses = DECOMPRESS_BLOCK.curry(DECOMPRESS_PUZZLE, DECOMPRESS_CSE_WITH_PREFIX, start, Program.to(end), cse2) cost, out = p_with_cses.run_with_cost( [DESERIALIZE_MOD, bytes(original_generator)]) print() print(p_with_cses) print(out)
async def test_genesis_by_puzhash(self, setup_sim): sim, sim_client = setup_sim try: standard_acs = Program.to(1) standard_acs_ph: bytes32 = standard_acs.get_tree_hash() await sim.farm_block(standard_acs_ph) starting_coin: Coin = (await sim_client.get_coin_records_by_puzzle_hash(standard_acs_ph))[0].coin tail: Program = GenesisByPuzhash.construct([Program.to(starting_coin.puzzle_hash)]) checker_solution: Program = GenesisByPuzhash.solve([], starting_coin.to_json_dict()) cat_puzzle: Program = construct_cat_puzzle(CAT_MOD, tail.get_tree_hash(), acs) cat_ph: bytes32 = cat_puzzle.get_tree_hash() await sim_client.push_tx( SpendBundle( [CoinSpend(starting_coin, standard_acs, Program.to([[51, cat_ph, starting_coin.amount]]))], G2Element(), ) ) await sim.farm_block() await self.do_spend( sim, sim_client, tail, [(await sim_client.get_coin_records_by_puzzle_hash(cat_ph, include_spent_coins=False))[0].coin], [NO_LINEAGE_PROOF], [ Program.to( [ [51, acs.get_tree_hash(), starting_coin.amount], [51, 0, -113, tail, checker_solution], ] ) ], (MempoolInclusionStatus.SUCCESS, None), limitations_solutions=[checker_solution], cost_str="Genesis by Puzhash", ) finally: await sim.close()
def parse_sexp_to_condition( sexp: Program, ) -> Tuple[Optional[Err], Optional[ConditionVarPair]]: """ Takes a ChiaLisp sexp and returns a ConditionVarPair. If it fails, returns an Error """ if not sexp.listp(): return Err.INVALID_CONDITION, None items = sexp.as_python() if not isinstance(items[0], bytes): return Err.INVALID_CONDITION, None try: opcode = ConditionOpcode(items[0]) except ValueError: opcode = ConditionOpcode.UNKNOWN if len(items) == 3: return None, ConditionVarPair(opcode, [items[1], items[2]]) return None, ConditionVarPair(opcode, [items[1]])
def get_seconds_and_delayed_puzhash_from_p2_singleton_puzzle( puzzle: Program) -> Tuple[uint64, bytes32]: r = puzzle.uncurry() if r is None: return False inner_f, args = r singleton_mod_hash, launcher_id, launcher_puzzle_hash, seconds_delay, delayed_puzzle_hash = list( args.as_iter()) seconds_delay = uint64(seconds_delay.as_int()) return seconds_delay, delayed_puzzle_hash.as_atom()
def default_payments_and_conditions( initial_index: int, key_lookup: KeyTool ) -> Tuple[List[Tuple[bytes32, int]], Program]: payments = [ (throwaway_puzzle_hash(initial_index + 1, key_lookup), initial_index * 1000), (throwaway_puzzle_hash(initial_index + 2, key_lookup), (initial_index + 1) * 1000), ] conditions = Program.to([make_create_coin_condition(ph, amount) for ph, amount in payments]) return payments, conditions
def test_rom_inputs(self): # this test checks that the generator just works # It's useful for debugging the generator prior to having the ROM invoke it. args = Program.to( [DESERIALIZE_MOD, [FIRST_GENERATOR, SECOND_GENERATOR]]) sp = to_sp(COMPILED_GENERATOR_CODE) cost, r = sp.run_with_cost(MAX_COST, args) assert cost == EXPECTED_ABBREVIATED_COST assert r.as_bin().hex() == EXPECTED_OUTPUT
async def test_invalid_seconds_absolute(self): # TODO: make the test suite not use `time.time` so we can more accurately # set `time_now` to make it minimal while still failing time_now = int(time.time()) + 3000 conditions = Program.to( assemble( f"(({ConditionOpcode.ASSERT_SECONDS_ABSOLUTE[0]} {time_now}))") ) await check_conditions(conditions, expected_err=Err.ASSERT_SECONDS_ABSOLUTE_FAILED)
async def test_valid_coin_announcement(self): blocks = initial_blocks() coin = list(blocks[-2].get_included_reward_coins())[0] announce = Announcement(coin.name(), b"test") conditions = Program.to( assemble( f"(({ConditionOpcode.CREATE_COIN_ANNOUNCEMENT[0]} 'test')" f"({ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT[0]} 0x{announce.name().hex()}))" )) await check_conditions(conditions)
def launcher_conditions_and_spend_bundle( puzzle_db: PuzzleDB, parent_coin_id: bytes32, launcher_amount: uint64, initial_singleton_inner_puzzle: Program, metadata: List[Tuple[str, str]], launcher_puzzle: Program, ) -> Tuple[bytes32, List[Program], SpendBundle]: puzzle_db.add_puzzle(launcher_puzzle) launcher_puzzle_hash = launcher_puzzle.get_tree_hash() launcher_coin = Coin(parent_coin_id, launcher_puzzle_hash, launcher_amount) singleton_full_puzzle = singleton_puzzle(launcher_coin.name(), launcher_puzzle_hash, initial_singleton_inner_puzzle) puzzle_db.add_puzzle(singleton_full_puzzle) singleton_full_puzzle_hash = singleton_full_puzzle.get_tree_hash() message_program = Program.to( [singleton_full_puzzle_hash, launcher_amount, metadata]) expected_announcement = Announcement(launcher_coin.name(), message_program.get_tree_hash()) expected_conditions = [] expected_conditions.append( Program.to( binutils.assemble( f"(0x{ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT.hex()} 0x{expected_announcement.name()})" ))) expected_conditions.append( Program.to( binutils.assemble( f"(0x{ConditionOpcode.CREATE_COIN.hex()} 0x{launcher_puzzle_hash} {launcher_amount})" ))) solution = solve_puzzle( puzzle_db, launcher_puzzle, destination_puzzle_hash=singleton_full_puzzle_hash, launcher_amount=launcher_amount, metadata=metadata, ) coin_spend = CoinSpend(launcher_coin, SerializedProgram.from_program(launcher_puzzle), solution) spend_bundle = SpendBundle([coin_spend], G2Element()) return launcher_coin.name(), expected_conditions, spend_bundle
def conditions_for_solution( puzzle_reveal: Program, solution: Program, ) -> Tuple[Optional[Err], Optional[List[ConditionWithArgs]], uint64]: # get the standard script for a puzzle hash and feed in the solution try: cost, r = puzzle_reveal.run_with_cost(solution) error, result = parse_sexp_to_conditions(r) return error, result, uint64(cost) except Program.EvalError: return Err.SEXP_ERROR, None, uint64(0)
async def test_invalid_my_id(self): blocks = initial_blocks() coin = list(blocks[-2].get_included_reward_coins())[0] wrong_name = bytearray(coin.name()) wrong_name[-1] ^= 1 conditions = Program.to( assemble( f"(({ConditionOpcode.ASSERT_MY_COIN_ID[0]} 0x{wrong_name.hex()}))" )) await check_conditions(conditions, expected_err=Err.ASSERT_MY_COIN_ID_FAILED)
def sign_delegated_puz(self, del_puz: Program, coin: Coin) -> G2Element: synthetic_secret_key: PrivateKey = p2_delegated_puzzle_or_hidden_puzzle.calculate_synthetic_secret_key( # noqa PrivateKey.from_bytes( secret_exponent_for_index(1).to_bytes(32, "big"), ), p2_delegated_puzzle_or_hidden_puzzle.DEFAULT_HIDDEN_PUZZLE_HASH, ) return AugSchemeMPL.sign( synthetic_secret_key, (del_puz.get_tree_hash() + coin.name() + DEFAULT_CONSTANTS.AGG_SIG_ME_ADDITIONAL_DATA), # noqa )
async def get_puzzle_and_solution(self, coin_id: bytes32, height: uint32) -> Optional[CoinSpend]: generator = list( filter(lambda block: block.height == height, self.service.blocks))[0].transactions_generator coin_record = await self.service.mempool_manager.coin_store.get_coin_record( coin_id) error, puzzle, solution = get_puzzle_and_solution_for_coin( generator, coin_id, DEFAULT_CONSTANTS.MAX_BLOCK_COST_CLVM, ) if error: return None else: puzzle_ser: SerializedProgram = SerializedProgram.from_program( Program.to(puzzle)) solution_ser: SerializedProgram = SerializedProgram.from_program( Program.to(solution)) return CoinSpend(coin_record.coin, puzzle_ser, solution_ser)
def test_only_odd_coins(): did_core_hash = DID_CORE_MOD.get_tree_hash() solution = Program.to([ did_core_hash, did_core_hash, 1, [0xDEADBEEF, 0xCAFEF00D, 200], 200, [[51, 0xCAFEF00D, 200]] ]) try: result, cost = DID_CORE_MOD.run_with_cost(INFINITE_COST, solution) except Exception as e: assert e.args == ("clvm raise", ) else: assert False solution = Program.to([ did_core_hash, did_core_hash, 1, [0xDEADBEEF, 0xCAFEF00D, 210], 205, [[51, 0xCAFEF00D, 205]] ]) try: result, cost = DID_CORE_MOD.run_with_cost(INFINITE_COST, solution) except Exception: assert False