diff --git a/chia/_tests/cmds/test_farm_cmd.py b/chia/_tests/cmds/test_farm_cmd.py index 968152331e7b..65fee50e68e3 100644 --- a/chia/_tests/cmds/test_farm_cmd.py +++ b/chia/_tests/cmds/test_farm_cmd.py @@ -46,7 +46,15 @@ async def receiver_available() -> bool: wallet_rpc_port = wallet_service.rpc_server.webserver.listen_port farmer_rpc_port = farmer_service.rpc_server.webserver.listen_port - await summary(full_node_rpc_port, wallet_rpc_port, None, farmer_rpc_port, bt.root_path) + # Test with include_pool_rewards=False (original test) + await summary( + rpc_port=full_node_rpc_port, + wallet_rpc_port=wallet_rpc_port, + harvester_rpc_port=None, + farmer_rpc_port=farmer_rpc_port, + include_pool_rewards=False, + root_path=bt.root_path, + ) captured = capsys.readouterr() match = re.search(r"^.+(Farming status:.+)$", captured.out, re.DOTALL) @@ -64,3 +72,47 @@ async def receiver_available() -> bool: assert "e (effective)" in lines[8] assert "Estimated network space:" in lines[9] assert "Expected time to win:" in lines[10] + + # Test with include_pool_rewards=True + await summary( + rpc_port=full_node_rpc_port, + wallet_rpc_port=wallet_rpc_port, + harvester_rpc_port=None, + farmer_rpc_port=farmer_rpc_port, + include_pool_rewards=True, + root_path=bt.root_path, + ) + + captured = capsys.readouterr() + match = re.search(r"Farming status:.*", captured.out, re.DOTALL) + assert match, "no 'Farming status:' line" + output = match.group(0).strip() + lines = [line.strip() for line in output.splitlines()] + + # always check these first six lines + assert lines[0].startswith("Farming status:") + assert lines[1].startswith("Total chia farmed:") + assert lines[2].startswith("User transaction fees:") + assert lines[3].startswith("Farmer rewards:") + assert lines[4].startswith("Pool rewards:") + assert lines[5].startswith("Total rewards:") + + # decide where the harvester section starts + if "Current/Last height farmed:" in output: + # we saw the height-farmed block, so it occupies lines[6-8] + assert lines[6].startswith("Current/Last height farmed:") + assert lines[7].startswith("Blocks since last farmed:") + assert lines[8].startswith("Time since last farmed:") + harvester_idx = 9 + else: + # no height block, so harvester begins at line 6 + harvester_idx = 6 + + # now the harvester lines + assert lines[harvester_idx] == "Local Harvester" + assert "plots of size" in lines[harvester_idx + 1] + assert lines[harvester_idx + 2].startswith("Plot count for all harvesters:") + assert lines[harvester_idx + 3].startswith("Total size of plots:") + assert lines[harvester_idx + 4].startswith("Estimated network space:") + assert lines[harvester_idx + 5].startswith("Expected time to win:") + assert lines[harvester_idx + 6].startswith("Note:") diff --git a/chia/cmds/farm.py b/chia/cmds/farm.py index e14658a4b797..2fd131e39866 100644 --- a/chia/cmds/farm.py +++ b/chia/cmds/farm.py @@ -49,6 +49,13 @@ def farm_cmd() -> None: default=None, show_default=True, ) +@click.option( + "-i", + "--include-pool-rewards", + help="Include pool farming rewards in the total farmed amount", + is_flag=True, + default=False, +) @click.pass_context def summary_cmd( ctx: click.Context, @@ -56,6 +63,7 @@ def summary_cmd( wallet_rpc_port: Optional[int], harvester_rpc_port: Optional[int], farmer_rpc_port: Optional[int], + include_pool_rewards: bool, ) -> None: import asyncio @@ -67,6 +75,7 @@ def summary_cmd( wallet_rpc_port, harvester_rpc_port, farmer_rpc_port, + include_pool_rewards, root_path=ChiaCliContext.set_default(ctx).root_path, ) ) diff --git a/chia/cmds/farm_funcs.py b/chia/cmds/farm_funcs.py index b251edded023..560a65b39b76 100644 --- a/chia/cmds/farm_funcs.py +++ b/chia/cmds/farm_funcs.py @@ -49,9 +49,13 @@ async def get_average_block_time(rpc_port: Optional[int], root_path: Path) -> fl return (curr.timestamp - past_curr.timestamp) / (curr.height - past_curr.height) -async def get_wallets_stats(wallet_rpc_port: Optional[int], root_path: Path) -> Optional[dict[str, Any]]: +async def get_wallets_stats( + wallet_rpc_port: Optional[int], + root_path: Path, + include_pool_rewards: bool, +) -> Optional[dict[str, Any]]: async with get_any_service_client(WalletRpcClient, root_path, wallet_rpc_port) as (wallet_client, _): - return await wallet_client.get_farmed_amount() + return await wallet_client.get_farmed_amount(include_pool_rewards) async def get_challenges(root_path: Path, farmer_rpc_port: Optional[int]) -> Optional[list[dict[str, Any]]]: @@ -80,6 +84,7 @@ async def summary( wallet_rpc_port: Optional[int], harvester_rpc_port: Optional[int], farmer_rpc_port: Optional[int], + include_pool_rewards: bool, root_path: Path, ) -> None: harvesters_summary = await get_harvesters_summary(farmer_rpc_port, root_path) @@ -97,7 +102,7 @@ async def summary( wallet_not_ready: bool = False amounts = None try: - amounts = await get_wallets_stats(wallet_rpc_port, root_path) + amounts = await get_wallets_stats(wallet_rpc_port, root_path, include_pool_rewards) except CliRpcConnectionError: wallet_not_ready = True except Exception: @@ -120,8 +125,21 @@ async def summary( if amounts is not None: print(f"Total chia farmed: {amounts['farmed_amount'] / units['chia']}") print(f"User transaction fees: {amounts['fee_amount'] / units['chia']}") - print(f"Block rewards: {(amounts['farmer_reward_amount'] + amounts['pool_reward_amount']) / units['chia']}") - print(f"Last height farmed: {amounts['last_height_farmed']}") + if include_pool_rewards: + print(f"Farmer rewards: {amounts['farmer_reward_amount'] / units['chia']}") + print(f"Pool rewards: {amounts['pool_reward_amount'] / units['chia']}") + print(f"Total rewards: {(amounts['farmer_reward_amount'] + amounts['pool_reward_amount']) / units['chia']}") + if blockchain_state is not None and blockchain_state["peak"] is not None: + peak_height = blockchain_state["peak"].height + blocks_since_last_farm = peak_height - amounts["last_height_farmed"] + print(f"Current/Last height farmed: {peak_height}/{amounts['last_height_farmed']}") + print(f"Blocks since last farmed: {blocks_since_last_farm}") + print( + f"Time since last farmed: {format_minutes(int((blocks_since_last_farm * SECONDS_PER_BLOCK) / 60))}" + ) + else: + print(f"Block rewards: {(amounts['farmer_reward_amount'] + amounts['pool_reward_amount']) / units['chia']}") + print(f"Last height farmed: {amounts['last_height_farmed']}") class PlotStats: total_plot_size = 0 diff --git a/chia/wallet/wallet_rpc_api.py b/chia/wallet/wallet_rpc_api.py index ed4630b7d27e..d8fa30022417 100644 --- a/chia/wallet/wallet_rpc_api.py +++ b/chia/wallet/wallet_rpc_api.py @@ -3664,12 +3664,18 @@ async def get_farmed_amount(self, request: dict[str, Any]) -> EndpointResult: fee_amount = 0 blocks_won = 0 last_height_farmed = uint32(0) + + include_pool_rewards = request.get("include_pool_rewards", False) + for record in tx_records: if record.wallet_id not in self.service.wallet_state_manager.wallets: continue if record.type == TransactionType.COINBASE_REWARD.value: - if self.service.wallet_state_manager.wallets[record.wallet_id].type() == WalletType.POOLING_WALLET: - # Don't add pool rewards for pool wallets. + if ( + not include_pool_rewards + and self.service.wallet_state_manager.wallets[record.wallet_id].type() == WalletType.POOLING_WALLET + ): + # Don't add pool rewards for pool wallets unless explicitly requested continue pool_reward_amount += record.amount height = record.height_farmed(self.service.constants.GENESIS_CHALLENGE) diff --git a/chia/wallet/wallet_rpc_client.py b/chia/wallet/wallet_rpc_client.py index 88ae0084b14e..4205921a5bcc 100644 --- a/chia/wallet/wallet_rpc_client.py +++ b/chia/wallet/wallet_rpc_client.py @@ -427,8 +427,8 @@ async def extend_derivation_index(self, index: int) -> str: updated_index = response["index"] return str(updated_index) - async def get_farmed_amount(self) -> dict[str, Any]: - return await self.fetch("get_farmed_amount", {}) + async def get_farmed_amount(self, include_pool_rewards: bool = False) -> dict[str, Any]: + return await self.fetch("get_farmed_amount", {"include_pool_rewards": include_pool_rewards}) async def create_signed_transactions( self,