def test_only_odd_coins_0(): blocks = initial_blocks() farmed_coin = list(blocks[-1].get_included_reward_coins())[0] metadata = [("foo", "bar")] ANYONE_CAN_SPEND_PUZZLE = Program.to(1) launcher_amount = uint64(1) launcher_puzzle = LAUNCHER_PUZZLE launcher_puzzle_hash = launcher_puzzle.get_tree_hash() initial_singleton_puzzle = adaptor_for_singleton_inner_puzzle( ANYONE_CAN_SPEND_PUZZLE) lineage_proof, launcher_id, condition_list, launcher_spend_bundle = launcher_conditions_and_spend_bundle( farmed_coin.name(), launcher_amount, initial_singleton_puzzle, metadata, launcher_puzzle) conditions = Program.to(condition_list) coin_spend = CoinSpend(farmed_coin, ANYONE_CAN_SPEND_PUZZLE, conditions) spend_bundle = SpendBundle.aggregate( [launcher_spend_bundle, SpendBundle([coin_spend], G2Element())]) run = asyncio.get_event_loop().run_until_complete coins_added, coins_removed = run( check_spend_bundle_validity(bt.constants, blocks, spend_bundle)) coin_set_added = set([_.coin for _ in coins_added]) coin_set_removed = set([_.coin for _ in coins_removed]) launcher_coin = launcher_spend_bundle.coin_spends[0].coin assert launcher_coin in coin_set_added assert launcher_coin in coin_set_removed assert farmed_coin in coin_set_removed # breakpoint() singleton_expected_puzzle_hash = singleton_puzzle_hash( launcher_id, launcher_puzzle_hash, initial_singleton_puzzle) expected_singleton_coin = Coin(launcher_coin.name(), singleton_expected_puzzle_hash, launcher_amount) assert expected_singleton_coin in coin_set_added # next up: spend the expected_singleton_coin # it's an adapted `ANYONE_CAN_SPEND_PUZZLE` # then try a bad lineage proof # then try writing two odd coins # then try writing zero odd coins # then, destroy the singleton with the -113 hack return 0
def spend_to_delayed_puzzle( p2_singleton_coin: Coin, output_amount: uint64, launcher_id: bytes32, delay_time: uint64, delay_ph: bytes32, ) -> CoinSpend: claim_coinsol = CoinSpend( p2_singleton_coin, pay_to_singleton_or_delay_puzzle(launcher_id, delay_time, delay_ph), solution_for_p2_delayed_puzzle(output_amount), ) return claim_coinsol
def spend_coin_to_singleton( puzzle_db: PuzzleDB, launcher_puzzle: Program, coin_store: CoinStore, now: CoinTimestamp) -> Tuple[List[Coin], List[CoinSpend]]: farmed_coin_amount = 100000 metadata = [("foo", "bar")] now = CoinTimestamp(10012300, 1) farmed_coin = coin_store.farm_coin(ANYONE_CAN_SPEND_PUZZLE.get_tree_hash(), now, amount=farmed_coin_amount) now.seconds += 500 now.height += 1 launcher_amount: uint64 = uint64(1) launcher_puzzle = LAUNCHER_PUZZLE launcher_puzzle_hash = launcher_puzzle.get_tree_hash() initial_singleton_puzzle = adaptor_for_singleton_inner_puzzle( ANYONE_CAN_SPEND_PUZZLE) launcher_id, condition_list, launcher_spend_bundle = launcher_conditions_and_spend_bundle( puzzle_db, farmed_coin.name(), launcher_amount, initial_singleton_puzzle, metadata, launcher_puzzle) conditions = Program.to(condition_list) coin_spend = CoinSpend(farmed_coin, ANYONE_CAN_SPEND_PUZZLE, conditions) spend_bundle = SpendBundle.aggregate( [launcher_spend_bundle, SpendBundle([coin_spend], G2Element())]) additions, removals = coin_store.update_coin_store_for_spend_bundle( spend_bundle, now, MAX_BLOCK_COST_CLVM, COST_PER_BYTE) launcher_coin = launcher_spend_bundle.coin_spends[0].coin assert_coin_spent(coin_store, launcher_coin) assert_coin_spent(coin_store, farmed_coin) # TODO: address hint error and remove ignore # error: Argument 1 to "singleton_puzzle" has incompatible type "bytes32"; expected "Program" [arg-type] singleton_expected_puzzle = singleton_puzzle( launcher_id, # type: ignore[arg-type] launcher_puzzle_hash, initial_singleton_puzzle, ) singleton_expected_puzzle_hash = singleton_expected_puzzle.get_tree_hash() expected_singleton_coin = Coin(launcher_coin.name(), singleton_expected_puzzle_hash, launcher_amount) assert_coin_spent(coin_store, expected_singleton_coin, is_spent=False) return additions, removals
def _row_to_farmer_record(row) -> FarmerRecord: return FarmerRecord( bytes.fromhex(row[0]), bytes.fromhex(row[1]), row[2], bytes.fromhex(row[3]), G1Element.from_bytes(bytes.fromhex(row[4])), CoinSpend.from_bytes(row[5]), PoolState.from_bytes(row[6]), row[7], row[8], row[9], True if row[10] == 1 else False, )
def make_spend_bundle(count: int) -> SpendBundle: puzzle_hash_db: Dict = dict() coins = [make_fake_coin(_, puzzle_hash_db) for _ in range(count)] coin_spends = [] for coin in coins: puzzle_reveal = puzzle_hash_db[coin.puzzle_hash] conditions = conditions_for_payment(coin) solution = solution_for_conditions(conditions) coin_spend = CoinSpend(coin, puzzle_reveal, solution) coin_spends.append(coin_spend) spend_bundle = SpendBundle(coin_spends, blspy.G2Element()) return spend_bundle
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, self.service.defaults.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)
async def check_conditions(condition_solution: Program, expected_err: Optional[Err] = None, spend_reward_index: int = -2): blocks = initial_blocks() coin = list(blocks[spend_reward_index].get_included_reward_coins())[0] coin_spend = CoinSpend(coin, EASY_PUZZLE, condition_solution) spend_bundle = SpendBundle([coin_spend], G2Element()) # now let's try to create a block with the spend bundle and ensure that it doesn't validate await check_spend_bundle_validity(bt.constants, blocks, spend_bundle, expected_err=expected_err)
async def rebuild_cache(self) -> None: """ This resets the cache, and loads all entries from the DB. Any entries in the cache that were not committed are removed. This can happen if a state transition in wallet_blockchain fails. """ cursor = await self.db_connection.execute("SELECT * FROM pool_state_transitions ORDER BY transition_index") rows = await cursor.fetchall() await cursor.close() self._state_transitions_cache = {} for row in rows: _, wallet_id, height, coin_spend_bytes = row coin_spend: CoinSpend = CoinSpend.from_bytes(coin_spend_bytes) if wallet_id not in self._state_transitions_cache: self._state_transitions_cache[wallet_id] = [] self._state_transitions_cache[wallet_id].append((height, coin_spend))
async def launch_smart_coin(self, source, **kwargs) -> CoinWrapper: """Create a new smart coin based on a parent coin and return the smart coin's living coin to the user or None if the spend failed.""" amt = 1 if 'amt' in kwargs: amt = kwargs['amt'] found_coin = await self.choose_coin(amt) if found_coin is None: raise ValueError( f'could not find available coin containing {amt} mojo') # Create a puzzle based on the incoming smart coin cw = ContractWrapper(DEFAULT_CONSTANTS.GENESIS_CHALLENGE, source) condition_args = [ [ConditionOpcode.CREATE_COIN, cw.puzzle_hash(), amt], ] if amt < found_coin.amount: condition_args.append([ ConditionOpcode.CREATE_COIN, self.puzzle_hash, found_coin.amount - amt ]) delegated_puzzle_solution = Program.to((1, condition_args)) solution = Program.to([[], delegated_puzzle_solution, []]) # Sign the (delegated_puzzle_hash + coin_name) with synthetic secret key signature = AugSchemeMPL.sign( calculate_synthetic_secret_key(self.sk_, DEFAULT_HIDDEN_PUZZLE_HASH), (delegated_puzzle_solution.get_tree_hash() + found_coin.name() + DEFAULT_CONSTANTS.AGG_SIG_ME_ADDITIONAL_DATA)) spend_bundle = SpendBundle( [ CoinSpend( found_coin.as_coin(), # Coin to spend self.puzzle, # Puzzle used for found_coin solution, # The solution to the puzzle locking found_coin ) ], signature) pushed = await self.parent.push_tx(spend_bundle) if 'error' not in pushed: return cw.custom_coin(found_coin, amt) else: return None
def do_test_spend( puzzle_reveal: Program, solution: Program, payments: Iterable[Tuple[bytes32, int]], key_lookup: KeyTool, farm_time: CoinTimestamp = T1, spend_time: CoinTimestamp = T2, ) -> SpendBundle: """ This method will farm a coin paid to the hash of `puzzle_reveal`, then try to spend it with `solution`, and verify that the created coins correspond to `payments`. The `key_lookup` is used to create a signed version of the `SpendBundle`, although at this time, signatures are not verified. """ coin_db = CoinStore() puzzle_hash = puzzle_reveal.get_tree_hash() # farm it coin = coin_db.farm_coin(puzzle_hash, farm_time) # spend it coin_spend = CoinSpend(coin, puzzle_reveal, solution) spend_bundle = SpendBundle([coin_spend], G2Element()) coin_db.update_coin_store_for_spend_bundle(spend_bundle, spend_time, MAX_BLOCK_COST_CLVM, COST_PER_BYTE) # ensure all outputs are there for puzzle_hash, amount in payments: for coin in coin_db.coins_for_puzzle_hash(puzzle_hash): if coin.amount == amount: break else: assert 0 # make sure we can actually sign the solution signatures = [] for coin_spend in spend_bundle.coin_spends: signature = key_lookup.signature_for_solution(coin_spend, bytes([2] * 32)) signatures.append(signature) return SpendBundle(spend_bundle.coin_spends, AugSchemeMPL.aggregate(signatures))
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) # TODO: address hint error and remove ignore # error: Argument 1 to "singleton_puzzle" has incompatible type "bytes32"; expected "Program" [arg-type] singleton_full_puzzle = singleton_puzzle( launcher_coin.name(), # type: ignore[arg-type] 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 test_version_override(self): coin_spend = CoinSpend( COIN, OFFER_MOD, SOLUTION, ) spend_bundle = SpendBundle([coin_spend], G2Element()) compressed = compress_object_with_puzzles(bytes(spend_bundle), LATEST_VERSION) compressed_earlier = compress_object_with_puzzles( bytes(spend_bundle), 1) assert len(bytes(spend_bundle)) > len(bytes(compressed)) assert spend_bundle == SpendBundle.from_bytes( decompress_object_with_puzzles(compressed)) assert spend_bundle == SpendBundle.from_bytes( decompress_object_with_puzzles(compressed_earlier)) assert len(bytes(compressed_earlier)) > len(bytes(compressed))
def create_standard_spend(self, priv, conditions): delegated_puzzle_solution = Program.to((1, conditions)) solution = Program.to([[], delegated_puzzle_solution, []]) coin_solution_object = CoinSpend( self.as_coin(), self.puzzle(), solution, ) # Create a signature for each of these. We'll aggregate them at the end. signature = AugSchemeMPL.sign( calculate_synthetic_secret_key(priv, DEFAULT_HIDDEN_PUZZLE_HASH), (delegated_puzzle_solution.get_tree_hash() + self.name() + DEFAULT_CONSTANTS.AGG_SIG_ME_ADDITIONAL_DATA)) return coin_solution_object, signature
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()
async def create_spend_bundle_relative_chia( self, chia_amount: int, exclude: List[Coin]) -> SpendBundle: list_of_solutions = [] utxos = None # If we're losing value then get coins with at least that much value # If we're gaining value then our amount doesn't matter if chia_amount < 0: utxos = await self.select_coins(abs(chia_amount), exclude) else: utxos = await self.select_coins(0, exclude) assert len(utxos) > 0 # Calculate output amount given sum of utxos spend_value = sum([coin.amount for coin in utxos]) chia_amount = spend_value + chia_amount # Create coin solutions for each utxo output_created = None for coin in utxos: puzzle = await self.puzzle_for_puzzle_hash(coin.puzzle_hash) if output_created is None: newpuzhash = await self.get_new_puzzlehash() primaries: List[AmountWithPuzzlehash] = [{ "puzzlehash": newpuzhash, "amount": uint64(chia_amount), "memos": [] }] solution = self.make_solution(primaries=primaries) output_created = coin list_of_solutions.append(CoinSpend(coin, puzzle, solution)) await self.hack_populate_secret_keys_for_coin_spends(list_of_solutions) spend_bundle = await sign_coin_spends( list_of_solutions, self.secret_key_store.secret_key_for_public_key, self.wallet_state_manager.constants.AGG_SIG_ME_ADDITIONAL_DATA, self.wallet_state_manager.constants.MAX_BLOCK_COST_CLVM, ) return spend_bundle
async def rl_generate_unsigned_transaction(self, to_puzzlehash, amount, fee) -> List[CoinSpend]: spends = [] assert self.rl_coin_record is not None coin = self.rl_coin_record.coin puzzle_hash = coin.puzzle_hash pubkey = self.rl_info.user_pubkey rl_parent: Optional[Coin] = await self._get_rl_parent() if rl_parent is None: raise ValueError("No RL parent coin") # these lines make mypy happy assert pubkey is not None assert self.rl_info.limit is not None assert self.rl_info.interval is not None assert self.rl_info.rl_origin_id is not None assert self.rl_info.admin_pubkey is not None puzzle = rl_puzzle_for_pk( bytes(pubkey), self.rl_info.limit, self.rl_info.interval, self.rl_info.rl_origin_id, self.rl_info.admin_pubkey, ) solution = solution_for_rl( coin.parent_coin_info, puzzle_hash, coin.amount, to_puzzlehash, amount, rl_parent.parent_coin_info, rl_parent.amount, self.rl_info.interval, self.rl_info.limit, fee, ) spends.append(CoinSpend(coin, puzzle, solution)) return spends
def generate_unsigned_clawback_transaction(self, clawback_coin: Coin, clawback_puzzle_hash: bytes32, fee): if (self.rl_info.limit is None or self.rl_info.interval is None or self.rl_info.user_pubkey is None or self.rl_info.admin_pubkey is None): raise ValueError("One ore more of the elements of rl_info is None") spends = [] coin = clawback_coin if self.rl_info.rl_origin is None: raise ValueError("Origin not initialized") puzzle = rl_puzzle_for_pk( self.rl_info.user_pubkey, self.rl_info.limit, self.rl_info.interval, self.rl_info.rl_origin.name(), self.rl_info.admin_pubkey, ) solution = make_clawback_solution(clawback_puzzle_hash, clawback_coin.amount, fee) spends.append((puzzle, CoinSpend(coin, puzzle, solution))) return spends
def launch_conditions_and_coinsol( coin: Coin, inner_puzzle: Program, comment: List[Tuple[str, str]], amount: uint64, ) -> Tuple[List[Program], CoinSpend]: if (amount % 2) == 0: raise ValueError("Coin amount cannot be even. Subtract one mojo.") launcher_coin: Coin = generate_launcher_coin(coin, amount) curried_singleton: Program = SINGLETON_MOD.curry( (SINGLETON_MOD_HASH, (launcher_coin.name(), SINGLETON_LAUNCHER_HASH)), inner_puzzle, ) launcher_solution = Program.to([ curried_singleton.get_tree_hash(), amount, comment, ]) create_launcher = Program.to([ ConditionOpcode.CREATE_COIN, SINGLETON_LAUNCHER_HASH, amount, ], ) assert_launcher_announcement = Program.to([ ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT, std_hash(launcher_coin.name() + launcher_solution.get_tree_hash()), ], ) conditions = [create_launcher, assert_launcher_announcement] launcher_coin_spend = CoinSpend( launcher_coin, SINGLETON_LAUNCHER, launcher_solution, ) return conditions, launcher_coin_spend
async def get_puzzle_and_solution(self, request: Dict) -> Optional[Dict]: coin_name: bytes32 = bytes32.from_hexstr(request["coin_id"]) height = request["height"] coin_record = await self.service.coin_store.get_coin_record(coin_name) if coin_record is None or not coin_record.spent or coin_record.spent_block_index != height: raise ValueError( f"Invalid height {height}. coin record {coin_record}") header_hash = self.service.blockchain.height_to_hash(height) # TODO: address hint error and remove ignore # error: Argument 1 to "get_full_block" of "BlockStore" has incompatible type "Optional[bytes32]"; # expected "bytes32" [arg-type] block: Optional[ FullBlock] = await self.service.block_store.get_full_block( header_hash) # type: ignore[arg-type] # noqa: E501 if block is None or block.transactions_generator is None: raise ValueError("Invalid block or block generator") block_generator: Optional[ BlockGenerator] = await self.service.blockchain.get_block_generator( block) assert block_generator is not None error, puzzle, solution = get_puzzle_and_solution_for_coin( block_generator, coin_name, self.service.constants.MAX_BLOCK_COST_CLVM) if error is not None: raise ValueError(f"Error: {error}") puzzle_ser: SerializedProgram = SerializedProgram.from_program( Program.to(puzzle)) solution_ser: SerializedProgram = SerializedProgram.from_program( Program.to(solution)) return { "coin_solution": CoinSpend(coin_record.coin, puzzle_ser, solution_ser) }
def to_valid_spend(self, arbitrage_ph: Optional[bytes32] = None) -> SpendBundle: if not self.is_valid(): raise ValueError("Offer is currently incomplete") completion_spends: List[CoinSpend] = [] for tail_hash, payments in self.requested_payments.items(): offered_coins: List[Coin] = self.get_offered_coins()[tail_hash] # Because of CAT supply laws, we must specify a place for the leftovers to go arbitrage_amount: int = self.arbitrage()[tail_hash] all_payments: List[NotarizedPayment] = payments.copy() if arbitrage_amount > 0: assert arbitrage_amount is not None assert arbitrage_ph is not None all_payments.append( NotarizedPayment(arbitrage_ph, uint64(arbitrage_amount), [])) for coin in offered_coins: inner_solutions = [] if coin == offered_coins[0]: nonces: List[bytes32] = [p.nonce for p in all_payments] for nonce in list(dict.fromkeys( nonces)): # dedup without messing with order nonce_payments: List[NotarizedPayment] = list( filter(lambda p: p.nonce == nonce, all_payments)) inner_solutions.append( (nonce, [np.as_condition_args() for np in nonce_payments])) if tail_hash: # CATs have a special way to be solved so we have to do some calculation before getting the solution parent_spend: CoinSpend = list( filter( lambda cs: cs.coin.name() == coin.parent_coin_info, self.bundle.coin_spends))[0] parent_coin: Coin = parent_spend.coin matched, curried_args = match_cat_puzzle( parent_spend.puzzle_reveal.to_program()) assert matched _, _, inner_puzzle = curried_args spendable_cat = SpendableCAT( coin, tail_hash, OFFER_MOD, Program.to(inner_solutions), lineage_proof=LineageProof( parent_coin.parent_coin_info, inner_puzzle.get_tree_hash(), parent_coin.amount), ) solution: Program = ( unsigned_spend_bundle_for_spendable_cats( CAT_MOD, [spendable_cat ]).coin_spends[0].solution.to_program()) else: solution = Program.to(inner_solutions) completion_spends.append( CoinSpend( coin, construct_cat_puzzle(CAT_MOD, tail_hash, OFFER_MOD) if tail_hash else OFFER_MOD, solution, )) return SpendBundle.aggregate( [SpendBundle(completion_spends, G2Element()), self.bundle])
def generate_unsigned_transaction( self, amount: uint64, new_puzzle_hash: bytes32, coins: List[Coin], condition_dic: Dict[ConditionOpcode, List[ConditionWithArgs]], fee: int = 0, secret_key: Optional[PrivateKey] = None, additional_outputs: Optional[List[Tuple[bytes32, int]]] = None, ) -> List[CoinSpend]: spends = [] spend_value = sum([c.amount for c in coins]) if ConditionOpcode.CREATE_COIN not in condition_dic: condition_dic[ConditionOpcode.CREATE_COIN] = [] if ConditionOpcode.CREATE_COIN_ANNOUNCEMENT not in condition_dic: condition_dic[ConditionOpcode.CREATE_COIN_ANNOUNCEMENT] = [] output = ConditionWithArgs( ConditionOpcode.CREATE_COIN, [new_puzzle_hash, int_to_bytes(amount)]) condition_dic[output.opcode].append(output) if additional_outputs is not None: for o in additional_outputs: out = ConditionWithArgs(ConditionOpcode.CREATE_COIN, [o[0], int_to_bytes(o[1])]) condition_dic[out.opcode].append(out) amount_total = sum( int_from_bytes(cvp.vars[1]) for cvp in condition_dic[ConditionOpcode.CREATE_COIN]) change = spend_value - amount_total - fee if change > 0: change_puzzle_hash = self.get_new_puzzlehash() change_output = ConditionWithArgs( ConditionOpcode.CREATE_COIN, [change_puzzle_hash, int_to_bytes(change)]) condition_dic[output.opcode].append(change_output) secondary_coins_cond_dic: Dict[ConditionOpcode, List[ConditionWithArgs]] = dict() secondary_coins_cond_dic[ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT] = [] for n, coin in enumerate(coins): puzzle_hash = coin.puzzle_hash if secret_key is None: secret_key = self.get_private_key_for_puzzle_hash(puzzle_hash) pubkey = secret_key.get_g1() puzzle = puzzle_for_pk(bytes(pubkey)) if n == 0: message_list = [c.name() for c in coins] for outputs in condition_dic[ConditionOpcode.CREATE_COIN]: message_list.append( Coin(coin.name(), outputs.vars[0], int_from_bytes(outputs.vars[1])).name()) message = std_hash(b"".join(message_list)) condition_dic[ConditionOpcode.CREATE_COIN_ANNOUNCEMENT].append( ConditionWithArgs(ConditionOpcode.CREATE_COIN_ANNOUNCEMENT, [message])) primary_announcement_hash = Announcement(coin.name(), message).name() secondary_coins_cond_dic[ ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT].append( ConditionWithArgs( ConditionOpcode.ASSERT_COIN_ANNOUNCEMENT, [primary_announcement_hash])) main_solution = self.make_solution(condition_dic) spends.append(CoinSpend(coin, puzzle, main_solution)) else: spends.append( CoinSpend(coin, puzzle, self.make_solution(secondary_coins_cond_dic))) return spends
async def test_cat_mod(self, setup_sim): sim, sim_client = setup_sim try: tail = Program.to([]) checker_solution = Program.to([]) cat_puzzle: Program = construct_cat_puzzle(CAT_MOD, tail.get_tree_hash(), acs) cat_ph: bytes32 = cat_puzzle.get_tree_hash() await sim.farm_block(cat_ph) starting_coin: Coin = (await sim_client.get_coin_records_by_puzzle_hash(cat_ph))[0].coin # Testing the eve spend await self.do_spend( sim, sim_client, tail, [starting_coin], [NO_LINEAGE_PROOF], [ Program.to( [ [51, acs.get_tree_hash(), starting_coin.amount - 3, [b"memo"]], [51, acs.get_tree_hash(), 1], [51, acs.get_tree_hash(), 2], [51, 0, -113, tail, checker_solution], ] ) ], (MempoolInclusionStatus.SUCCESS, None), limitations_solutions=[checker_solution], cost_str="Eve Spend", ) # There's 4 total coins at this point. A farming reward and the three children of the spend above. # Testing a combination of two coins: List[Coin] = [ record.coin for record in (await sim_client.get_coin_records_by_puzzle_hash(cat_ph, include_spent_coins=False)) ] coins = [coins[0], coins[1]] await self.do_spend( sim, sim_client, tail, coins, [NO_LINEAGE_PROOF] * 2, [ Program.to( [ [51, acs.get_tree_hash(), coins[0].amount + coins[1].amount], [51, 0, -113, tail, checker_solution], ] ), Program.to([[51, 0, -113, tail, checker_solution]]), ], (MempoolInclusionStatus.SUCCESS, None), limitations_solutions=[checker_solution] * 2, cost_str="Two CATs", ) # Testing a combination of three coins = [ record.coin for record in (await sim_client.get_coin_records_by_puzzle_hash(cat_ph, include_spent_coins=False)) ] total_amount: uint64 = uint64(sum([c.amount for c in coins])) await self.do_spend( sim, sim_client, tail, coins, [NO_LINEAGE_PROOF] * 3, [ Program.to( [ [51, acs.get_tree_hash(), total_amount], [51, 0, -113, tail, checker_solution], ] ), Program.to([[51, 0, -113, tail, checker_solution]]), Program.to([[51, 0, -113, tail, checker_solution]]), ], (MempoolInclusionStatus.SUCCESS, None), limitations_solutions=[checker_solution] * 3, cost_str="Three CATs", ) # Spend with a standard lineage proof parent_coin: Coin = coins[0] # The first one is the one we didn't light on fire _, curried_args = cat_puzzle.uncurry() _, _, innerpuzzle = curried_args.as_iter() lineage_proof = LineageProof(parent_coin.parent_coin_info, innerpuzzle.get_tree_hash(), parent_coin.amount) 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], [lineage_proof], [Program.to([[51, acs.get_tree_hash(), total_amount]])], (MempoolInclusionStatus.SUCCESS, None), reveal_limitations_program=False, cost_str="Standard Lineage Check", ) # Melt some value 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(), total_amount - 1], [51, 0, -113, tail, checker_solution], ] ) ], (MempoolInclusionStatus.SUCCESS, None), extra_deltas=[-1], limitations_solutions=[checker_solution], cost_str="Melting Value", ) # Mint some value temp_p = Program.to(1) temp_ph: bytes32 = temp_p.get_tree_hash() await sim.farm_block(temp_ph) acs_coin: Coin = (await sim_client.get_coin_records_by_puzzle_hash(temp_ph, include_spent_coins=False))[ 0 ].coin acs_bundle = SpendBundle( [ CoinSpend( acs_coin, temp_p, Program.to([]), ) ], G2Element(), ) 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(), total_amount], [51, 0, -113, tail, checker_solution], ] ) ], # We subtracted 1 last time so it's normal now (MempoolInclusionStatus.SUCCESS, None), extra_deltas=[1], additional_spends=[acs_bundle], limitations_solutions=[checker_solution], cost_str="Mint Value", ) finally: await sim.close()
async def test_singleton_top_layer(self): try: # START TESTS # Generate starting info key_lookup = KeyTool() pk: G1Element = public_key_for_index(1, key_lookup) starting_puzzle: Program = p2_delegated_puzzle_or_hidden_puzzle.puzzle_for_pk( pk) # noqa adapted_puzzle: Program = singleton_top_layer.adapt_inner_to_singleton( starting_puzzle) # noqa adapted_puzzle_hash: bytes32 = adapted_puzzle.get_tree_hash() # Get our starting standard coin created START_AMOUNT: uint64 = 1023 sim = await SpendSim.create() sim_client = SimClient(sim) await sim.farm_block(starting_puzzle.get_tree_hash()) starting_coin: Coin = await sim_client.get_coin_records_by_puzzle_hash( starting_puzzle.get_tree_hash()) starting_coin = starting_coin[0].coin comment: List[Tuple[str, str]] = [("hello", "world")] # LAUNCHING # Try to create an even singleton (driver test) try: conditions, launcher_coinsol = singleton_top_layer.launch_conditions_and_coinsol( # noqa starting_coin, adapted_puzzle, comment, (START_AMOUNT - 1)) raise AssertionError("This should fail due to an even amount") except ValueError as msg: assert str( msg) == "Coin amount cannot be even. Subtract one mojo." conditions, launcher_coinsol = singleton_top_layer.launch_conditions_and_coinsol( # noqa starting_coin, adapted_puzzle, comment, START_AMOUNT) # Creating solution for standard transaction delegated_puzzle: Program = p2_conditions.puzzle_for_conditions( conditions) # noqa full_solution: Program = p2_delegated_puzzle_or_hidden_puzzle.solution_for_conditions( conditions) # noqa starting_coinsol = CoinSpend( starting_coin, starting_puzzle, full_solution, ) await self.make_and_spend_bundle( sim, sim_client, starting_coin, delegated_puzzle, [starting_coinsol, launcher_coinsol], ) # EVE singleton_eve: Coin = (await sim.all_non_reward_coins())[0] launcher_coin: Coin = singleton_top_layer.generate_launcher_coin( starting_coin, START_AMOUNT, ) launcher_id: bytes32 = launcher_coin.name() # This delegated puzzle just recreates the coin exactly delegated_puzzle: Program = Program.to(( 1, [[ ConditionOpcode.CREATE_COIN, adapted_puzzle_hash, singleton_eve.amount, ]], )) inner_solution: Program = Program.to([[], delegated_puzzle, []]) # Generate the lineage proof we will need from the launcher coin lineage_proof: LineageProof = singleton_top_layer.lineage_proof_for_coinsol( launcher_coinsol) # noqa puzzle_reveal: Program = singleton_top_layer.puzzle_for_singleton( launcher_id, adapted_puzzle, ) full_solution: Program = singleton_top_layer.solution_for_singleton( lineage_proof, singleton_eve.amount, inner_solution, ) singleton_eve_coinsol = CoinSpend( singleton_eve, puzzle_reveal, full_solution, ) await self.make_and_spend_bundle( sim, sim_client, singleton_eve, delegated_puzzle, [singleton_eve_coinsol], ) # POST-EVE singleton: Coin = (await sim.all_non_reward_coins())[0] # Same delegated_puzzle / inner_solution. We're just recreating ourself lineage_proof: LineageProof = singleton_top_layer.lineage_proof_for_coinsol( singleton_eve_coinsol) # noqa # Same puzzle_reveal too full_solution: Program = singleton_top_layer.solution_for_singleton( lineage_proof, singleton.amount, inner_solution, ) singleton_coinsol = CoinSpend( singleton, puzzle_reveal, full_solution, ) await self.make_and_spend_bundle( sim, sim_client, singleton, delegated_puzzle, [singleton_coinsol], ) # CLAIM A P2_SINGLETON singleton_child: Coin = (await sim.all_non_reward_coins())[0] p2_singleton_puz: Program = singleton_top_layer.pay_to_singleton_puzzle( launcher_id) p2_singleton_ph: bytes32 = p2_singleton_puz.get_tree_hash() ARBITRARY_AMOUNT: uint64 = 1379 await sim.farm_block(p2_singleton_ph) p2_singleton_coin: Coin = await sim_client.get_coin_records_by_puzzle_hash( p2_singleton_ph) p2_singleton_coin = p2_singleton_coin[0].coin assertion, announcement, claim_coinsol = singleton_top_layer.claim_p2_singleton( p2_singleton_coin, adapted_puzzle_hash, launcher_id, ) delegated_puzzle: Program = Program.to(( 1, [ [ ConditionOpcode.CREATE_COIN, adapted_puzzle_hash, singleton_eve.amount ], assertion, announcement, ], )) inner_solution: Program = Program.to([[], delegated_puzzle, []]) lineage_proof: LineageProof = singleton_top_layer.lineage_proof_for_coinsol( singleton_coinsol) puzzle_reveal: Program = singleton_top_layer.puzzle_for_singleton( launcher_id, adapted_puzzle, ) full_solution: Program = singleton_top_layer.solution_for_singleton( lineage_proof, singleton_eve.amount, inner_solution, ) singleton_claim_coinsol = CoinSpend( singleton_child, puzzle_reveal, full_solution, ) await self.make_and_spend_bundle( sim, sim_client, singleton_child, delegated_puzzle, [singleton_claim_coinsol, claim_coinsol]) # CLAIM A P2_SINGLETON_OR_DELAYED singleton_child: Coin = (await sim.all_non_reward_coins())[0] DELAY_TIME: uint64 = 1 DELAY_PH: bytes32 = adapted_puzzle_hash p2_singleton_puz: Program = singleton_top_layer.pay_to_singleton_or_delay_puzzle( launcher_id, DELAY_TIME, DELAY_PH, ) p2_singleton_ph: bytes32 = p2_singleton_puz.get_tree_hash() ARBITRARY_AMOUNT: uint64 = 1379 await sim.farm_block(p2_singleton_ph) p2_singleton_coin: Coin = await sim_client.get_coin_records_by_puzzle_hash( p2_singleton_ph) p2_singleton_coin = p2_singleton_coin[0].coin assertion, announcement, claim_coinsol = singleton_top_layer.claim_p2_singleton( p2_singleton_coin, adapted_puzzle_hash, launcher_id, DELAY_TIME, DELAY_PH, ) delegated_puzzle: Program = Program.to(( 1, [ [ ConditionOpcode.CREATE_COIN, adapted_puzzle_hash, singleton_eve.amount ], assertion, announcement, ], )) inner_solution: Program = Program.to([[], delegated_puzzle, []]) lineage_proof: LineageProof = singleton_top_layer.lineage_proof_for_coinsol( singleton_claim_coinsol) puzzle_reveal: Program = singleton_top_layer.puzzle_for_singleton( launcher_id, adapted_puzzle, ) full_solution: Program = singleton_top_layer.solution_for_singleton( lineage_proof, singleton_eve.amount, inner_solution, ) delay_claim_coinsol = CoinSpend( singleton_child, puzzle_reveal, full_solution, ) # Save the height so we can rewind after this save_height: uint64 = ( sim.get_height() ) # The last coin solution before this point is singleton_claim_coinsol await self.make_and_spend_bundle( sim, sim_client, singleton_child, delegated_puzzle, [delay_claim_coinsol, claim_coinsol]) # TRY TO SPEND AWAY TOO SOON (Negative Test) await sim.rewind(save_height) to_delay_ph_coinsol: CoinSpend = singleton_top_layer.spend_to_delayed_puzzle( p2_singleton_coin, ARBITRARY_AMOUNT, launcher_id, DELAY_TIME, DELAY_PH, ) result, error = await sim_client.push_tx( SpendBundle([to_delay_ph_coinsol], G2Element())) assert error == Err.ASSERT_SECONDS_RELATIVE_FAILED # SPEND TO DELAYED PUZZLE HASH await sim.rewind(save_height) sim.pass_time(10000005) sim.pass_blocks(100) await sim_client.push_tx( SpendBundle([to_delay_ph_coinsol], G2Element())) # CREATE MULTIPLE ODD CHILDREN (Negative Test) singleton_child: Coin = (await sim.all_non_reward_coins())[0] delegated_puzzle: Program = Program.to(( 1, [ [ConditionOpcode.CREATE_COIN, adapted_puzzle_hash, 3], [ConditionOpcode.CREATE_COIN, adapted_puzzle_hash, 7], ], )) inner_solution: Program = Program.to([[], delegated_puzzle, []]) lineage_proof: LineageProof = singleton_top_layer.lineage_proof_for_coinsol( singleton_claim_coinsol) puzzle_reveal: Program = singleton_top_layer.puzzle_for_singleton( launcher_id, adapted_puzzle, ) full_solution: Program = singleton_top_layer.solution_for_singleton( lineage_proof, singleton_child.amount, inner_solution) multi_odd_coinsol = CoinSpend( singleton_child, puzzle_reveal, full_solution, ) await self.make_and_spend_bundle( sim, sim_client, singleton_child, delegated_puzzle, [multi_odd_coinsol], ex_error=Err.GENERATOR_RUNTIME_ERROR, fail_msg="Too many odd children were allowed", ) # CREATE NO ODD CHILDREN (Negative Test) delegated_puzzle: Program = Program.to(( 1, [ [ConditionOpcode.CREATE_COIN, adapted_puzzle_hash, 4], [ConditionOpcode.CREATE_COIN, adapted_puzzle_hash, 10], ], )) inner_solution: Program = Program.to([[], delegated_puzzle, []]) lineage_proof: LineageProof = singleton_top_layer.lineage_proof_for_coinsol( singleton_claim_coinsol) puzzle_reveal: Program = singleton_top_layer.puzzle_for_singleton( launcher_id, adapted_puzzle, ) full_solution: Program = singleton_top_layer.solution_for_singleton( lineage_proof, singleton_child.amount, inner_solution) no_odd_coinsol = CoinSpend( singleton_child, puzzle_reveal, full_solution, ) await self.make_and_spend_bundle( sim, sim_client, singleton_child, delegated_puzzle, [no_odd_coinsol], ex_error=Err.GENERATOR_RUNTIME_ERROR, fail_msg="Need at least one odd child", ) # ATTEMPT TO CREATE AN EVEN SINGLETON (Negative test) await sim.rewind(save_height) delegated_puzzle: Program = Program.to(( 1, [ [ ConditionOpcode.CREATE_COIN, singleton_child.puzzle_hash, 2, ], [ConditionOpcode.CREATE_COIN, adapted_puzzle_hash, 1], ], )) inner_solution: Program = Program.to([[], delegated_puzzle, []]) lineage_proof: LineageProof = singleton_top_layer.lineage_proof_for_coinsol( singleton_claim_coinsol) puzzle_reveal: Program = singleton_top_layer.puzzle_for_singleton( launcher_id, adapted_puzzle, ) full_solution: Program = singleton_top_layer.solution_for_singleton( lineage_proof, singleton_child.amount, inner_solution) singleton_even_coinsol = CoinSpend( singleton_child, puzzle_reveal, full_solution, ) await self.make_and_spend_bundle( sim, sim_client, singleton_child, delegated_puzzle, [singleton_even_coinsol], ) # Now try a perfectly innocent spend evil_coin: Coin = (await sim.all_non_reward_coins())[0] delegated_puzzle: Program = Program.to(( 1, [ [ ConditionOpcode.CREATE_COIN, adapted_puzzle_hash, 1, ], ], )) inner_solution: Program = Program.to([[], delegated_puzzle, []]) lineage_proof: LineageProof = singleton_top_layer.lineage_proof_for_coinsol( singleton_even_coinsol) # noqa puzzle_reveal: Program = singleton_top_layer.puzzle_for_singleton( launcher_id, adapted_puzzle, ) full_solution: Program = singleton_top_layer.solution_for_singleton( lineage_proof, 1, inner_solution, ) evil_coinsol = CoinSpend( evil_coin, puzzle_reveal, full_solution, ) await self.make_and_spend_bundle( sim, sim_client, evil_coin, delegated_puzzle, [evil_coinsol], ex_error=Err.ASSERT_MY_COIN_ID_FAILED, fail_msg="This coin is even!", ) # MELTING # Remember, we're still spending singleton_child await sim.rewind(save_height) conditions = [ singleton_top_layer.MELT_CONDITION, [ ConditionOpcode.CREATE_COIN, adapted_puzzle_hash, (singleton_child.amount - 1), ], ] delegated_puzzle: Program = p2_conditions.puzzle_for_conditions( conditions) inner_solution: Program = p2_delegated_puzzle_or_hidden_puzzle.solution_for_conditions( conditions) lineage_proof: LineageProof = singleton_top_layer.lineage_proof_for_coinsol( singleton_claim_coinsol) puzzle_reveal: Program = singleton_top_layer.puzzle_for_singleton( launcher_id, adapted_puzzle, ) full_solution: Program = singleton_top_layer.solution_for_singleton( lineage_proof, singleton_child.amount, inner_solution) melt_coinsol = CoinSpend( singleton_child, puzzle_reveal, full_solution, ) await self.make_and_spend_bundle( sim, sim_client, singleton_child, delegated_puzzle, [melt_coinsol], ) melted_coin: Coin = (await sim.all_non_reward_coins())[0] assert melted_coin.puzzle_hash == adapted_puzzle_hash finally: await sim.close()
async def test_everything_with_signature(self, setup_sim): sim, sim_client = setup_sim try: sk = PrivateKey.from_bytes(secret_exponent_for_index(1).to_bytes(32, "big")) tail: Program = EverythingWithSig.construct([Program.to(sk.get_g1())]) checker_solution: Program = EverythingWithSig.solve([], {}) cat_puzzle: Program = construct_cat_puzzle(CAT_MOD, tail.get_tree_hash(), acs) cat_ph: bytes32 = cat_puzzle.get_tree_hash() await sim.farm_block(cat_ph) # Test eve spend # We don't sign any message data because CLVM 0 translates to b'' apparently starting_coin: Coin = (await sim_client.get_coin_records_by_puzzle_hash(cat_ph))[0].coin signature: G2Element = AugSchemeMPL.sign( sk, (starting_coin.name() + sim.defaults.AGG_SIG_ME_ADDITIONAL_DATA) ) await self.do_spend( sim, sim_client, tail, [starting_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], signatures=[signature], cost_str="Signature Issuance", ) # Test melting value coin: Coin = (await sim_client.get_coin_records_by_puzzle_hash(cat_ph, include_spent_coins=False))[0].coin signature = AugSchemeMPL.sign( sk, (int_to_bytes(-1) + coin.name() + sim.defaults.AGG_SIG_ME_ADDITIONAL_DATA) ) await self.do_spend( sim, sim_client, tail, [coin], [NO_LINEAGE_PROOF], [ Program.to( [ [51, acs.get_tree_hash(), coin.amount - 1], [51, 0, -113, tail, checker_solution], ] ) ], (MempoolInclusionStatus.SUCCESS, None), extra_deltas=[-1], limitations_solutions=[checker_solution], signatures=[signature], cost_str="Signature Melt", ) # Test minting value coin = (await sim_client.get_coin_records_by_puzzle_hash(cat_ph, include_spent_coins=False))[0].coin signature = AugSchemeMPL.sign(sk, (int_to_bytes(1) + coin.name() + sim.defaults.AGG_SIG_ME_ADDITIONAL_DATA)) # Need something to fund the minting temp_p = Program.to(1) temp_ph: bytes32 = temp_p.get_tree_hash() await sim.farm_block(temp_ph) acs_coin: Coin = (await sim_client.get_coin_records_by_puzzle_hash(temp_ph, include_spent_coins=False))[ 0 ].coin acs_bundle = SpendBundle( [ CoinSpend( acs_coin, temp_p, Program.to([]), ) ], G2Element(), ) await self.do_spend( sim, sim_client, tail, [coin], [NO_LINEAGE_PROOF], [ Program.to( [ [51, acs.get_tree_hash(), coin.amount + 1], [51, 0, -113, tail, checker_solution], ] ) ], (MempoolInclusionStatus.SUCCESS, None), extra_deltas=[1], limitations_solutions=[checker_solution], signatures=[signature], additional_spends=[acs_bundle], cost_str="Signature Mint", ) finally: await sim.close()
async def generate_launcher_spend( standard_wallet: Wallet, amount: uint64, initial_target_state: PoolState, genesis_challenge: bytes32, delay_time: uint64, delay_ph: bytes32, ) -> Tuple[SpendBundle, bytes32, bytes32]: """ Creates the initial singleton, which includes spending an origin coin, the launcher, and creating a singleton with the "pooling" inner state, which can be either self pooling or using a pool """ coins: Set[Coin] = await standard_wallet.select_coins(amount) if coins is None: raise ValueError("Not enough coins to create pool wallet") assert len(coins) == 1 launcher_parent: Coin = coins.copy().pop() genesis_launcher_puz: Program = SINGLETON_LAUNCHER launcher_coin: Coin = Coin(launcher_parent.name(), genesis_launcher_puz.get_tree_hash(), amount) escaping_inner_puzzle: Program = create_waiting_room_inner_puzzle( initial_target_state.target_puzzle_hash, initial_target_state.relative_lock_height, initial_target_state.owner_pubkey, launcher_coin.name(), genesis_challenge, delay_time, delay_ph, ) escaping_inner_puzzle_hash = escaping_inner_puzzle.get_tree_hash() self_pooling_inner_puzzle: Program = create_pooling_inner_puzzle( initial_target_state.target_puzzle_hash, escaping_inner_puzzle_hash, initial_target_state.owner_pubkey, launcher_coin.name(), genesis_challenge, delay_time, delay_ph, ) if initial_target_state.state == SELF_POOLING: puzzle = escaping_inner_puzzle elif initial_target_state.state == FARMING_TO_POOL: puzzle = self_pooling_inner_puzzle else: raise ValueError("Invalid initial state") full_pooling_puzzle: Program = create_full_puzzle(puzzle, launcher_id=launcher_coin.name()) puzzle_hash: bytes32 = full_pooling_puzzle.get_tree_hash() pool_state_bytes = Program.to([("p", bytes(initial_target_state)), ("t", delay_time), ("h", delay_ph)]) announcement_set: Set[Announcement] = set() announcement_message = Program.to([puzzle_hash, amount, pool_state_bytes]).get_tree_hash() announcement_set.add(Announcement(launcher_coin.name(), announcement_message)) create_launcher_tx_record: Optional[TransactionRecord] = await standard_wallet.generate_signed_transaction( amount, genesis_launcher_puz.get_tree_hash(), uint64(0), None, coins, None, False, announcement_set, ) assert create_launcher_tx_record is not None and create_launcher_tx_record.spend_bundle is not None genesis_launcher_solution: Program = Program.to([puzzle_hash, amount, pool_state_bytes]) launcher_cs: CoinSpend = CoinSpend( launcher_coin, SerializedProgram.from_program(genesis_launcher_puz), SerializedProgram.from_program(genesis_launcher_solution), ) launcher_sb: SpendBundle = SpendBundle([launcher_cs], G2Element()) # Current inner will be updated when state is verified on the blockchain full_spend: SpendBundle = SpendBundle.aggregate([create_launcher_tx_record.spend_bundle, launcher_sb]) return full_spend, puzzle_hash, launcher_coin.name()
async def generate_new_decentralised_id( self, amount: uint64) -> Optional[SpendBundle]: """ This must be called under the wallet state manager lock """ coins = await self.standard_wallet.select_coins(amount) if coins is None: return None origin = coins.copy().pop() genesis_launcher_puz = did_wallet_puzzles.SINGLETON_LAUNCHER launcher_coin = Coin(origin.name(), genesis_launcher_puz.get_tree_hash(), amount) did_inner: Program = await self.get_new_innerpuz() did_inner_hash = did_inner.get_tree_hash() did_full_puz = did_wallet_puzzles.create_fullpuz( did_inner, launcher_coin.name()) did_puzzle_hash = did_full_puz.get_tree_hash() announcement_set: Set[Announcement] = set() announcement_message = Program.to( [did_puzzle_hash, amount, bytes(0x80)]).get_tree_hash() announcement_set.add( Announcement(launcher_coin.name(), announcement_message).name()) tx_record: Optional[ TransactionRecord] = await self.standard_wallet.generate_signed_transaction( amount, genesis_launcher_puz.get_tree_hash(), uint64(0), origin.name(), coins, None, False, announcement_set) genesis_launcher_solution = Program.to( [did_puzzle_hash, amount, bytes(0x80)]) launcher_cs = CoinSpend(launcher_coin, genesis_launcher_puz, genesis_launcher_solution) launcher_sb = SpendBundle([launcher_cs], AugSchemeMPL.aggregate([])) eve_coin = Coin(launcher_coin.name(), did_puzzle_hash, amount) future_parent = LineageProof( eve_coin.parent_coin_info, did_inner_hash, eve_coin.amount, ) eve_parent = LineageProof( launcher_coin.parent_coin_info, launcher_coin.puzzle_hash, launcher_coin.amount, ) await self.add_parent(eve_coin.parent_coin_info, eve_parent, False) await self.add_parent(eve_coin.name(), future_parent, False) if tx_record is None or tx_record.spend_bundle is None: return None # Only want to save this information if the transaction is valid did_info: DIDInfo = DIDInfo( launcher_coin, self.did_info.backup_ids, self.did_info.num_of_backup_ids_needed, self.did_info.parent_info, did_inner, None, None, None, False, ) await self.save_info(did_info, False) eve_spend = await self.generate_eve_spend(eve_coin, did_full_puz, did_inner) full_spend = SpendBundle.aggregate( [tx_record.spend_bundle, eve_spend, launcher_sb]) return full_spend
async def recovery_spend( self, coin: Coin, puzhash: bytes32, parent_innerpuzhash_amounts_for_recovery_ids: List[Tuple[bytes, bytes, int]], pubkey: G1Element, spend_bundle: SpendBundle, ) -> SpendBundle: assert self.did_info.origin_coin is not None # innersol is mode new_amount message new_inner_puzhash parent_innerpuzhash_amounts_for_recovery_ids pubkey recovery_list_reveal) # noqa innersol: Program = Program.to([ 2, coin.amount, puzhash, puzhash, parent_innerpuzhash_amounts_for_recovery_ids, bytes(pubkey), self.did_info.backup_ids, ]) # full solution is (parent_info my_amount solution) assert self.did_info.current_inner is not None innerpuz: Program = self.did_info.current_inner full_puzzle: Program = did_wallet_puzzles.create_fullpuz( innerpuz, self.did_info.origin_coin.name(), ) parent_info = await self.get_parent_for_coin(coin) assert parent_info is not None fullsol = Program.to([ [ parent_info.parent_name, parent_info.inner_puzzle_hash, parent_info.amount, ], coin.amount, innersol, ]) list_of_solutions = [CoinSpend(coin, full_puzzle, fullsol)] index = await self.wallet_state_manager.puzzle_store.index_for_pubkey( pubkey) if index is None: raise ValueError("Unknown pubkey.") private = master_sk_to_wallet_sk(self.wallet_state_manager.private_key, index) message = bytes(puzhash) sigs = [AugSchemeMPL.sign(private, message)] for _ in spend_bundle.coin_spends: sigs.append(AugSchemeMPL.sign(private, message)) aggsig = AugSchemeMPL.aggregate(sigs) # assert AugSchemeMPL.verify(pubkey, message, aggsig) if spend_bundle is None: spend_bundle = SpendBundle(list_of_solutions, aggsig) else: spend_bundle = spend_bundle.aggregate( [spend_bundle, SpendBundle(list_of_solutions, aggsig)]) did_record = TransactionRecord( confirmed_at_height=uint32(0), created_at_time=uint64(int(time.time())), to_puzzle_hash=puzhash, amount=uint64(coin.amount), fee_amount=uint64(0), confirmed=False, sent=uint32(0), spend_bundle=spend_bundle, additions=spend_bundle.additions(), removals=spend_bundle.removals(), wallet_id=self.wallet_info.id, sent_to=[], trade_id=None, type=uint32(TransactionType.OUTGOING_TX.value), name=token_bytes(), ) await self.standard_wallet.push_transaction(did_record) new_did_info = DIDInfo( self.did_info.origin_coin, self.did_info.backup_ids, self.did_info.num_of_backup_ids_needed, self.did_info.parent_info, self.did_info.current_inner, self.did_info.temp_coin, self.did_info.temp_puzhash, self.did_info.temp_pubkey, True, ) await self.save_info(new_did_info, True) return spend_bundle
async def create_attestment(self, recovering_coin_name: bytes32, newpuz: bytes32, pubkey: G1Element, filename=None) -> SpendBundle: assert self.did_info.current_inner is not None assert self.did_info.origin_coin is not None coins = await self.select_coins(1) assert coins is not None and coins != set() coin = coins.pop() message = did_wallet_puzzles.create_recovery_message_puzzle( recovering_coin_name, newpuz, pubkey) innermessage = message.get_tree_hash() innerpuz: Program = self.did_info.current_inner # innerpuz solution is (mode, amount, message, new_inner_puzhash) messages = [(0, innermessage)] innersol = Program.to( [1, coin.amount, messages, innerpuz.get_tree_hash()]) # full solution is (corehash parent_info my_amount innerpuz_reveal solution) full_puzzle: Program = did_wallet_puzzles.create_fullpuz( innerpuz, self.did_info.origin_coin.name(), ) parent_info = await self.get_parent_for_coin(coin) assert parent_info is not None fullsol = Program.to([ [ parent_info.parent_name, parent_info.inner_puzzle_hash, parent_info.amount, ], coin.amount, innersol, ]) list_of_solutions = [CoinSpend(coin, full_puzzle, fullsol)] message_spend = did_wallet_puzzles.create_spend_for_message( coin.name(), recovering_coin_name, newpuz, pubkey) message_spend_bundle = SpendBundle([message_spend], AugSchemeMPL.aggregate([])) # sign for AGG_SIG_ME to_sign = Program.to([innerpuz.get_tree_hash(), coin.amount, messages]).get_tree_hash() message = to_sign + coin.name( ) + self.wallet_state_manager.constants.AGG_SIG_ME_ADDITIONAL_DATA pubkey = did_wallet_puzzles.get_pubkey_from_innerpuz(innerpuz) index = await self.wallet_state_manager.puzzle_store.index_for_pubkey( pubkey) private = master_sk_to_wallet_sk(self.wallet_state_manager.private_key, index) signature = AugSchemeMPL.sign(private, message) # assert signature.validate([signature.PkMessagePair(pubkey, message)]) spend_bundle = SpendBundle(list_of_solutions, signature) did_record = TransactionRecord( confirmed_at_height=uint32(0), created_at_time=uint64(int(time.time())), to_puzzle_hash=coin.puzzle_hash, amount=uint64(coin.amount), fee_amount=uint64(0), confirmed=False, sent=uint32(0), spend_bundle=spend_bundle, additions=spend_bundle.additions(), removals=spend_bundle.removals(), wallet_id=self.wallet_info.id, sent_to=[], trade_id=None, type=uint32(TransactionType.INCOMING_TX.value), name=token_bytes(), ) await self.standard_wallet.push_transaction(did_record) if filename is not None: f = open(filename, "w") f.write(self.get_my_DID()) f.write(":") f.write(bytes(message_spend_bundle).hex()) f.write(":") parent = coin.parent_coin_info.hex() innerpuzhash = self.did_info.current_inner.get_tree_hash().hex() amount = coin.amount f.write(parent) f.write(":") f.write(innerpuzhash) f.write(":") f.write(str(amount)) f.close() return message_spend_bundle
async def create_exit_spend(self, puzhash: bytes32): assert self.did_info.current_inner is not None assert self.did_info.origin_coin is not None coins = await self.select_coins(1) assert coins is not None coin = coins.pop() amount = coin.amount - 1 # innerpuz solution is (mode amount new_puzhash) innersol: Program = Program.to([0, amount, puzhash]) # full solution is (corehash parent_info my_amount innerpuz_reveal solution) innerpuz: Program = self.did_info.current_inner full_puzzle: Program = did_wallet_puzzles.create_fullpuz( innerpuz, self.did_info.origin_coin.name(), ) parent_info = await self.get_parent_for_coin(coin) assert parent_info is not None fullsol = Program.to([ [ parent_info.parent_name, parent_info.inner_puzzle_hash, parent_info.amount, ], coin.amount, innersol, ]) list_of_solutions = [CoinSpend(coin, full_puzzle, fullsol)] # sign for AGG_SIG_ME message = ( Program.to([amount, puzhash]).get_tree_hash() + coin.name() + self.wallet_state_manager.constants.AGG_SIG_ME_ADDITIONAL_DATA) pubkey = did_wallet_puzzles.get_pubkey_from_innerpuz(innerpuz) index = await self.wallet_state_manager.puzzle_store.index_for_pubkey( pubkey) private = master_sk_to_wallet_sk(self.wallet_state_manager.private_key, index) signature = AugSchemeMPL.sign(private, message) # assert signature.validate([signature.PkMessagePair(pubkey, message)]) sigs = [signature] aggsig = AugSchemeMPL.aggregate(sigs) spend_bundle = SpendBundle(list_of_solutions, aggsig) did_record = TransactionRecord( confirmed_at_height=uint32(0), created_at_time=uint64(int(time.time())), to_puzzle_hash=puzhash, amount=uint64(coin.amount), fee_amount=uint64(0), confirmed=False, sent=uint32(0), spend_bundle=spend_bundle, additions=spend_bundle.additions(), removals=spend_bundle.removals(), wallet_id=self.wallet_info.id, sent_to=[], trade_id=None, type=uint32(TransactionType.OUTGOING_TX.value), name=token_bytes(), ) await self.standard_wallet.push_transaction(did_record) return 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