async def test_standard_tx(self): # this isn't a real public key, but we don't care public_key = bytes.fromhex( "af949b78fa6a957602c3593a3d6cb7711e08720415dad83" "1ab18adacaa9b27ec3dda508ee32e24bc811c0abc5781ae21") puzzle_program = SerializedProgram.from_bytes( p2_delegated_puzzle_or_hidden_puzzle.puzzle_for_pk(public_key)) conditions = binutils.assemble( "((51 0x699eca24f2b6f4b25b16f7a418d0dc4fc5fce3b9145aecdda184158927738e3e 10)" " (51 0x847bb2385534070c39a39cc5dfdc7b35e2db472dc0ab10ab4dec157a2178adbf 0x00cbba106df6))" ) solution_program = SerializedProgram.from_bytes( p2_delegated_puzzle_or_hidden_puzzle.solution_for_conditions( conditions)) time_start = time.time() total_cost = 0 for i in range(0, 1000): cost, result = puzzle_program.run_with_cost( test_constants.MAX_BLOCK_COST_CLVM, solution_program) total_cost += cost time_end = time.time() duration = time_end - time_start log.info(f"Time spent: {duration}") assert duration < 3
async def test_clvm_strict_mode(self, rust_checker: bool): block = Program.from_bytes(bytes(SMALL_BLOCK_GENERATOR.program)) disassembly = binutils.disassemble(block) # this is a valid generator program except the first clvm # if-condition, that depends on executing an unknown operator # ("0xfe"). In strict mode, this should fail, but in non-strict # mode, the unknown operator should be treated as if it returns (). program = SerializedProgram.from_bytes( binutils.assemble( f"(i (0xfe (q . 0)) (q . ()) {disassembly})").as_bin()) generator = BlockGenerator(program, []) npc_result: NPCResult = get_name_puzzle_conditions( generator, test_constants.MAX_BLOCK_COST_CLVM, cost_per_byte=test_constants.COST_PER_BYTE, safe_mode=True, rust_checker=rust_checker, ) assert npc_result.error is not None npc_result = get_name_puzzle_conditions( generator, test_constants.MAX_BLOCK_COST_CLVM, cost_per_byte=test_constants.COST_PER_BYTE, safe_mode=False, rust_checker=rust_checker, ) assert npc_result.error is None
def load_serialized_clvm(clvm_filename, package_or_requirement=__name__) -> SerializedProgram: """ This function takes a .clvm file in the given package and compiles it to a .clvm.hex file if the .hex file is missing or older than the .clvm file, then returns the contents of the .hex file as a `Program`. clvm_filename: file name package_or_requirement: usually `__name__` if the clvm file is in the same package """ hex_filename = f"{clvm_filename}.hex" try: if pkg_resources.resource_exists(package_or_requirement, clvm_filename): full_path = pathlib.Path( pkg_resources.resource_filename(package_or_requirement, clvm_filename)) output = full_path.parent / hex_filename compile_clvm(full_path, output, search_paths=[full_path.parent]) except NotImplementedError: # pyinstaller doesn't support `pkg_resources.resource_exists` # so we just fall through to loading the hex clvm pass clvm_hex = pkg_resources.resource_string(package_or_requirement, hex_filename).decode("utf8") clvm_blob = bytes.fromhex(clvm_hex) return SerializedProgram.from_bytes(clvm_blob)
async def test_clvm_max_cost(self): block = Program.from_bytes(bytes(SMALL_BLOCK_GENERATOR.program)) disassembly = binutils.disassemble(block) # this is a valid generator program except the first clvm # if-condition, that depends on executing an unknown operator # ("0xfe"). In strict mode, this should fail, but in non-strict # mode, the unknown operator should be treated as if it returns (). # the CLVM program has a cost of 391969 program = SerializedProgram.from_bytes( binutils.assemble( f"(i (softfork (q . 10000000)) (q . ()) {disassembly})"). as_bin()) # ensure we fail if the program exceeds the cost generator = BlockGenerator(program, []) npc_result: NPCResult = get_name_puzzle_conditions( generator, 10000000, False) assert npc_result.error is not None assert npc_result.clvm_cost == 0 # raise the max cost to make sure this passes # ensure we pass if the program does not exceeds the cost npc_result: NPCResult = get_name_puzzle_conditions( generator, 20000000, False) assert npc_result.error is None assert npc_result.clvm_cost > 10000000
def test_shatrees_match(self): """Checks to see that all .sha256tree files match their .hex files""" for prog_path in wallet_program_files: # load the .hex file as a program hex_filename = path_with_ext(prog_path, ".hex") clvm_hex = hex_filename.read_text() # .decode("utf8") clvm_blob = bytes.fromhex(clvm_hex) s = SerializedProgram.from_bytes(clvm_blob) p = Program.from_bytes(clvm_blob) # load the checked-in shatree existing_sha = path_with_ext( prog_path, ".hex.sha256tree").read_text().strip() self.assertEqual( s.get_tree_hash().hex(), existing_sha, msg= f"Checked-in shatree hash file does not match shatree hash of loaded SerializedProgram: {prog_path}", # noqa ) self.assertEqual( p.get_tree_hash().hex(), existing_sha, msg= f"Checked-in shatree hash file does not match shatree hash of loaded Program: {prog_path}", )
def make_generator_args( generator_ref_list: List[SerializedProgram]) -> SerializedProgram: """ `make_generator_args`: The format and contents of these arguments affect consensus. """ gen_ref_list = [Program.from_bytes(bytes(g)) for g in generator_ref_list] return SerializedProgram.from_bytes( bytes(Program.to([DESERIALIZE_MOD, gen_ref_list])))
def simple_solution_generator(bundle: SpendBundle) -> BlockGenerator: """ Simply quotes the solutions we know. """ cse_list = spend_bundle_to_coin_solution_entry_list(bundle) block_program = SerializedProgram.from_bytes(SExp.to((binutils.assemble("#q"), cse_list)).as_bin()) generator = BlockGenerator(block_program, []) return generator
def get_puzzle_and_solution_for_coin(block_program: SerializedProgram, coin_name: bytes): try: block_program_args = SerializedProgram.from_bytes(b"\x80") cost, result = GENERATOR_FOR_SINGLE_COIN_MOD.run_with_cost(block_program, block_program_args, coin_name) puzzle = result.first() solution = result.rest().first() return None, puzzle, solution except Exception as e: return e, None, None
async def test_strict_mode(self, rust_checker: bool): wallet_tool = bt.get_pool_wallet_tool() ph = wallet_tool.get_new_puzzlehash() num_blocks = 3 blocks = bt.get_consecutive_blocks(num_blocks, [], guarantee_transaction_block=True, pool_reward_puzzle_hash=ph, farmer_reward_puzzle_hash=ph) coinbase = None for coin in blocks[2].get_included_reward_coins(): if coin.puzzle_hash == ph: coinbase = coin break assert coinbase is not None spend_bundle = wallet_tool.generate_signed_transaction( coinbase.amount, BURN_PUZZLE_HASH, coinbase, ) assert spend_bundle is not None pk = bytes.fromhex( "88bc9360319e7c54ab42e19e974288a2d7a817976f7633f4b43f36ce72074e59c4ab8ddac362202f3e366f0aebbb6280" ) puzzle = p2_delegated_puzzle_or_hidden_puzzle.puzzle_for_pk(pk) disassembly = binutils.disassemble(puzzle) program = SerializedProgram.from_bytes( binutils.assemble( f"(q ((0x3d2331635a58c0d49912bc1427d7db51afe3f20a7b4bcaffa17ee250dcbcbfaa {disassembly} 300" f" (() (q . ((65 '00000000000000000000000000000000' 0x0cbba106e000))) ()))))" ).as_bin()) generator = BlockGenerator(program, []) npc_result: NPCResult = get_name_puzzle_conditions( generator, test_constants.MAX_BLOCK_COST_CLVM, cost_per_byte=test_constants.COST_PER_BYTE, safe_mode=True, rust_checker=rust_checker, ) assert npc_result.error is not None npc_result = get_name_puzzle_conditions( generator, test_constants.MAX_BLOCK_COST_CLVM, cost_per_byte=test_constants.COST_PER_BYTE, safe_mode=False, rust_checker=rust_checker, ) assert npc_result.error is None coin_name = npc_result.npc_list[0].coin_name error, puzzle, solution = get_puzzle_and_solution_for_coin( generator, coin_name, test_constants.MAX_BLOCK_COST_CLVM) assert error is None
def simple_solution_generator(bundle: SpendBundle) -> BlockGenerator: """ Simply quotes the solutions we know. """ cse_list = spend_bundle_to_serialized_coin_solution_entry_list(bundle) block_program = b"\xff" block_program += SExp.to(binutils.assemble("#q")).as_bin() block_program += b"\xff" + cse_list + b"\x80" return BlockGenerator(SerializedProgram.from_bytes(block_program), [])
def test_multiple_input_gen_refs(self): start1, end1 = match_standard_transaction_at_any_index(gen1) start2, end2 = match_standard_transaction_at_any_index(gen2) ca1 = CompressorArg(FAKE_BLOCK_HEIGHT1, SerializedProgram.from_bytes(gen1), start1, end1) ca2 = CompressorArg(FAKE_BLOCK_HEIGHT2, SerializedProgram.from_bytes(gen2), start2, end2) prefix_len1 = end1 - start1 prefix_len2 = end2 - start2 assert prefix_len1 == prefix_len2 prefix_len = prefix_len1 results = [] for split_offset in range(prefix_len): gen_args = MultipleCompressorArg([ca1, ca2], split_offset) spend_bundle: SpendBundle = make_spend_bundle(1) multi_gen = create_multiple_ref_generator(gen_args, spend_bundle) cost, result = run_generator(multi_gen, INFINITE_COST) results.append(result) assert result is not None assert cost > 0 assert all(r == results[0] for r in results)
async def test_tx_generator_speed(self): LARGE_BLOCK_COIN_CONSUMED_COUNT = 687 generator = large_block_generator(LARGE_BLOCK_COIN_CONSUMED_COUNT) program = SerializedProgram.from_bytes(generator) start_time = time.time() err, npc, cost = get_name_puzzle_conditions(program, False) end_time = time.time() duration = end_time - start_time assert err is None assert len(npc) == LARGE_BLOCK_COIN_CONSUMED_COUNT log.info(f"Time spent: {duration}") assert duration < 3
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(INFINITE_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( INFINITE_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, DESERIALIZE_MOD, ) generator_args = create_generator_args( [SerializedProgram.from_bytes(original_generator)]) cost, out = p_with_cses.run_with_cost(INFINITE_COST, generator_args) print() print(p_with_cses) print(out)
async def test_clvm_strict_mode(self): block = Program.from_bytes(bytes(SMALL_BLOCK_GENERATOR)) disassembly = binutils.disassemble(block) # this is a valid generator program except the first clvm # if-condition, that depends on executing an unknown operator # ("0xfe"). In strict mode, this should fail, but in non-strict # mode, the unknown operator should be treated as if it returns (). program = SerializedProgram.from_bytes( binutils.assemble( f"(i (0xfe (q . 0)) (q . ()) {disassembly})").as_bin()) error, npc_list, cost = get_name_puzzle_conditions(program, True) assert error is not None error, npc_list, cost = get_name_puzzle_conditions(program, False) assert error is None
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 batch_pre_validate_blocks( constants_dict: Dict, blocks_pickled: Dict[bytes, bytes], header_blocks_pickled: List[bytes], transaction_generators: List[Optional[bytes]], check_filter: bool, expected_difficulty: List[uint64], expected_sub_slot_iters: List[uint64], validate_transactions: bool, ) -> List[bytes]: assert len(header_blocks_pickled) == len(transaction_generators) blocks = {} for k, v in blocks_pickled.items(): blocks[k] = BlockRecord.from_bytes(v) results: List[PreValidationResult] = [] constants: ConsensusConstants = dataclass_from_dict( ConsensusConstants, constants_dict) for i in range(len(header_blocks_pickled)): try: header_block: HeaderBlock = HeaderBlock.from_bytes( header_blocks_pickled[i]) generator: Optional[bytes] = transaction_generators[i] required_iters, error = validate_finished_header_block( constants, BlockCache(blocks), header_block, check_filter, expected_difficulty[i], expected_sub_slot_iters[i], ) cost_result: Optional[CostResult] = None error_int: Optional[uint16] = None if error is not None: error_int = uint16(error.code.value) if constants_dict["NETWORK_TYPE"] == NetworkType.MAINNET.value: cost_result = None else: if not error and generator is not None and validate_transactions: cost_result = calculate_cost_of_program( SerializedProgram.from_bytes(generator), constants.CLVM_COST_RATIO_CONSTANT) results.append( PreValidationResult(error_int, required_iters, cost_result)) except Exception: error_stack = traceback.format_exc() log.error(f"Exception: {error_stack}") results.append( PreValidationResult(uint16(Err.UNKNOWN.value), None, None)) return [bytes(r) for r in results]
async def test_tx_generator_speed(self): LARGE_BLOCK_COIN_CONSUMED_COUNT = 687 generator_bytes = large_block_generator(LARGE_BLOCK_COIN_CONSUMED_COUNT) program = SerializedProgram.from_bytes(generator_bytes) start_time = time.time() generator = BlockGenerator(program, []) npc_result = get_name_puzzle_conditions(generator, False) end_time = time.time() duration = end_time - start_time assert npc_result.error is None assert len(npc_result.npc_list) == LARGE_BLOCK_COIN_CONSUMED_COUNT log.info(f"Time spent: {duration}") assert duration < 3
def best_solution_program(bundle: SpendBundle) -> SerializedProgram: """ This could potentially do a lot of clever and complicated compression optimizations in conjunction with choosing the set of SpendBundles to include. For now, we just quote the solutions we know. """ r = [] for coin_solution in bundle.coin_solutions: entry = [ [coin_solution.coin.parent_coin_info, coin_solution.coin.amount], [coin_solution.puzzle_reveal, coin_solution.solution], ] r.append(entry) return SerializedProgram.from_bytes( SExp.to((binutils.assemble("#q"), r)).as_bin())
def test_compressed_block_results(self): sb: SpendBundle = make_spend_bundle(1) start, end = match_standard_transaction_at_any_index( original_generator) ca = CompressorArg(uint32(0), SerializedProgram.from_bytes(original_generator), start, end) c = compressed_spend_bundle_solution(ca, sb) s = simple_solution_generator(sb) assert c != s cost_c, result_c = run_generator(c) cost_s, result_s = run_generator(s) print(result_c) assert result_c is not None assert result_s is not None assert result_c == result_s
def get_generator(): # args0 is generate_npc_pair_list, args1 is coin_solutions, args2 is output_list programs = args(0) coin_solutions = args(1) output_list = args(2) # coin_solution = first(coin_solutions) # coin_name = first(coin_solution) # puzzle_solution_pair = first(rest(coin_solution)) # puzzle = first(puzzle_solution_pair) # solution = first(rest(puzzle_solution_pair)) # coin_tuple = args(0, 0, 1) coin_parent = args(0, 0, 0, 1) coin_amount = args(1, 0, 0, 1) coin_puzzle = args(0, 1, 0, 1) coin_solution = args(1, 1, 0, 1) get_npc = make_list( make_list(coin_parent, sha256tree(coin_puzzle), coin_amount), eval(coin_puzzle, coin_solution)) recursive_call = eval( programs, make_list(programs, rest(coin_solutions), cons(get_npc, output_list))) generate_npc_pair_list = make_if(coin_solutions, recursive_call, output_list) # Run the block_program and enter loop over the results # args0 is generate_npc_pair_list, args1 is block_program being passed in programs = args(0) coin_solutions = args(1) execute_generate_npc_pair = eval( programs, make_list(programs, coin_solutions, sexp())) # Bootstrap the execution by passing functions in as parameters before the actual data arguments get_coinsols = eval(args(0), args(1)) core = eval(quote(execute_generate_npc_pair), make_list(quote(generate_npc_pair_list), get_coinsols)) # The below string is exactly the same as the value of 'core' above, except '(r 5)' is replaced with '13' # test = "(a (q . (a 2 (c 2 (c 5 (c () ()))))) (c (q . (a (i 5 (q . (a 2 (c 2 (c 13 (c (c (c 17 (c (a (q . (a 2 (c 2 (c 3 0)))) (c (q . (a (i (l 5) (q . (sha256 (q . 2) (a 2 (c 2 (c 9 0))) (a 2 (c 2 (c 13 0))))) (q . (sha256 (q . 1) 5))) 1)) 73)) (c (a 73 169) ()))) 11) ()))))) (q . 11)) 1)) (c (a 2 5) ())))" # noqa ret = SerializedProgram.from_bytes( bytes(Program.to(binutils.assemble(core)))) return ret
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 test_get_removals_for_single_coin(self): sb: SpendBundle = make_spend_bundle(1) start, end = match_standard_transaction_at_any_index( original_generator) ca = CompressorArg(uint32(0), SerializedProgram.from_bytes(original_generator), start, end) c = compressed_spend_bundle_solution(ca, sb) removal = sb.coin_spends[0].coin.name() error, puzzle, solution = get_puzzle_and_solution_for_coin( c, removal, INFINITE_COST) assert error is None assert bytes(puzzle) == bytes(sb.coin_spends[0].puzzle_reveal) assert bytes(solution) == bytes(sb.coin_spends[0].solution) # Test non compressed generator as well s = simple_solution_generator(sb) error, puzzle, solution = get_puzzle_and_solution_for_coin( s, removal, INFINITE_COST) assert error is None assert bytes(puzzle) == bytes(sb.coin_spends[0].puzzle_reveal) assert bytes(solution) == bytes(sb.coin_spends[0].solution)
async def test_tx_generator_speed(self, rust_checker: bool): LARGE_BLOCK_COIN_CONSUMED_COUNT = 687 generator_bytes = large_block_generator( LARGE_BLOCK_COIN_CONSUMED_COUNT) program = SerializedProgram.from_bytes(generator_bytes) start_time = time.time() generator = BlockGenerator(program, []) npc_result = get_name_puzzle_conditions( generator, test_constants.MAX_BLOCK_COST_CLVM, cost_per_byte=test_constants.COST_PER_BYTE, safe_mode=False, rust_checker=rust_checker, ) end_time = time.time() duration = end_time - start_time assert npc_result.error is None assert len(npc_result.npc_list) == LARGE_BLOCK_COIN_CONSUMED_COUNT log.info(f"Time spent: {duration}") assert duration < 1
async def create_absorb_transaction( self, farmer_record: FarmerRecord, singleton_coin: Coin, reward_coin_records: List[CoinRecord]) -> SpendBundle: # We assume that the farmer record singleton state is updated to the latest escape_inner_puzzle: Program = POOL_ESCAPING_MOD.curry( farmer_record.pool_puzzle_hash, self.relative_lock_height, bytes(farmer_record.owner_public_key), farmer_record.p2_singleton_puzzle_hash, ) committed_inner_puzzle: Program = POOL_COMMITED_MOD.curry( farmer_record.pool_puzzle_hash, escape_inner_puzzle.get_tree_hash(), farmer_record.p2_singleton_puzzle_hash, bytes(farmer_record.owner_public_key), ) aggregate_spend_bundle: SpendBundle = SpendBundle([], G2Element()) for reward_coin_record in reward_coin_records: found_block_index: Optional[uint32] = None for block_index in range( reward_coin_record.confirmed_block_index, reward_coin_record.confirmed_block_index - 100, -1): if block_index < 0: break pool_parent = pool_parent_id(uint32(block_index), self.constants.GENESIS_CHALLENGE) if pool_parent == reward_coin_record.coin.parent_coin_info: found_block_index = uint32(block_index) if not found_block_index: self.log.info( f"Received reward {reward_coin_record.coin} that is not a pool reward." ) singleton_full = SINGLETON_MOD.curry( singleton_mod_hash, farmer_record.singleton_genesis, committed_inner_puzzle) inner_sol = Program.to([ 0, singleton_full.get_tree_hash(), singleton_coin.amount, reward_coin_record.amount, found_block_index ]) full_sol = Program.to([ farmer_record.singleton_genesis, singleton_coin.amount, inner_sol ]) new_spend = SpendBundle( [ CoinSolution( singleton_coin, SerializedProgram.from_bytes(bytes(singleton_full)), full_sol) ], G2Element(), ) # TODO(pool): handle the case where the cost exceeds the size of the block aggregate_spend_bundle = SpendBundle.aggregate( [aggregate_spend_bundle, new_spend]) singleton_coin = await self.get_next_singleton_coin(new_spend) cost, result = singleton_full.run_with_cost( INFINITE_COST, full_sol) self.log.info(f"Cost: {cost}, result {result}") return aggregate_spend_bundle
async def _generate_unsigned_transaction( self, amount: uint64, newpuzzlehash: bytes32, fee: uint64 = uint64(0), origin_id: bytes32 = None, coins: Set[Coin] = None, primaries_input: Optional[List[Dict[str, Any]]] = None, ignore_max_send_amount: bool = False, announcements_to_consume: Set[Announcement] = None, ) -> List[CoinSpend]: """ Generates a unsigned transaction in form of List(Puzzle, Solutions) Note: this must be called under a wallet state manager lock """ if primaries_input is None: primaries: Optional[List[Dict]] = None total_amount = amount + fee else: primaries = primaries_input.copy() primaries_amount = 0 for prim in primaries: primaries_amount += prim["amount"] total_amount = amount + fee + primaries_amount if not ignore_max_send_amount: max_send = await self.get_max_send_amount() if total_amount > max_send: raise ValueError( f"Can't send more than {max_send} in a single transaction") if coins is None: coins = await self.select_coins(total_amount) assert len(coins) > 0 self.log.info(f"coins is not None {coins}") spend_value = sum([coin.amount for coin in coins]) change = spend_value - total_amount assert change >= 0 spends: List[CoinSpend] = [] primary_announcement_hash: Optional[bytes32] = None # Check for duplicates if primaries is not None: all_primaries_list = [ (p["puzzlehash"], p["amount"]) for p in primaries ] + [(newpuzzlehash, amount)] if len(set(all_primaries_list)) != len(all_primaries_list): raise ValueError("Cannot create two identical coins") for coin in coins: self.log.info(f"coin from coins {coin}") puzzle: Program = await self.puzzle_for_puzzle_hash( coin.puzzle_hash) # Only one coin creates outputs if primary_announcement_hash is None and origin_id in ( None, coin.name()): if primaries is None: primaries = [{ "puzzlehash": newpuzzlehash, "amount": amount }] else: primaries.append({ "puzzlehash": newpuzzlehash, "amount": amount }) if change > 0: change_puzzle_hash: bytes32 = await self.get_new_puzzlehash( ) primaries.append({ "puzzlehash": change_puzzle_hash, "amount": change }) message_list: List[bytes32] = [c.name() for c in coins] for primary in primaries: message_list.append( Coin(coin.name(), primary["puzzlehash"], primary["amount"]).name()) message: bytes32 = std_hash(b"".join(message_list)) solution: Program = self.make_solution( primaries=primaries, fee=fee, coin_announcements={message}, coin_announcements_to_assert=announcements_to_consume, ) primary_announcement_hash = Announcement(coin.name(), message).name() else: solution = self.make_solution( coin_announcements_to_assert={primary_announcement_hash}) spends.append( CoinSpend(coin, SerializedProgram.from_bytes(bytes(puzzle)), SerializedProgram.from_bytes(bytes(solution)))) self.log.info(f"Spends is {spends}") return spends
def to_sp(sexp) -> SerializedProgram: return SerializedProgram.from_bytes(bytes(sexp))
def test_serialization(self): s0 = SerializedProgram.from_bytes(b"\x00") p0 = Program.from_bytes(b"\x00") print(s0, p0)
def test_program_execution(self): p_result = SHA256TREE_MOD.run(SHA256TREE_MOD) sp = SerializedProgram.from_bytes(bytes(SHA256TREE_MOD)) cost, sp_result = sp.run_with_cost(INFINITE_COST, sp) self.assertEqual(p_result, sp_result)
def test_tree_hash(self): p = SHA256TREE_MOD s = SerializedProgram.from_bytes(bytes(SHA256TREE_MOD)) self.assertEqual(s.get_tree_hash(), p.get_tree_hash())
from typing import Dict from unittest import TestCase from chia.types.blockchain_format.program import Program, SerializedProgram from chia.types.generator_types import GeneratorBlockCacheInterface from chia.full_node.generator import create_block_generator, create_generator_args, list_to_tree from chia.util.ints import uint32 gen0 = SerializedProgram.from_bytes( bytes.fromhex( "ff01ffffffa00000000000000000000000000000000000000000000000000000000000000000ff830186a080ffffff02ffff01ff02ffff01ff02ffff03ff0bffff01ff02ffff03ffff09ff05ffff1dff0bffff1effff0bff0bffff02ff06ffff04ff02ffff04ff17ff8080808080808080ffff01ff02ff17ff2f80ffff01ff088080ff0180ffff01ff04ffff04ff04ffff04ff05ffff04ffff02ff06ffff04ff02ffff04ff17ff80808080ff80808080ffff02ff17ff2f808080ff0180ffff04ffff01ff32ff02ffff03ffff07ff0580ffff01ff0bffff0102ffff02ff06ffff04ff02ffff04ff09ff80808080ffff02ff06ffff04ff02ffff04ff0dff8080808080ffff01ff0bffff0101ff058080ff0180ff018080ffff04ffff01b081963921826355dcb6c355ccf9c2637c18adf7d38ee44d803ea9ca41587e48c913d8d46896eb830aeadfc13144a8eac3ff018080ffff80ffff01ffff33ffa06b7a83babea1eec790c947db4464ab657dbe9b887fe9acc247062847b8c2a8a9ff830186a08080ff8080808080" # noqa )) gen1 = SerializedProgram.from_bytes( bytes.fromhex( "ff01ffffffa00000000000000000000000000000000000000000000000000000000000000000ff830186a080ffffff02ffff01ff02ffff01ff02ffff03ff0bffff01ff02ffff03ffff09ff05ffff1dff0bffff1effff0bff0bffff02ff06ffff04ff02ffff04ff17ff8080808080808080ffff01ff02ff17ff2f80ffff01ff088080ff0180ffff01ff04ffff04ff04ffff04ff05ffff04ffff02ff06ffff04ff02ffff04ff17ff80808080ff80808080ffff02ff17ff2f808080ff0180ffff04ffff01ff32ff02ffff03ffff07ff0580ffff01ff0bffff0102ffff02ff06ffff04ff02ffff04ff09ff80808080ffff02ff06ffff04ff02ffff04ff0dff8080808080ffff01ff0bffff0101ff058080ff0180ff018080ffff04ffff01b081963921826355dcb6c355ccf9c2637c18adf7d38ee44d803ea9ca41587e48c913d8d46896eb830aeadfc13144a8eac3ff018080ffff80ffff01ffff33ffa06b7a83babea1eec790c947db4464ab657dbe9b887fe9acc247062847b8c2a8a9ff830186a08080ff8080808080" # noqa )) gen2 = SerializedProgram.from_bytes( bytes.fromhex( "ff01ffffffa00000000000000000000000000000000000000000000000000000000000000000ff830186a080ffffff02ffff01ff02ffff01ff02ffff03ff0bffff01ff02ffff03ffff09ff05ffff1dff0bffff1effff0bff0bffff02ff06ffff04ff02ffff04ff17ff8080808080808080ffff01ff02ff17ff2f80ffff01ff088080ff0180ffff01ff04ffff04ff04ffff04ff05ffff04ffff02ff06ffff04ff02ffff04ff17ff80808080ff80808080ffff02ff17ff2f808080ff0180ffff04ffff01ff32ff02ffff03ffff07ff0580ffff01ff0bffff0102ffff02ff06ffff04ff02ffff04ff09ff80808080ffff02ff06ffff04ff02ffff04ff0dff8080808080ffff01ff0bffff0101ff058080ff0180ff018080ffff04ffff01b081963921826355dcb6c355ccf9c2637c18adf7d38ee44d803ea9ca41587e48c913d8d46896eb830aeadfc13144a8eac3ff018080ffff80ffff01ffff33ffa06b7a83babea1eec790c947db4464ab657dbe9b887fe9acc247062847b8c2a8a9ff830186a08080ff8080808080" # noqa )) class BlockDict(GeneratorBlockCacheInterface): def __init__(self, d: Dict[uint32, SerializedProgram]): self.d = d def get_generator_for_block_height(self, index: uint32) -> SerializedProgram: return self.d[index]