async def get_coin_records_by_parent_ids( self, include_spent_coins: bool, parent_ids: List[bytes32], start_height: uint32 = uint32(0), end_height: uint32 = uint32((2 ** 32) - 1), ) -> List[CoinRecord]: if len(parent_ids) == 0: return [] coins = set() parent_ids_db = tuple([pid.hex() for pid in parent_ids]) cursor = await self.coin_record_db.execute( f'SELECT * from coin_record WHERE coin_parent in ({"?," * (len(parent_ids_db) - 1)}?) ' f"AND confirmed_index>=? AND confirmed_index<? " f"{'' if include_spent_coins else 'AND spent=0'}", parent_ids_db + (start_height, end_height), ) rows = await cursor.fetchall() await cursor.close() for row in rows: coin = Coin(bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), uint64.from_bytes(row[7])) coins.add(CoinRecord(coin, row[1], row[2], row[3], row[4], row[8])) return list(coins)
def do_inspect_coin_cmd(ctx, coins, print_results=True, **kwargs): if kwargs and all([kwargs[key] for key in kwargs.keys()]): coin_objs = [ Coin(bytes.fromhex(kwargs['parent_id']), bytes.fromhex(kwargs['puzzle_hash']), uint64(kwargs['amount'])) ] elif not kwargs or not any([kwargs[key] for key in kwargs.keys()]): coin_objs = [] try: if type(coins[0]) == str: coin_objs = streamable_load(Coin, coins) else: coin_objs = coins except: print("One or more of the specified objects was not a coin") else: print("Invalid arguments specified.") return if print_results: inspect_callback(coin_objs, ctx, id_calc=(lambda e: e.name()), type='Coin') else: return coin_objs
def to_spend_bundle(self) -> SpendBundle: # Before we serialze this as a SpendBundle, we need to serialze the `requested_payments` as dummy CoinSpends additional_coin_spends: List[CoinSpend] = [] for tail_hash, payments in self.requested_payments.items(): puzzle_reveal: Program = construct_cat_puzzle( CAT_MOD, tail_hash, OFFER_MOD) if tail_hash else OFFER_MOD inner_solutions = [] nonces: List[bytes32] = [p.nonce for p in payments] for nonce in list( dict.fromkeys(nonces)): # dedup without messing with order nonce_payments: List[NotarizedPayment] = list( filter(lambda p: p.nonce == nonce, payments)) inner_solutions.append( (nonce, [np.as_condition_args() for np in nonce_payments])) additional_coin_spends.append( CoinSpend( Coin( ZERO_32, puzzle_reveal.get_tree_hash(), uint64(0), ), puzzle_reveal, Program.to(inner_solutions), )) return SpendBundle.aggregate([ SpendBundle(additional_coin_spends, G2Element()), self.bundle, ])
def create_spend_for_message(parent_of_message, recovering_coin, newpuz, pubkey): puzzle = create_recovery_message_puzzle(recovering_coin, newpuz, pubkey) coin = Coin(parent_of_message, puzzle.get_tree_hash(), uint64(0)) solution = Program.to([]) coinsol = CoinSolution(coin, puzzle, solution) return coinsol
async def get_unspent_coins_for_wallet( self, wallet_id: int) -> Set[WalletCoinRecord]: """ Returns set of CoinRecords that have not been spent yet for a wallet. """ async with self.wallet_cache_lock: if wallet_id in self.coin_wallet_record_cache: wallet_coins: Dict[ bytes32, WalletCoinRecord] = self.coin_wallet_record_cache[ wallet_id] return set(wallet_coins.values()) coin_set = set() cursor = await self.db_connection.execute( "SELECT * from coin_record WHERE spent=0 and wallet_id=?", (wallet_id, ), ) rows = await cursor.fetchall() await cursor.close() cache_dict = {} for row in rows: coin = Coin(bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), uint64.from_bytes(row[7])) coin_record = WalletCoinRecord(coin, row[1], row[2], row[3], row[4], WalletType(row[8]), row[9]) coin_set.add(coin_record) cache_dict[coin.name()] = coin_record self.coin_wallet_record_cache[wallet_id] = cache_dict return coin_set
def launcher_conditions_and_spend_bundle( parent_coin_id: bytes32, launcher_amount: uint64, initial_singleton_inner_puzzle: Program, metadata: List[Tuple[str, str]], launcher_puzzle: Program = LAUNCHER_PUZZLE, ) -> Tuple[Program, bytes32, List[Program], SpendBundle]: launcher_puzzle_hash = launcher_puzzle.get_tree_hash() launcher_coin = Coin(parent_coin_id, launcher_puzzle_hash, launcher_amount) singleton_full_puzzle = SINGLETON_MOD.curry( SINGLETON_MOD_HASH, launcher_coin.name(), launcher_puzzle_hash, initial_singleton_inner_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})" ))) launcher_solution = Program.to( [singleton_full_puzzle_hash, launcher_amount, metadata]) coin_spend = CoinSpend(launcher_coin, launcher_puzzle, launcher_solution) spend_bundle = SpendBundle([coin_spend], G2Element()) lineage_proof = Program.to([parent_coin_id, launcher_amount]) return lineage_proof, launcher_coin.name( ), expected_conditions, spend_bundle
def farm_coin(self, puzzle_hash: bytes32, birthday: CoinTimestamp, amount: int = 1024) -> Coin: parent = birthday.height.to_bytes(32, "big") coin = Coin(parent, puzzle_hash, uint64(amount)) self._add_coin_entry(coin, birthday) return coin
async def test_store(self): db_filename = Path("wallet_store_test.db") if db_filename.exists(): db_filename.unlink() db_connection = await aiosqlite.connect(db_filename) db_wrapper = DBWrapper(db_connection) store = await WalletInterestedStore.create(db_wrapper) try: coin_1 = Coin(token_bytes(32), token_bytes(32), uint64(12312)) coin_2 = Coin(token_bytes(32), token_bytes(32), uint64(12312)) assert (await store.get_interested_coin_ids()) == [] await store.add_interested_coin_id(coin_1.name()) assert (await store.get_interested_coin_ids()) == [coin_1.name()] await store.add_interested_coin_id(coin_1.name()) assert (await store.get_interested_coin_ids()) == [coin_1.name()] await store.add_interested_coin_id(coin_2.name()) assert set(await store.get_interested_coin_ids()) == { coin_1.name(), coin_2.name() } puzzle_hash = token_bytes(32) assert len(await store.get_interested_puzzle_hashes()) == 0 await store.add_interested_puzzle_hash(puzzle_hash, 2) assert len(await store.get_interested_puzzle_hashes()) == 1 await store.add_interested_puzzle_hash(puzzle_hash, 2) assert len(await store.get_interested_puzzle_hashes()) == 1 assert ( await store.get_interested_puzzle_hash_wallet_id(puzzle_hash)) == 2 await store.add_interested_puzzle_hash(puzzle_hash, 3) assert len(await store.get_interested_puzzle_hashes()) == 1 assert ( await store.get_interested_puzzle_hash_wallet_id(puzzle_hash)) == 3 await store.remove_interested_puzzle_hash(puzzle_hash) assert (await store.get_interested_puzzle_hash_wallet_id( puzzle_hash)) is None assert len(await store.get_interested_puzzle_hashes()) == 0 finally: await db_connection.close() db_filename.unlink()
def coin_spend_for_lock_coin( prev_coin: Coin, subtotal: int, coin: Coin, ) -> CoinSpend: puzzle_reveal = LOCK_INNER_PUZZLE.curry(prev_coin.as_list(), subtotal) coin = Coin(coin.name(), puzzle_reveal.get_tree_hash(), uint64(0)) coin_spend = CoinSpend(coin, puzzle_reveal, Program.to(0)) return coin_spend
async def get_coins_added_at_height(self, height: uint32) -> List[CoinRecord]: cursor = await self.coin_record_db.execute("SELECT * from coin_record WHERE confirmed_index=?", (height,)) rows = await cursor.fetchall() await cursor.close() coins = [] for row in rows: coin = Coin(bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), uint64.from_bytes(row[7])) coins.append(CoinRecord(coin, row[1], row[2], row[3], row[4], row[8])) return coins
def make_fake_coin(index: int, puzzle_hash_db: dict) -> Coin: """ Make a fake coin with parent id equal to the index (ie. a genesis block coin) """ parent = index.to_bytes(32, "big") puzzle_hash = puzzle_hash_for_index(index, puzzle_hash_db) amount = 100000 return Coin(parent, puzzle_hash, uint64(amount))
async def get_coin_records_by_puzzle_hash(self, puzzle_hash: bytes32) -> List[WalletCoinRecord]: """Returns a list of all coin records with the given puzzle hash""" coins = set() cursor = await self.db_connection.execute("SELECT * from coin_record WHERE puzzle_hash=?", (puzzle_hash.hex(),)) rows = await cursor.fetchall() await cursor.close() for row in rows: coin = Coin(bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), uint64.from_bytes(row[7])) coins.add(WalletCoinRecord(coin, row[1], row[2], row[3], row[4], WalletType(row[8]), row[9])) return list(coins)
def generate_farmed_coin( block_index: int, puzzle_hash: bytes32, amount: int, ) -> Coin: """ Generate a (fake) coin which can be used as a starting point for a chain of coin tests. """ return Coin(int_as_bytes32(block_index), puzzle_hash, uint64(amount))
async def get_coin_record(self, coin_name: bytes32) -> Optional[CoinRecord]: if coin_name.hex() in self.coin_record_cache: return self.coin_record_cache[coin_name.hex()] cursor = await self.coin_record_db.execute("SELECT * from coin_record WHERE coin_name=?", (coin_name.hex(),)) row = await cursor.fetchone() await cursor.close() if row is not None: coin = Coin(bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), uint64.from_bytes(row[7])) return CoinRecord(coin, row[1], row[2], row[3], row[4], row[8]) return None
async def get_coin_record_by_coin_id(self, coin_id: bytes32) -> Optional[WalletCoinRecord]: """Returns a coin records with the given name, if it exists""" cursor = await self.db_connection.execute("SELECT * from coin_record WHERE coin_name=?", (coin_id.hex(),)) row = await cursor.fetchone() await cursor.close() if row is None: return None coin = Coin(bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), uint64.from_bytes(row[7])) coin_record = WalletCoinRecord(coin, row[1], row[2], row[3], row[4], WalletType(row[8]), row[9]) return coin_record
def created_outputs_for_conditions_dict( conditions_dict: Dict[ConditionOpcode, List[ConditionWithArgs]], input_coin_name: bytes32, ) -> List[Coin]: output_coins = [] for cvp in conditions_dict.get(ConditionOpcode.CREATE_COIN, []): puzzle_hash, amount_bin = cvp.vars[0], cvp.vars[1] amount = int_from_bytes(amount_bin) coin = Coin(input_coin_name, bytes32(puzzle_hash), uint64(amount)) output_coins.append(coin) return output_coins
async def get_all_coins(self) -> Set[WalletCoinRecord]: """ Returns set of all CoinRecords.""" coins = set() cursor = await self.db_connection.execute("SELECT * from coin_record") rows = await cursor.fetchall() await cursor.close() for row in rows: coin = Coin(bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), uint64.from_bytes(row[7])) coins.add(WalletCoinRecord(coin, row[1], row[2], row[3], row[4], WalletType(row[8]), row[9])) return coins
async def check_all_there(): spendable = await cc_wallet.get_cc_spendable_coins() spendable_name_set = set() for record in spendable: spendable_name_set.add(record.coin.name()) puzzle_hash = cc_puzzle_hash_for_inner_puzzle_hash(CC_MOD, cc_wallet.cc_info.my_genesis_checker, cc_2_hash) for i in range(1, 50): coin = Coin(spent_coint.name(), puzzle_hash, i) if coin.name() not in spendable_name_set: return False return True
async def get_coin_record(self, coin_name: bytes32) -> Optional[WalletCoinRecord]: """ Returns CoinRecord with specified coin id. """ if coin_name in self.coin_record_cache: return self.coin_record_cache[coin_name] cursor = await self.db_connection.execute("SELECT * from coin_record WHERE coin_name=?", (coin_name.hex(),)) row = await cursor.fetchone() await cursor.close() if row is not None: coin = Coin(bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), uint64.from_bytes(row[7])) return WalletCoinRecord(coin, row[1], row[2], row[3], row[4], WalletType(row[8]), row[9]) return None
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_solution = CoinSolution(farmed_coin, ANYONE_CAN_SPEND_PUZZLE, conditions) spend_bundle = SpendBundle.aggregate( [launcher_spend_bundle, SpendBundle([coin_solution], 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_solutions[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
async def all_non_reward_coins(self) -> List[Coin]: coins = set() cursor = await self.mempool_manager.coin_store.coin_record_db.execute( "SELECT * from coin_record WHERE coinbase=0 AND spent=0 ", ) rows = await cursor.fetchall() await cursor.close() for row in rows: coin = Coin(bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), uint64.from_bytes(row[7])) coins.add(coin) return list(coins)
def make_fake_coin(index: int, puzzle_hash_db: dict) -> Coin: """ Make a fake coin with parent id equal to the index (ie. a genesis block coin) """ parent = index.to_bytes(32, "big") puzzle_hash = puzzle_hash_for_index(index, puzzle_hash_db) amount = 100000 # TODO: address hint error and remove ignore # error: Argument 1 to "Coin" has incompatible type "bytes"; expected "bytes32" [arg-type] # error: Argument 2 to "Coin" has incompatible type "bytes"; expected "bytes32" [arg-type] return Coin(parent, puzzle_hash, uint64(amount)) # type: ignore[arg-type]
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() did_inner: Program = await self.get_new_innerpuz() did_inner_hash = did_inner.get_tree_hash() did_puz = did_wallet_puzzles.create_fullpuz(did_inner, origin.puzzle_hash) did_puzzle_hash = did_puz.get_tree_hash() tx_record: Optional[ TransactionRecord] = await self.standard_wallet.generate_signed_transaction( amount, did_puzzle_hash, uint64(0), origin.name(), coins) eve_coin = Coin(origin.name(), did_puzzle_hash, amount) future_parent = CCParent( eve_coin.parent_coin_info, did_inner_hash, eve_coin.amount, ) eve_parent = CCParent( origin.parent_coin_info, origin.puzzle_hash, origin.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( origin, self.did_info.backup_ids, self.did_info.num_of_backup_ids_needed, self.did_info.parent_info, did_inner, None, None, None, ) await self.save_info(did_info, False) eve_spend = await self.generate_eve_spend(eve_coin, did_puz, did_inner) full_spend = SpendBundle.aggregate([tx_record.spend_bundle, eve_spend]) return full_spend
async def check_all_there(): spendable = await cat_wallet.get_cat_spendable_coins() spendable_name_set = set() for record in spendable: spendable_name_set.add(record.coin.name()) puzzle_hash = construct_cat_puzzle( CAT_MOD, cat_wallet.cat_info.limitations_program_hash, cat_2).get_tree_hash() for i in range(1, 50): coin = Coin(spent_coint.name(), puzzle_hash, i) if coin.name() not in spendable_name_set: return False return True
async def get_coin_record(self, coin_name: bytes32) -> Optional[CoinRecord]: cached = self.coin_record_cache.get(coin_name) if cached is not None: return cached cursor = await self.coin_record_db.execute("SELECT * from coin_record WHERE coin_name=?", (coin_name.hex(),)) row = await cursor.fetchone() await cursor.close() if row is not None: coin = Coin(bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), uint64.from_bytes(row[7])) record = CoinRecord(coin, row[1], row[2], row[3], row[4], row[8]) self.coin_record_cache.put(record.coin.name(), record) return record return None
def check_messages_for_hint(messages): notified_state = None for message in messages: if message.type == ProtocolMessageTypes.coin_state_update.value: data_response: CoinStateUpdate = CoinStateUpdate.from_bytes( message.data) notified_state = data_response break assert notified_state is not None assert notified_state.items[0].coin == Coin( coin_spent.name(), hint_puzzle_hash, amount)
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 created_outputs_for_conditions_dict( conditions_dict: Dict[ConditionOpcode, List[ConditionWithArgs]], input_coin_name: bytes32, ) -> List[Coin]: output_coins = [] for cvp in conditions_dict.get(ConditionOpcode.CREATE_COIN, []): puzzle_hash, amount_bin = cvp.vars[0], cvp.vars[1] amount = int_from_bytes(amount_bin) # TODO: address hint error and remove ignore # error: Argument 2 to "Coin" has incompatible type "bytes"; expected "bytes32" [arg-type] coin = Coin(input_coin_name, puzzle_hash, uint64(amount)) # type: ignore[arg-type] output_coins.append(coin) return output_coins
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 created_outputs_for_conditions_dict( conditions_dict: Dict[ConditionOpcode, List[ConditionWithArgs]], input_coin_name: bytes32, ) -> List[Coin]: output_coins = [] for cvp in conditions_dict.get(ConditionOpcode.CREATE_COIN, []): # TODO: check condition very carefully # (ensure there are the correct number and type of parameters) # maybe write a type-checking framework for conditions # and don't just fail with asserts puzzle_hash, amount_bin = cvp.vars[0], cvp.vars[1] amount = int_from_bytes(amount_bin) coin = Coin(input_coin_name, puzzle_hash, amount) output_coins.append(coin) return output_coins