async def test_create_new_pool_wallet_farm_to_pool(self, one_wallet_node_and_rpc, fee): client, wallet_node_0, full_node_api = one_wallet_node_and_rpc wallet_0 = wallet_node_0.wallet_state_manager.main_wallet our_ph = await wallet_0.get_new_puzzlehash() summaries_response = await client.get_wallets() for summary in summaries_response: if WalletType(int(summary["type"])) == WalletType.POOLING_WALLET: assert False creation_tx: TransactionRecord = await client.create_new_pool_wallet( our_ph, "http://pool.example.com", 10, "localhost:5000", "new", "FARMING_TO_POOL", fee) await time_out_assert( 10, full_node_api.full_node.mempool_manager.get_spendbundle, creation_tx.spend_bundle, creation_tx.name, ) await self.farm_blocks(full_node_api, our_ph, 6) assert full_node_api.full_node.mempool_manager.get_spendbundle( creation_tx.name) is None summaries_response = await client.get_wallets() wallet_id: Optional[int] = None for summary in summaries_response: if WalletType(int(summary["type"])) == WalletType.POOLING_WALLET: wallet_id = summary["id"] assert wallet_id is not None status: PoolWalletInfo = (await client.pw_status(wallet_id))[0] assert status.current.state == PoolSingletonState.FARMING_TO_POOL.value assert status.target is None assert status.current.owner_pubkey == G1Element.from_bytes( bytes.fromhex( "b286bbf7a10fa058d2a2a758921377ef00bb7f8143e1bd40dd195ae918dbef42cfc481140f01b9eae13b430a0c8fe304" )) assert status.current.pool_url == "http://pool.example.com" assert status.current.relative_lock_height == 10 assert status.current.version == 1 # Check that config has been written properly full_config: Dict = load_config( wallet_0.wallet_state_manager.root_path, "config.yaml") pool_list: List[Dict] = full_config["pool"]["pool_list"] assert len(pool_list) == 1 pool_config = pool_list[0] assert ( pool_config["authentication_public_key"] == "0xb3c4b513600729c6b2cf776d8786d620b6acc88f86f9d6f489fa0a0aff81d634262d5348fb7ba304db55185bb4c5c8a4" ) # It can be one of multiple launcher IDs, due to selecting a different coin assert pool_config["launcher_id"] in { "0x78a1eadf583a2f27a129d7aeba076ec6a5200e1ec8225a72c9d4180342bf91a7", "0x2bcab0310e78a7ab04e251ac6bdd5dfc80ce6895132e64f97265029db3d8309a", "0x09edf686c318c138cd3461c38e9b4e10e7f21fc476a0929b4480e126b6efcb81", } assert pool_config["pool_url"] == "http://pool.example.com"
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
async def get_derivation_record( self, index: uint32, wallet_id: uint32) -> Optional[DerivationRecord]: """ Returns the derivation record by index and wallet id. """ cursor = await self.db_connection.execute( "SELECT * FROM derivation_paths WHERE derivation_index=? and wallet_id=?;", ( index, wallet_id, ), ) row = await cursor.fetchone() await cursor.close() if row is not None and row[0] is not None: return DerivationRecord( uint32(row[0]), bytes32.fromhex(row[2]), G1Element.from_bytes(bytes.fromhex(row[1])), WalletType(row[3]), uint32(row[4]), ) return None
async def print_balances(args: dict, wallet_client: WalletRpcClient, fingerprint: int) -> None: summaries_response = await wallet_client.get_wallets() config = load_config(DEFAULT_ROOT_PATH, "config.yaml") address_prefix = config["network_overrides"]["config"][ config["selected_network"]]["address_prefix"] print(f"Wallet height: {await wallet_client.get_height_info()}") print( f"Sync status: {'Synced' if (await wallet_client.get_synced()) else 'Not synced'}" ) print(f"Balances, fingerprint: {fingerprint}") for summary in summaries_response: wallet_id = summary["id"] balances = await wallet_client.get_wallet_balance(wallet_id) typ = WalletType(int(summary["type"])) address_prefix, scale = wallet_coin_unit(typ, address_prefix) print(f"Wallet ID {wallet_id} type {typ.name} {summary['name']}") print( f" -Total Balance: {print_balance(balances['confirmed_wallet_balance'], scale, address_prefix)}" ) print( f" -Pending Total Balance: {print_balance(balances['unconfirmed_wallet_balance'], scale, address_prefix)}" ) print( f" -Spendable: {print_balance(balances['spendable_balance'], scale, address_prefix)}" )
async def print_balances(args: dict, wallet_client: WalletRpcClient, fingerprint: int) -> None: summaries_response = await wallet_client.get_wallets() config = load_config(DEFAULT_ROOT_PATH, "config.yaml") address_prefix = config["network_overrides"]["config"][config["selected_network"]]["address_prefix"] print(f"Wallet height: {await wallet_client.get_height_info()}") print(f"Sync status: {'Synced' if (await wallet_client.get_synced()) else 'Not synced'}") print(f"Balances, fingerprint: {fingerprint}") for summary in summaries_response: wallet_id = summary["id"] balances = await wallet_client.get_wallet_balance(wallet_id) typ = WalletType(int(summary["type"])).name if typ != "STANDARD_WALLET": print(f"Wallet ID {wallet_id} type {typ} {summary['name']}") print(f" -Total Balance: " f"{balances['confirmed_wallet_balance']/units['colouredcoin']}") print(f" -Pending Total Balance: {balances['unconfirmed_wallet_balance']/units['colouredcoin']}") print(f" -Spendable Balance: {balances['spendable_balance']/units['colouredcoin']}") else: print(f"Wallet ID {wallet_id} type {typ}") print( f" -Total Balance: {balances['confirmed_wallet_balance']/units['chia']} {address_prefix} " f"({balances['confirmed_wallet_balance']} mojo)" ) print( f" -Pending Total Balance: {balances['unconfirmed_wallet_balance']/units['chia']} {address_prefix} " f"({balances['unconfirmed_wallet_balance']} mojo)" ) print( f" -Spendable: {balances['spendable_balance']/units['chia']} {address_prefix} " f"({balances['spendable_balance']} mojo)" )
def row_to_record(self, row) -> DerivationRecord: return DerivationRecord( uint32(row[0]), bytes32.fromhex(row[2]), G1Element.from_bytes(bytes.fromhex(row[1])), WalletType(row[3]), uint32(row[4]), bool(row[6]), )
async def get_wallet_type(wallet_id: int, wallet_client: WalletRpcClient) -> WalletType: summaries_response = await wallet_client.get_wallets() for summary in summaries_response: summary_id: int = summary["id"] summary_type: int = summary["type"] if wallet_id == summary_id: return WalletType(summary_type) raise LookupError(f"Wallet ID not found: {wallet_id}")
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)
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
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
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 get_wallet_action(self, id: int) -> Optional[WalletAction]: """ Return a wallet action by id """ cursor = await self.db_connection.execute("SELECT * from action_queue WHERE id=?", (id,)) row = await cursor.fetchone() await cursor.close() if row is None: return None return WalletAction(row[0], row[1], row[2], WalletType(row[3]), row[4], bool(row[5]), row[6])
async def wallet_info_for_puzzle_hash(self, puzzle_hash: bytes32) -> Optional[Tuple[uint32, WalletType]]: """ Returns the derivation path for the puzzle_hash. Returns None if not present. """ cursor = await self.db_connection.execute( "SELECT * from derivation_paths WHERE puzzle_hash=?", (puzzle_hash.hex(),) ) row = await cursor.fetchone() await cursor.close() if row is not None: return row[4], WalletType(row[3]) return None
async def get_all_pending_actions(self) -> List[WalletAction]: """ Returns list of all pending action """ result: List[WalletAction] = [] cursor = await self.db_connection.execute("SELECT * from action_queue WHERE done=?", (0,)) rows = await cursor.fetchall() await cursor.close() if rows is None: return result for row in rows: action = WalletAction(row[0], row[1], row[2], WalletType(row[3]), row[4], bool(row[5]), row[6]) result.append(action) return result
def get_backup_info(file_path, private_key): json_dict = open_backup_file(file_path, private_key) data = json_dict["data"] wallet_list_json = data["wallet_list"] info_dict = {} wallets = [] for wallet_info in wallet_list_json: wallet = {} wallet["name"] = wallet_info["name"] wallet["type"] = wallet_info["type"] wallet["type_name"] = WalletType(wallet_info["type"]).name wallet["id"] = wallet_info["id"] wallet["data"] = wallet_info["data"] wallets.append(wallet) info_dict["version"] = data["version"] info_dict["fingerprint"] = data["fingerprint"] info_dict["timestamp"] = data["timestamp"] info_dict["wallets"] = wallets return info_dict
async def get_derivation_record_for_puzzle_hash(self, puzzle_hash: bytes32) -> Optional[DerivationRecord]: """ Returns the derivation record by index and wallet id. """ cursor = await self.db_connection.execute( "SELECT * FROM derivation_paths WHERE puzzle_hash=?;", (puzzle_hash.hex(),), ) row = await cursor.fetchone() await cursor.close() if row is not None and row[0] is not None: return DerivationRecord( uint32(row[0]), bytes32.fromhex(row[2]), G1Element.from_bytes(bytes.fromhex(row[1])), WalletType(row[3]), uint32(row[4]), bool(row[6]), ) return None
async def test_absorb_pooling(self, one_wallet_node_and_rpc): client, wallet_node_0, full_node_api = one_wallet_node_and_rpc wallet_0 = wallet_node_0.wallet_state_manager.main_wallet our_ph = await wallet_0.get_new_puzzlehash() summaries_response = await client.get_wallets() for summary in summaries_response: if WalletType(int(summary["type"])) == WalletType.POOLING_WALLET: assert False # Balance stars at 6 XCH assert (await wallet_0.get_confirmed_balance()) == 6000000000000 creation_tx: TransactionRecord = await client.create_new_pool_wallet( our_ph, "http://123.45.67.89", 10, "localhost:5000", "new", "FARMING_TO_POOL" ) await time_out_assert( 10, full_node_api.full_node.mempool_manager.get_spendbundle, creation_tx.spend_bundle, creation_tx.name, ) await self.farm_blocks(full_node_api, our_ph, 1) await asyncio.sleep(2) status: PoolWalletInfo = (await client.pw_status(2))[0] log.warning(f"{await wallet_0.get_confirmed_balance()}") assert status.current.state == PoolSingletonState.FARMING_TO_POOL.value plot_id: bytes32 = self.create_pool_plot(status.p2_singleton_puzzle_hash) all_blocks = await full_node_api.get_all_full_blocks() blocks = bt.get_consecutive_blocks( 3, block_list_input=all_blocks, force_plot_id=plot_id, farmer_reward_puzzle_hash=our_ph, guarantee_transaction_block=True, ) await full_node_api.full_node.respond_block(full_node_protocol.RespondBlock(blocks[-3])) await full_node_api.full_node.respond_block(full_node_protocol.RespondBlock(blocks[-2])) await full_node_api.full_node.respond_block(full_node_protocol.RespondBlock(blocks[-1])) await asyncio.sleep(2) bal = await client.get_wallet_balance(2) log.warning(f"{await wallet_0.get_confirmed_balance()}") # Pooled plots don't have balance assert bal["confirmed_wallet_balance"] == 0 # Claim 2 * 1.75, and farm a new 1.75 absorb_tx: TransactionRecord = await client.pw_absorb_rewards(2) await time_out_assert( 5, full_node_api.full_node.mempool_manager.get_spendbundle, absorb_tx.spend_bundle, absorb_tx.name, ) await self.farm_blocks(full_node_api, our_ph, 2) await asyncio.sleep(2) new_status: PoolWalletInfo = (await client.pw_status(2))[0] assert status.current == new_status.current assert status.tip_singleton_coin_id != new_status.tip_singleton_coin_id bal = await client.get_wallet_balance(2) log.warning(f"{await wallet_0.get_confirmed_balance()}") assert bal["confirmed_wallet_balance"] == 0 # Claim another 1.75 absorb_tx: TransactionRecord = await client.pw_absorb_rewards(2) absorb_tx.spend_bundle.debug() await time_out_assert( 5, full_node_api.full_node.mempool_manager.get_spendbundle, absorb_tx.spend_bundle, absorb_tx.name, ) await self.farm_blocks(full_node_api, our_ph, 2) await asyncio.sleep(2) bal = await client.get_wallet_balance(2) assert bal["confirmed_wallet_balance"] == 0 log.warning(f"{await wallet_0.get_confirmed_balance()}") self.delete_plot(plot_id) assert len(await wallet_node_0.wallet_state_manager.tx_store.get_unconfirmed_for_wallet(2)) == 0 assert ( wallet_node_0.wallet_state_manager.get_peak().height == full_node_api.full_node.blockchain.get_peak().height ) # Balance stars at 6 XCH and 5 more blocks are farmed, total 22 XCH assert (await wallet_0.get_confirmed_balance()) == 21999999999999
async def test_leave_pool(self, setup, fee, trusted): """This tests self-pooling -> pooling -> escaping -> self pooling""" full_nodes, wallet_nodes, receive_address, client, rpc_cleanup = setup our_ph = receive_address[0] wallets = [wallet_n.wallet_state_manager.main_wallet for wallet_n in wallet_nodes] pool_ph = receive_address[1] full_node_api = full_nodes[0] if trusted: wallet_nodes[0].config["trusted_peers"] = { full_node_api.full_node.server.node_id.hex(): full_node_api.full_node.server.node_id.hex() } else: wallet_nodes[0].config["trusted_peers"] = {} await wallet_nodes[0].server.start_client( PeerInfo(self_hostname, uint16(full_node_api.full_node.server._port)), None ) WAIT_SECS = 200 try: summaries_response = await client.get_wallets() for summary in summaries_response: if WalletType(int(summary["type"])) == WalletType.POOLING_WALLET: assert False async def have_chia(): await self.farm_blocks(full_node_api, our_ph, 1) return (await wallets[0].get_confirmed_balance()) > 0 await time_out_assert(timeout=WAIT_SECS, function=have_chia) creation_tx: TransactionRecord = await client.create_new_pool_wallet( our_ph, "", 0, "localhost:5000", "new", "SELF_POOLING", fee ) await time_out_assert( 10, full_node_api.full_node.mempool_manager.get_spendbundle, creation_tx.spend_bundle, creation_tx.name, ) await self.farm_blocks(full_node_api, our_ph, 6) assert full_node_api.full_node.mempool_manager.get_spendbundle(creation_tx.name) is None summaries_response = await client.get_wallets() wallet_id: Optional[int] = None for summary in summaries_response: if WalletType(int(summary["type"])) == WalletType.POOLING_WALLET: wallet_id = summary["id"] assert wallet_id is not None status: PoolWalletInfo = (await client.pw_status(wallet_id))[0] assert status.current.state == PoolSingletonState.SELF_POOLING.value assert status.target is None join_pool_tx: TransactionRecord = await client.pw_join_pool( wallet_id, pool_ph, "https://pool.example.com", 5, fee, ) assert join_pool_tx is not None status: PoolWalletInfo = (await client.pw_status(wallet_id))[0] assert status.current.state == PoolSingletonState.SELF_POOLING.value assert status.current.pool_url is None assert status.current.relative_lock_height == 0 assert status.current.state == 1 assert status.current.version == 1 assert status.target.pool_url == "https://pool.example.com" assert status.target.relative_lock_height == 5 assert status.target.state == 3 assert status.target.version == 1 async def status_is_farming_to_pool(): await self.farm_blocks(full_node_api, our_ph, 1) pw_status: PoolWalletInfo = (await client.pw_status(wallet_id))[0] return pw_status.current.state == PoolSingletonState.FARMING_TO_POOL.value await time_out_assert(timeout=WAIT_SECS, function=status_is_farming_to_pool) status: PoolWalletInfo = (await client.pw_status(wallet_id))[0] leave_pool_tx: TransactionRecord = await client.pw_self_pool(wallet_id, fee) assert leave_pool_tx.wallet_id == wallet_id assert leave_pool_tx.amount == 1 async def status_is_leaving(): await self.farm_blocks(full_node_api, our_ph, 1) pw_status: PoolWalletInfo = (await client.pw_status(wallet_id))[0] return pw_status.current.state == PoolSingletonState.LEAVING_POOL.value await time_out_assert(timeout=WAIT_SECS, function=status_is_leaving) async def status_is_self_pooling(): # Farm enough blocks to wait for relative_lock_height await self.farm_blocks(full_node_api, our_ph, 1) pw_status: PoolWalletInfo = (await client.pw_status(wallet_id))[0] return pw_status.current.state == PoolSingletonState.SELF_POOLING.value await time_out_assert(timeout=WAIT_SECS, function=status_is_self_pooling) assert len(await wallets[0].wallet_state_manager.tx_store.get_unconfirmed_for_wallet(2)) == 0 finally: client.close() await client.await_closed() await rpc_cleanup()
async def create_more_puzzle_hashes(self, from_zero: bool = False): """ For all wallets in the user store, generates the first few puzzle hashes so that we can restore the wallet from only the private keys. """ targets = list(self.wallets.keys()) unused: Optional[ uint32] = await self.puzzle_store.get_unused_derivation_path() if unused is None: # This handles the case where the database has entries but they have all been used unused = await self.puzzle_store.get_last_derivation_path() if unused is None: # This handles the case where the database is empty unused = uint32(0) if self.new_wallet: to_generate = self.config["initial_num_public_keys_new_wallet"] else: to_generate = self.config["initial_num_public_keys"] for wallet_id in targets: target_wallet = self.wallets[wallet_id] last: Optional[ uint32] = await self.puzzle_store.get_last_derivation_path_for_wallet( wallet_id) start_index = 0 derivation_paths: List[DerivationRecord] = [] if last is not None: start_index = last + 1 # If the key was replaced (from_zero=True), we should generate the puzzle hashes for the new key if from_zero: start_index = 0 for index in range(start_index, unused + to_generate): if WalletType(target_wallet.type()) == WalletType.RATE_LIMITED: if target_wallet.rl_info.initialized is False: break wallet_type = target_wallet.rl_info.type if wallet_type == "user": rl_pubkey = G1Element.from_bytes( target_wallet.rl_info.user_pubkey) else: rl_pubkey = G1Element.from_bytes( target_wallet.rl_info.admin_pubkey) rl_puzzle: Program = target_wallet.puzzle_for_pk(rl_pubkey) puzzle_hash: bytes32 = rl_puzzle.get_tree_hash() rl_index = self.get_derivation_index(rl_pubkey) if rl_index == -1: break derivation_paths.append( DerivationRecord( uint32(rl_index), puzzle_hash, rl_pubkey, target_wallet.type(), uint32(target_wallet.id()), )) break pubkey: G1Element = self.get_public_key(uint32(index)) puzzle: Program = target_wallet.puzzle_for_pk(bytes(pubkey)) if puzzle is None: self.log.warning( f"Unable to create puzzles with wallet {target_wallet}" ) break puzzlehash: bytes32 = puzzle.get_tree_hash() self.log.info( f"Puzzle at index {index} wallet ID {wallet_id} puzzle hash {puzzlehash.hex()}" ) derivation_paths.append( DerivationRecord( uint32(index), puzzlehash, pubkey, target_wallet.type(), uint32(target_wallet.id()), )) await self.puzzle_store.add_derivation_paths(derivation_paths) if unused > 0: await self.puzzle_store.set_used_up_to(uint32(unused - 1))
def coin_record_from_row(self, row: sqlite3.Row) -> WalletCoinRecord: coin = Coin(bytes32(bytes.fromhex(row[6])), bytes32(bytes.fromhex(row[5])), uint64.from_bytes(row[7])) return WalletCoinRecord(coin, uint32(row[1]), uint32(row[2]), bool(row[3]), bool(row[4]), WalletType(row[8]), row[9])
async def test_change_pools_reorg(self, setup): """This tests Pool A -> escaping -> reorg -> escaping -> Pool B""" full_nodes, wallets, receive_address, client, rpc_cleanup = setup our_ph = receive_address[0] pool_a_ph = receive_address[1] pool_b_ph = await wallets[1].get_new_puzzlehash() full_node_api = full_nodes[0] WAIT_SECS = 30 try: summaries_response = await client.get_wallets() for summary in summaries_response: if WalletType(int(summary["type"])) == WalletType.POOLING_WALLET: assert False async def have_chia(): await self.farm_blocks(full_node_api, our_ph, 1) return (await wallets[0].get_confirmed_balance()) > 0 await time_out_assert(timeout=WAIT_SECS, function=have_chia) creation_tx: TransactionRecord = await client.create_new_pool_wallet( pool_a_ph, "https://pool-a.org", 5, "localhost:5000", "new", "FARMING_TO_POOL" ) await time_out_assert( 10, full_node_api.full_node.mempool_manager.get_spendbundle, creation_tx.spend_bundle, creation_tx.name, ) await self.farm_blocks(full_node_api, our_ph, 6) assert full_node_api.full_node.mempool_manager.get_spendbundle(creation_tx.name) is None summaries_response = await client.get_wallets() wallet_id: Optional[int] = None for summary in summaries_response: if WalletType(int(summary["type"])) == WalletType.POOLING_WALLET: wallet_id = summary["id"] assert wallet_id is not None status: PoolWalletInfo = (await client.pw_status(wallet_id))[0] assert status.current.state == PoolSingletonState.FARMING_TO_POOL.value assert status.target is None async def status_is_farming_to_pool(): await self.farm_blocks(full_node_api, our_ph, 1) pw_status: PoolWalletInfo = (await client.pw_status(wallet_id))[0] return pw_status.current.state == PoolSingletonState.FARMING_TO_POOL.value await time_out_assert(timeout=WAIT_SECS, function=status_is_farming_to_pool) pw_info: PoolWalletInfo = (await client.pw_status(wallet_id))[0] assert pw_info.current.pool_url == "https://pool-a.org" assert pw_info.current.relative_lock_height == 5 original_height = full_node_api.full_node.blockchain.get_peak().height join_pool_tx: TransactionRecord = await client.pw_join_pool( wallet_id, pool_b_ph, "https://pool-b.org", 10, ) assert join_pool_tx is not None await time_out_assert( 10, full_node_api.full_node.mempool_manager.get_spendbundle, join_pool_tx.spend_bundle, join_pool_tx.name, ) await self.farm_blocks(full_node_api, our_ph, 1) async def status_is_leaving_no_blocks(): pw_status: PoolWalletInfo = (await client.pw_status(wallet_id))[0] return pw_status.current.state == PoolSingletonState.LEAVING_POOL.value async def status_is_farming_to_pool_no_blocks(): pw_status: PoolWalletInfo = (await client.pw_status(wallet_id))[0] return pw_status.current.state == PoolSingletonState.FARMING_TO_POOL.value await time_out_assert(timeout=WAIT_SECS, function=status_is_leaving_no_blocks) log.warning(f"Doing reorg: {original_height - 1} {original_height + 2}") current_blocks = await full_node_api.get_all_full_blocks() more_blocks = full_node_api.bt.get_consecutive_blocks( 3, farmer_reward_puzzle_hash=pool_a_ph, pool_reward_puzzle_hash=pool_b_ph, block_list_input=current_blocks[:-1], force_overflow=True, guarantee_transaction_block=True, seed=32 * b"4", transaction_data=join_pool_tx.spend_bundle, ) for block in more_blocks[-3:]: await full_node_api.full_node.respond_block(RespondBlock(block)) await asyncio.sleep(5) await time_out_assert(timeout=WAIT_SECS, function=status_is_leaving_no_blocks) # Eventually, leaves pool await time_out_assert(timeout=WAIT_SECS, function=status_is_farming_to_pool) finally: client.close() await client.await_closed() await rpc_cleanup()
async def test_change_pools(self, setup): """This tests Pool A -> escaping -> Pool B""" full_nodes, wallets, receive_address, client, rpc_cleanup = setup our_ph = receive_address[0] pool_a_ph = receive_address[1] pool_b_ph = await wallets[1].get_new_puzzlehash() full_node_api = full_nodes[0] WAIT_SECS = 200 try: summaries_response = await client.get_wallets() for summary in summaries_response: if WalletType(int(summary["type"])) == WalletType.POOLING_WALLET: assert False async def have_chia(): await self.farm_blocks(full_node_api, our_ph, 1) return (await wallets[0].get_confirmed_balance()) > 0 await time_out_assert(timeout=WAIT_SECS, function=have_chia) creation_tx: TransactionRecord = await client.create_new_pool_wallet( pool_a_ph, "https://pool-a.org", 5, "localhost:5000", "new", "FARMING_TO_POOL" ) await time_out_assert( 10, full_node_api.full_node.mempool_manager.get_spendbundle, creation_tx.spend_bundle, creation_tx.name, ) await self.farm_blocks(full_node_api, our_ph, 6) assert full_node_api.full_node.mempool_manager.get_spendbundle(creation_tx.name) is None summaries_response = await client.get_wallets() wallet_id: Optional[int] = None for summary in summaries_response: if WalletType(int(summary["type"])) == WalletType.POOLING_WALLET: wallet_id = summary["id"] assert wallet_id is not None status: PoolWalletInfo = (await client.pw_status(wallet_id))[0] assert status.current.state == PoolSingletonState.FARMING_TO_POOL.value assert status.target is None async def status_is_farming_to_pool(): await self.farm_blocks(full_node_api, our_ph, 1) pw_status: PoolWalletInfo = (await client.pw_status(wallet_id))[0] return pw_status.current.state == PoolSingletonState.FARMING_TO_POOL.value await time_out_assert(timeout=WAIT_SECS, function=status_is_farming_to_pool) pw_info: PoolWalletInfo = (await client.pw_status(wallet_id))[0] assert pw_info.current.pool_url == "https://pool-a.org" assert pw_info.current.relative_lock_height == 5 status: PoolWalletInfo = (await client.pw_status(wallet_id))[0] join_pool_tx: TransactionRecord = await client.pw_join_pool( wallet_id, pool_b_ph, "https://pool-b.org", 10, ) assert join_pool_tx is not None async def status_is_leaving(): await self.farm_blocks(full_node_api, our_ph, 1) pw_status: PoolWalletInfo = (await client.pw_status(wallet_id))[0] return pw_status.current.state == PoolSingletonState.LEAVING_POOL.value await time_out_assert(timeout=WAIT_SECS, function=status_is_leaving) pw_info: PoolWalletInfo = (await client.pw_status(wallet_id))[0] await time_out_assert(timeout=WAIT_SECS, function=status_is_farming_to_pool) pw_info: PoolWalletInfo = (await client.pw_status(wallet_id))[0] assert pw_info.current.pool_url == "https://pool-b.org" assert pw_info.current.relative_lock_height == 10 assert len(await wallets[0].wallet_state_manager.tx_store.get_unconfirmed_for_wallet(2)) == 0 finally: client.close() await client.await_closed() await rpc_cleanup()
async def test_leave_pool(self, setup): """This tests self-pooling -> pooling -> escaping -> self pooling""" full_nodes, wallets, receive_address, client, rpc_cleanup = setup our_ph = receive_address[0] pool_ph = receive_address[1] full_node_api = full_nodes[0] WAIT_SECS = 200 try: summaries_response = await client.get_wallets() for summary in summaries_response: if WalletType(int(summary["type"])) == WalletType.POOLING_WALLET: assert False async def have_chia(): await self.farm_blocks(full_node_api, our_ph, 1) return (await wallets[0].get_confirmed_balance()) > 0 await time_out_assert(timeout=WAIT_SECS, function=have_chia) creation_tx: TransactionRecord = await client.create_new_pool_wallet( our_ph, "", 0, "localhost:5000", "new", "SELF_POOLING" ) await time_out_assert( 10, full_node_api.full_node.mempool_manager.get_spendbundle, creation_tx.spend_bundle, creation_tx.name, ) await self.farm_blocks(full_node_api, our_ph, 6) assert full_node_api.full_node.mempool_manager.get_spendbundle(creation_tx.name) is None summaries_response = await client.get_wallets() wallet_id: Optional[int] = None for summary in summaries_response: if WalletType(int(summary["type"])) == WalletType.POOLING_WALLET: wallet_id = summary["id"] assert wallet_id is not None status: PoolWalletInfo = (await client.pw_status(wallet_id))[0] assert status.current.state == PoolSingletonState.SELF_POOLING.value assert status.target is None join_pool_tx: TransactionRecord = await client.pw_join_pool( wallet_id, pool_ph, "https://pool.example.com", 5, ) assert join_pool_tx is not None status: PoolWalletInfo = (await client.pw_status(wallet_id))[0] assert status.current.state == PoolSingletonState.SELF_POOLING.value assert status.current.to_json_dict() == { "owner_pubkey": "0xb286bbf7a10fa058d2a2a758921377ef00bb7f8143e1bd40dd195ae918dbef42cfc481140f01b9eae13b430a0c8fe304", "pool_url": None, "relative_lock_height": 0, "state": 1, "target_puzzle_hash": "0x738127e26cb61ffe5530ce0cef02b5eeadb1264aa423e82204a6d6bf9f31c2b7", "version": 1, } assert status.target.to_json_dict() == { "owner_pubkey": "0xb286bbf7a10fa058d2a2a758921377ef00bb7f8143e1bd40dd195ae918dbef42cfc481140f01b9eae13b430a0c8fe304", "pool_url": "https://pool.example.com", "relative_lock_height": 5, "state": 3, "target_puzzle_hash": "0x9ba327777484b8300d60427e4f3b776ac81948dfedd069a8d3f55834e101696e", "version": 1, } async def status_is_farming_to_pool(): await self.farm_blocks(full_node_api, our_ph, 1) pw_status: PoolWalletInfo = (await client.pw_status(wallet_id))[0] return pw_status.current.state == PoolSingletonState.FARMING_TO_POOL.value await time_out_assert(timeout=WAIT_SECS, function=status_is_farming_to_pool) status: PoolWalletInfo = (await client.pw_status(wallet_id))[0] leave_pool_tx: TransactionRecord = await client.pw_self_pool(wallet_id) assert leave_pool_tx.wallet_id == wallet_id assert leave_pool_tx.amount == 1 async def status_is_leaving(): await self.farm_blocks(full_node_api, our_ph, 1) pw_status: PoolWalletInfo = (await client.pw_status(wallet_id))[0] return pw_status.current.state == PoolSingletonState.LEAVING_POOL.value await time_out_assert(timeout=WAIT_SECS, function=status_is_leaving) pw_info: PoolWalletInfo = (await client.pw_status(wallet_id))[0] async def status_is_self_pooling(): # Farm enough blocks to wait for relative_lock_height await self.farm_blocks(full_node_api, our_ph, 1) pw_status: PoolWalletInfo = (await client.pw_status(wallet_id))[0] return pw_status.current.state == PoolSingletonState.SELF_POOLING.value await time_out_assert(timeout=WAIT_SECS, function=status_is_self_pooling) pw_info: PoolWalletInfo = (await client.pw_status(wallet_id))[0] assert len(await wallets[0].wallet_state_manager.tx_store.get_unconfirmed_for_wallet(2)) == 0 finally: client.close() await client.await_closed() await rpc_cleanup()
async def test_self_pooling_to_pooling(self, setup, fee, trusted): """This tests self-pooling -> pooling""" num_blocks = 4 # Num blocks to farm at a time total_blocks = 0 # Total blocks farmed so far full_nodes, wallet_nodes, receive_address, client, rpc_cleanup = setup wallets = [wallet_n.wallet_state_manager.main_wallet for wallet_n in wallet_nodes] wallet_node_0 = wallet_nodes[0] our_ph = receive_address[0] pool_ph = receive_address[1] full_node_api = full_nodes[0] if trusted: wallet_node_0.config["trusted_peers"] = { full_node_api.full_node.server.node_id.hex(): full_node_api.full_node.server.node_id.hex() } else: wallet_node_0.config["trusted_peers"] = {} await wallet_node_0.server.start_client( PeerInfo(self_hostname, uint16(full_node_api.full_node.server._port)), None ) try: total_blocks += await self.farm_blocks(full_node_api, our_ph, num_blocks) total_block_rewards = await self.get_total_block_rewards(total_blocks) await time_out_assert(10, wallets[0].get_unconfirmed_balance, total_block_rewards) await time_out_assert(10, wallets[0].get_confirmed_balance, total_block_rewards) await time_out_assert(10, wallets[0].get_spendable_balance, total_block_rewards) assert total_block_rewards > 0 summaries_response = await client.get_wallets() for summary in summaries_response: if WalletType(int(summary["type"])) == WalletType.POOLING_WALLET: assert False creation_tx: TransactionRecord = await client.create_new_pool_wallet( our_ph, "", 0, "localhost:5000", "new", "SELF_POOLING", fee ) creation_tx_2: TransactionRecord = await client.create_new_pool_wallet( our_ph, "", 0, "localhost:5000", "new", "SELF_POOLING", fee ) await time_out_assert( 10, full_node_api.full_node.mempool_manager.get_spendbundle, creation_tx.spend_bundle, creation_tx.name, ) await time_out_assert( 10, full_node_api.full_node.mempool_manager.get_spendbundle, creation_tx_2.spend_bundle, creation_tx_2.name, ) await self.farm_blocks(full_node_api, our_ph, 6) assert full_node_api.full_node.mempool_manager.get_spendbundle(creation_tx.name) is None summaries_response = await client.get_wallets() wallet_id: Optional[int] = None wallet_id_2: Optional[int] = None for summary in summaries_response: if WalletType(int(summary["type"])) == WalletType.POOLING_WALLET: if wallet_id is not None: wallet_id_2 = summary["id"] else: wallet_id = summary["id"] await asyncio.sleep(1) assert wallet_id is not None assert wallet_id_2 is not None status: PoolWalletInfo = (await client.pw_status(wallet_id))[0] status_2: PoolWalletInfo = (await client.pw_status(wallet_id_2))[0] assert status.current.state == PoolSingletonState.SELF_POOLING.value assert status_2.current.state == PoolSingletonState.SELF_POOLING.value assert status.target is None assert status_2.target is None log.warning("JOINING POOL") join_pool_tx: TransactionRecord = await client.pw_join_pool( wallet_id, pool_ph, "https://pool.example.com", 10, fee, ) join_pool_tx_2: TransactionRecord = await client.pw_join_pool( wallet_id_2, pool_ph, "https://pool.example.com", 10, fee, ) assert join_pool_tx is not None assert join_pool_tx_2 is not None status: PoolWalletInfo = (await client.pw_status(wallet_id))[0] status_2: PoolWalletInfo = (await client.pw_status(wallet_id_2))[0] async def tx_is_in_mempool(wid, tx: TransactionRecord): fetched: Optional[TransactionRecord] = await client.get_transaction(wid, tx.name) return fetched is not None and fetched.is_in_mempool() await time_out_assert(5, tx_is_in_mempool, True, wallet_id, join_pool_tx) await time_out_assert(5, tx_is_in_mempool, True, wallet_id_2, join_pool_tx_2) assert status.current.state == PoolSingletonState.SELF_POOLING.value assert status.target is not None assert status.target.state == PoolSingletonState.FARMING_TO_POOL.value assert status_2.current.state == PoolSingletonState.SELF_POOLING.value assert status_2.target is not None assert status_2.target.state == PoolSingletonState.FARMING_TO_POOL.value await self.farm_blocks(full_node_api, our_ph, 6) total_blocks += await self.farm_blocks(full_node_api, our_ph, num_blocks) async def status_is_farming_to_pool(w_id: int): pw_status: PoolWalletInfo = (await client.pw_status(w_id))[0] return pw_status.current.state == PoolSingletonState.FARMING_TO_POOL.value await time_out_assert(20, status_is_farming_to_pool, True, wallet_id) await time_out_assert(20, status_is_farming_to_pool, True, wallet_id_2) assert len(await wallets[0].wallet_state_manager.tx_store.get_unconfirmed_for_wallet(2)) == 0 finally: client.close() await client.await_closed() await rpc_cleanup()
async def test_absorb_self(self, one_wallet_node_and_rpc): client, wallet_node_0, full_node_api = one_wallet_node_and_rpc wallet_0 = wallet_node_0.wallet_state_manager.main_wallet our_ph = await wallet_0.get_new_puzzlehash() summaries_response = await client.get_wallets() for summary in summaries_response: if WalletType(int(summary["type"])) == WalletType.POOLING_WALLET: assert False creation_tx: TransactionRecord = await client.create_new_pool_wallet( our_ph, "", 0, "localhost:5000", "new", "SELF_POOLING" ) await time_out_assert( 10, full_node_api.full_node.mempool_manager.get_spendbundle, creation_tx.spend_bundle, creation_tx.name, ) await self.farm_blocks(full_node_api, our_ph, 1) await asyncio.sleep(2) status: PoolWalletInfo = (await client.pw_status(2))[0] assert status.current.state == PoolSingletonState.SELF_POOLING.value plot_id: bytes32 = self.create_pool_plot(status.p2_singleton_puzzle_hash) all_blocks = await full_node_api.get_all_full_blocks() blocks = bt.get_consecutive_blocks( 3, block_list_input=all_blocks, force_plot_id=plot_id, farmer_reward_puzzle_hash=our_ph, guarantee_transaction_block=True, ) await full_node_api.full_node.respond_block(full_node_protocol.RespondBlock(blocks[-3])) await full_node_api.full_node.respond_block(full_node_protocol.RespondBlock(blocks[-2])) await full_node_api.full_node.respond_block(full_node_protocol.RespondBlock(blocks[-1])) await asyncio.sleep(2) bal = await client.get_wallet_balance(2) assert bal["confirmed_wallet_balance"] == 2 * 1750000000000 # Claim 2 * 1.75, and farm a new 1.75 absorb_tx: TransactionRecord = await client.pw_absorb_rewards(2) await time_out_assert( 5, full_node_api.full_node.mempool_manager.get_spendbundle, absorb_tx.spend_bundle, absorb_tx.name, ) await self.farm_blocks(full_node_api, our_ph, 2) await asyncio.sleep(2) new_status: PoolWalletInfo = (await client.pw_status(2))[0] assert status.current == new_status.current assert status.tip_singleton_coin_id != new_status.tip_singleton_coin_id bal = await client.get_wallet_balance(2) assert bal["confirmed_wallet_balance"] == 1 * 1750000000000 # Claim another 1.75 absorb_tx: TransactionRecord = await client.pw_absorb_rewards(2) absorb_tx.spend_bundle.debug() await time_out_assert( 5, full_node_api.full_node.mempool_manager.get_spendbundle, absorb_tx.spend_bundle, absorb_tx.name, ) await self.farm_blocks(full_node_api, our_ph, 2) await asyncio.sleep(2) bal = await client.get_wallet_balance(2) assert bal["confirmed_wallet_balance"] == 0 assert len(await wallet_node_0.wallet_state_manager.tx_store.get_unconfirmed_for_wallet(2)) == 0 tr: TransactionRecord = await client.send_transaction( 1, 100, encode_puzzle_hash(status.p2_singleton_puzzle_hash, "txch") ) await time_out_assert( 10, full_node_api.full_node.mempool_manager.get_spendbundle, tr.spend_bundle, tr.name, ) await self.farm_blocks(full_node_api, our_ph, 2) # Balance ignores non coinbase TX bal = await client.get_wallet_balance(2) assert bal["confirmed_wallet_balance"] == 0 with pytest.raises(ValueError): await client.pw_absorb_rewards(2) self.delete_plot(plot_id)
async def test_create_multiple_pool_wallets(self, one_wallet_node_and_rpc): client, wallet_node_0, full_node_api = one_wallet_node_and_rpc wallet_0 = wallet_node_0.wallet_state_manager.main_wallet our_ph_1 = await wallet_0.get_new_puzzlehash() our_ph_2 = await wallet_0.get_new_puzzlehash() summaries_response = await client.get_wallets() for summary in summaries_response: if WalletType(int(summary["type"])) == WalletType.POOLING_WALLET: assert False creation_tx: TransactionRecord = await client.create_new_pool_wallet( our_ph_1, "", 0, "localhost:5000", "new", "SELF_POOLING" ) creation_tx_2: TransactionRecord = await client.create_new_pool_wallet( our_ph_1, "localhost", 12, "localhost:5000", "new", "FARMING_TO_POOL" ) await time_out_assert( 10, full_node_api.full_node.mempool_manager.get_spendbundle, creation_tx.spend_bundle, creation_tx.name, ) await time_out_assert( 10, full_node_api.full_node.mempool_manager.get_spendbundle, creation_tx_2.spend_bundle, creation_tx_2.name, ) await self.farm_blocks(full_node_api, our_ph_2, 6) assert full_node_api.full_node.mempool_manager.get_spendbundle(creation_tx.name) is None assert full_node_api.full_node.mempool_manager.get_spendbundle(creation_tx_2.name) is None await asyncio.sleep(3) status_2: PoolWalletInfo = (await client.pw_status(2))[0] status_3: PoolWalletInfo = (await client.pw_status(3))[0] if status_2.current.state == PoolSingletonState.SELF_POOLING.value: assert status_3.current.state == PoolSingletonState.FARMING_TO_POOL.value else: assert status_2.current.state == PoolSingletonState.FARMING_TO_POOL.value assert status_3.current.state == PoolSingletonState.SELF_POOLING.value full_config: Dict = load_config(wallet_0.wallet_state_manager.root_path, "config.yaml") pool_list: List[Dict] = full_config["pool"]["pool_list"] assert len(pool_list) == 2 p2_singleton_ph_2: bytes32 = status_2.p2_singleton_puzzle_hash p2_singleton_ph_3: bytes32 = status_3.p2_singleton_puzzle_hash assert ( await wallet_node_0.wallet_state_manager.interested_store.get_interested_puzzle_hash_wallet_id( p2_singleton_ph_2 ) ) is not None assert ( await wallet_node_0.wallet_state_manager.interested_store.get_interested_puzzle_hash_wallet_id( p2_singleton_ph_3 ) ) is not None assert len(await wallet_node_0.wallet_state_manager.tx_store.get_unconfirmed_for_wallet(2)) == 0 assert len(await wallet_node_0.wallet_state_manager.tx_store.get_unconfirmed_for_wallet(3)) == 0 # Doing a reorg reverts and removes the pool wallets await full_node_api.reorg_from_index_to_new_index(ReorgProtocol(uint32(0), uint32(20), our_ph_2)) await asyncio.sleep(5) summaries_response = await client.get_wallets() assert len(summaries_response) == 1 with pytest.raises(ValueError): await client.pw_status(2) with pytest.raises(ValueError): await client.pw_status(3) # It also removed interested PH, so we can recreated the pool wallet with another wallet_id later assert ( await wallet_node_0.wallet_state_manager.interested_store.get_interested_puzzle_hash_wallet_id( p2_singleton_ph_2 ) ) is None assert ( await wallet_node_0.wallet_state_manager.interested_store.get_interested_puzzle_hash_wallet_id( p2_singleton_ph_3 ) ) is None
async def pprint_pool_wallet_state( wallet_client: WalletRpcClient, wallet_id: int, pool_wallet_info: PoolWalletInfo, address_prefix: str, pool_state_dict: Dict, unconfirmed_transactions: List[TransactionRecord], ): if pool_wallet_info.current.state == PoolSingletonState.LEAVING_POOL and pool_wallet_info.target is None: expected_leave_height = pool_wallet_info.singleton_block_height + pool_wallet_info.current.relative_lock_height print( f"Current state: INVALID_STATE. Please leave/join again after block height {expected_leave_height}" ) else: print( f"Current state: {PoolSingletonState(pool_wallet_info.current.state).name}" ) print( f"Current state from block height: {pool_wallet_info.singleton_block_height}" ) print(f"Launcher ID: {pool_wallet_info.launcher_id}") print( "Target address (not for plotting): " f"{encode_puzzle_hash(pool_wallet_info.current.target_puzzle_hash, address_prefix)}" ) print(f"Owner public key: {pool_wallet_info.current.owner_pubkey}") print( f"P2 singleton address (pool contract address for plotting): " f"{encode_puzzle_hash(pool_wallet_info.p2_singleton_puzzle_hash, address_prefix)}" ) if pool_wallet_info.target is not None: print( f"Target state: {PoolSingletonState(pool_wallet_info.target.state).name}" ) print(f"Target pool URL: {pool_wallet_info.target.pool_url}") if pool_wallet_info.current.state == PoolSingletonState.SELF_POOLING.value: balances: Dict = await wallet_client.get_wallet_balance(str(wallet_id)) balance = balances["confirmed_wallet_balance"] typ = WalletType(int(WalletType.POOLING_WALLET)) address_prefix, scale = wallet_coin_unit(typ, address_prefix) print( f"Claimable balance: {print_balance(balance, scale, address_prefix)}" ) if pool_wallet_info.current.state == PoolSingletonState.FARMING_TO_POOL: print(f"Current pool URL: {pool_wallet_info.current.pool_url}") if pool_wallet_info.launcher_id in pool_state_dict: print( f"Current difficulty: {pool_state_dict[pool_wallet_info.launcher_id]['current_difficulty']}" ) print( f"Points balance: {pool_state_dict[pool_wallet_info.launcher_id]['current_points']}" ) print( f"Relative lock height: {pool_wallet_info.current.relative_lock_height} blocks" ) payout_instructions: str = pool_state_dict[ pool_wallet_info.launcher_id]["pool_config"]["payout_instructions"] try: payout_address = encode_puzzle_hash( bytes32.fromhex(payout_instructions), address_prefix) print( f"Payout instructions (pool will pay to this address): {payout_address}" ) except Exception: print( f"Payout instructions (pool will pay you with this): {payout_instructions}" ) if pool_wallet_info.current.state == PoolSingletonState.LEAVING_POOL: expected_leave_height = pool_wallet_info.singleton_block_height + pool_wallet_info.current.relative_lock_height if pool_wallet_info.target is not None: print( f"Expected to leave after block height: {expected_leave_height}" )
async def pprint_pool_wallet_state( wallet_client: WalletRpcClient, wallet_id: int, pool_wallet_info: PoolWalletInfo, address_prefix: str, pool_state_dict: Dict, plot_counts: Counter, ): if pool_wallet_info.current.state == PoolSingletonState.LEAVING_POOL and pool_wallet_info.target is None: expected_leave_height = pool_wallet_info.singleton_block_height + pool_wallet_info.current.relative_lock_height print( f"Current state: INVALID_STATE. Please leave/join again after block height {expected_leave_height}" ) else: print( f"Current state: {PoolSingletonState(pool_wallet_info.current.state).name}" ) print( f"Current state from block height: {pool_wallet_info.singleton_block_height}" ) print(f"Launcher ID: {pool_wallet_info.launcher_id}") print( "Target address (not for plotting): " f"{encode_puzzle_hash(pool_wallet_info.current.target_puzzle_hash, address_prefix)}" ) print( f"Number of plots: {plot_counts[pool_wallet_info.p2_singleton_puzzle_hash]}" ) print(f"Owner public key: {pool_wallet_info.current.owner_pubkey}") print( f"Pool contract address (use ONLY for plotting - do not send money to this address): " f"{encode_puzzle_hash(pool_wallet_info.p2_singleton_puzzle_hash, address_prefix)}" ) if pool_wallet_info.target is not None: print( f"Target state: {PoolSingletonState(pool_wallet_info.target.state).name}" ) print(f"Target pool URL: {pool_wallet_info.target.pool_url}") if pool_wallet_info.current.state == PoolSingletonState.SELF_POOLING.value: balances: Dict = await wallet_client.get_wallet_balance(str(wallet_id)) balance = balances["confirmed_wallet_balance"] typ = WalletType(int(WalletType.POOLING_WALLET)) address_prefix, scale = wallet_coin_unit(typ, address_prefix) print( f"Claimable balance: {print_balance(balance, scale, address_prefix)}" ) if pool_wallet_info.current.state == PoolSingletonState.FARMING_TO_POOL: print(f"Current pool URL: {pool_wallet_info.current.pool_url}") if pool_wallet_info.launcher_id in pool_state_dict: pool_state = pool_state_dict[pool_wallet_info.launcher_id] print( f"Current difficulty: {pool_state_dict[pool_wallet_info.launcher_id]['current_difficulty']}" ) print( f"Points balance: {pool_state_dict[pool_wallet_info.launcher_id]['current_points']}" ) points_found_24h = [ points for timestamp, points in pool_state["points_found_24h"] ] points_acknowledged_24h = [ points for timestamp, points in pool_state["points_acknowledged_24h"] ] summed_points_found_24h = sum(points_found_24h) summed_points_acknowledged_24h = sum(points_acknowledged_24h) if summed_points_found_24h == 0: success_pct = 0.0 else: success_pct = summed_points_acknowledged_24h / summed_points_found_24h print(f"Points found (24h): {summed_points_found_24h}") print(f"Percent Successful Points (24h): {success_pct:.2%}") print( f"Relative lock height: {pool_wallet_info.current.relative_lock_height} blocks" ) payout_instructions: str = pool_state_dict[ pool_wallet_info.launcher_id]["pool_config"]["payout_instructions"] try: payout_address = encode_puzzle_hash( bytes32.fromhex(payout_instructions), address_prefix) print( f"Payout instructions (pool will pay to this address): {payout_address}" ) except Exception: print( f"Payout instructions (pool will pay you with this): {payout_instructions}" ) if pool_wallet_info.current.state == PoolSingletonState.LEAVING_POOL: expected_leave_height = pool_wallet_info.singleton_block_height + pool_wallet_info.current.relative_lock_height if pool_wallet_info.target is not None: print( f"Expected to leave after block height: {expected_leave_height}" )
async def show(args: dict, wallet_client: WalletRpcClient, fingerprint: int) -> None: config = load_config(DEFAULT_ROOT_PATH, "config.yaml") self_hostname = config["self_hostname"] farmer_rpc_port = config["farmer"]["rpc_port"] farmer_client = await FarmerRpcClient.create(self_hostname, uint16(farmer_rpc_port), DEFAULT_ROOT_PATH, config) address_prefix = config["network_overrides"]["config"][ config["selected_network"]]["address_prefix"] summaries_response = await wallet_client.get_wallets() wallet_id_passed_in = args.get("id", None) plot_counts: Counter = Counter() try: pool_state_list: List = (await farmer_client.get_pool_state())["pool_state"] harvesters = await farmer_client.get_harvesters() for d in harvesters["harvesters"]: for plot in d["plots"]: if plot.get("pool_contract_puzzle_hash", None) is not None: # Non pooled plots will have a None pool_contract_puzzle_hash plot_counts[hexstr_to_bytes( plot["pool_contract_puzzle_hash"])] += 1 except Exception as e: if isinstance(e, aiohttp.ClientConnectorError): print( f"Connection error. Check if farmer is running at {farmer_rpc_port}." f" You can run the farmer by:\n chia start farmer-only") else: print(f"Exception from 'wallet' {e}") farmer_client.close() await farmer_client.await_closed() return pool_state_dict: Dict[bytes32, Dict] = { bytes32.from_hexstr(pool_state_item["pool_config"]["launcher_id"]): pool_state_item for pool_state_item in pool_state_list } if wallet_id_passed_in is not None: for summary in summaries_response: typ = WalletType(int(summary["type"])) if summary[ "id"] == wallet_id_passed_in and typ != WalletType.POOLING_WALLET: print( f"Wallet with id: {wallet_id_passed_in} is not a pooling wallet. Please provide a different id." ) return pool_wallet_info, _ = await wallet_client.pw_status(wallet_id_passed_in ) await pprint_pool_wallet_state( wallet_client, wallet_id_passed_in, pool_wallet_info, address_prefix, pool_state_dict, plot_counts, ) else: print(f"Wallet height: {await wallet_client.get_height_info()}") print( f"Sync status: {'Synced' if (await wallet_client.get_synced()) else 'Not synced'}" ) for summary in summaries_response: wallet_id = summary["id"] typ = WalletType(int(summary["type"])) if typ == WalletType.POOLING_WALLET: print(f"Wallet id {wallet_id}: ") pool_wallet_info, _ = await wallet_client.pw_status(wallet_id) await pprint_pool_wallet_state( wallet_client, wallet_id, pool_wallet_info, address_prefix, pool_state_dict, plot_counts, ) print("") farmer_client.close() await farmer_client.await_closed()
async def test_self_pooling_to_pooling(self, setup): """This tests self-pooling -> pooling""" num_blocks = 4 # Num blocks to farm at a time total_blocks = 0 # Total blocks farmed so far full_nodes, wallets, receive_address, client, rpc_cleanup = setup our_ph = receive_address[0] pool_ph = receive_address[1] full_node_api = full_nodes[0] try: total_blocks += await self.farm_blocks(full_node_api, our_ph, num_blocks) total_block_rewards = await self.get_total_block_rewards(total_blocks) await time_out_assert(10, wallets[0].get_unconfirmed_balance, total_block_rewards) await time_out_assert(10, wallets[0].get_confirmed_balance, total_block_rewards) await time_out_assert(10, wallets[0].get_spendable_balance, total_block_rewards) assert total_block_rewards > 0 summaries_response = await client.get_wallets() for summary in summaries_response: if WalletType(int(summary["type"])) == WalletType.POOLING_WALLET: assert False creation_tx: TransactionRecord = await client.create_new_pool_wallet( our_ph, "", 0, "localhost:5000", "new", "SELF_POOLING" ) creation_tx_2: TransactionRecord = await client.create_new_pool_wallet( our_ph, "", 0, "localhost:5000", "new", "SELF_POOLING" ) await time_out_assert( 10, full_node_api.full_node.mempool_manager.get_spendbundle, creation_tx.spend_bundle, creation_tx.name, ) await time_out_assert( 10, full_node_api.full_node.mempool_manager.get_spendbundle, creation_tx_2.spend_bundle, creation_tx_2.name, ) await self.farm_blocks(full_node_api, our_ph, 6) assert full_node_api.full_node.mempool_manager.get_spendbundle(creation_tx.name) is None summaries_response = await client.get_wallets() wallet_id: Optional[int] = None wallet_id_2: Optional[int] = None for summary in summaries_response: if WalletType(int(summary["type"])) == WalletType.POOLING_WALLET: if wallet_id is not None: wallet_id_2 = summary["id"] else: wallet_id = summary["id"] assert wallet_id is not None assert wallet_id_2 is not None status: PoolWalletInfo = (await client.pw_status(wallet_id))[0] status_2: PoolWalletInfo = (await client.pw_status(wallet_id))[0] assert status.current.state == PoolSingletonState.SELF_POOLING.value assert status_2.current.state == PoolSingletonState.SELF_POOLING.value assert status.target is None assert status_2.target is None join_pool_tx: TransactionRecord = await client.pw_join_pool( wallet_id, pool_ph, "https://pool.example.com", 10, ) join_pool_tx_2: TransactionRecord = await client.pw_join_pool( wallet_id_2, pool_ph, "https://pool.example.com", 10, ) assert join_pool_tx is not None assert join_pool_tx_2 is not None status: PoolWalletInfo = (await client.pw_status(wallet_id))[0] status_2: PoolWalletInfo = (await client.pw_status(wallet_id_2))[0] assert status.current.state == PoolSingletonState.SELF_POOLING.value assert status.target is not None assert status.target.state == PoolSingletonState.FARMING_TO_POOL.value assert status_2.current.state == PoolSingletonState.SELF_POOLING.value assert status_2.target is not None assert status_2.target.state == PoolSingletonState.FARMING_TO_POOL.value await self.farm_blocks(full_node_api, our_ph, 6) total_blocks += await self.farm_blocks(full_node_api, our_ph, num_blocks) async def status_is_farming_to_pool(w_id: int): pw_status: PoolWalletInfo = (await client.pw_status(w_id))[0] return pw_status.current.state == PoolSingletonState.FARMING_TO_POOL.value await time_out_assert(20, status_is_farming_to_pool, True, wallet_id) await time_out_assert(20, status_is_farming_to_pool, True, wallet_id_2) assert len(await wallets[0].wallet_state_manager.tx_store.get_unconfirmed_for_wallet(2)) == 0 finally: client.close() await client.await_closed() await rpc_cleanup()