From d5ab67c5b81cba5041dd8f70282bc6bc17e1dd67 Mon Sep 17 00:00:00 2001 From: opendansor Date: Tue, 11 Jun 2024 13:22:39 -0700 Subject: [PATCH 1/3] Test Incentive E2E --- tests/e2e_tests/multistep/test_incentive.py | 287 ++++++++++++++++++++ 1 file changed, 287 insertions(+) create mode 100644 tests/e2e_tests/multistep/test_incentive.py diff --git a/tests/e2e_tests/multistep/test_incentive.py b/tests/e2e_tests/multistep/test_incentive.py new file mode 100644 index 0000000000..837cb694b7 --- /dev/null +++ b/tests/e2e_tests/multistep/test_incentive.py @@ -0,0 +1,287 @@ +import asyncio +import logging +import sys +import time + +import pytest + +import bittensor +from bittensor.commands import ( + RegisterCommand, + RegisterSubnetworkCommand, + StakeCommand, + RootRegisterCommand, + RootSetBoostCommand, +) +from tests.e2e_tests.utils import ( + setup_wallet, + template_path, + repo_name, +) + +logging.basicConfig(level=logging.INFO) + + +@pytest.mark.asyncio +async def test_incentive(local_chain): + # Register root as Alice - the subnet owner and validator + alice_keypair, alice_exec_command, alice_wallet_path = setup_wallet("//Alice") + alice_exec_command(RegisterSubnetworkCommand, ["s", "create"]) + # Verify subnet 1 created successfully + assert local_chain.query("SubtensorModule", "NetworksAdded", [1]).serialize() + + # Register Bob as miner + bob_keypair, bob_exec_command, bob_wallet_path = setup_wallet("//Bob") + + # Register Alice as neuron to the subnet + alice_exec_command( + RegisterCommand, + [ + "s", + "register", + "--netuid", + "1", + "--wallet.name", + "default", + "--wallet.hotkey", + "default", + "--wallet.path", + alice_wallet_path, + "--subtensor.network", + "local", + "--subtensor.chain_endpoint", + "ws://localhost:9945", + "--no_prompt", + ], + ) + + # Register Bob as neuron to the subnet + bob_exec_command( + RegisterCommand, + [ + "s", + "register", + "--netuid", + "1", + "--wallet.name", + "default", + "--wallet.hotkey", + "default", + "--subtensor.network", + "local", + "--subtensor.chain_endpoint", + "ws://localhost:9945", + "--no_prompt", + ], + ) + + subtensor = bittensor.subtensor(network="ws://localhost:9945") + # assert two neurons are in network + assert len(subtensor.neurons(netuid=1)) == 2 + + # Alice to stake to become to top neuron after the first epoch + alice_exec_command( + StakeCommand, + [ + "stake", + "add", + "--amount", + "10000", + ], + ) + + # register Bob as miner + cmd = " ".join( + [ + f"{sys.executable}", + f'"{template_path}{repo_name}/neurons/miner.py"', + "--no_prompt", + "--netuid", + "1", + "--subtensor.network", + "local", + "--subtensor.chain_endpoint", + "ws://localhost:9945", + "--wallet.path", + bob_wallet_path, + "--wallet.name", + "default", + "--wallet.hotkey", + "default", + "--logging.trace", + ] + ) + + miner_process = await asyncio.create_subprocess_shell( + cmd, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + ) + + # Function to write output to the log file + async def miner_write_output(stream): + log_file = "miner.log" + with open(log_file, "a") as f: + while True: + line = await stream.readline() + if not line: + break + f.write(line.decode()) + f.flush() + + # Create tasks to read stdout and stderr concurrently + asyncio.create_task(miner_write_output(miner_process.stdout)) + asyncio.create_task(miner_write_output(miner_process.stderr)) + + await asyncio.sleep( + 5 + ) # wait for 5 seconds for the metagraph to refresh with latest data + + # register Alice as validator + cmd = " ".join( + [ + f"{sys.executable}", + f'"{template_path}{repo_name}/neurons/validator.py"', + "--no_prompt", + "--netuid", + "1", + "--subtensor.network", + "local", + "--subtensor.chain_endpoint", + "ws://localhost:9945", + "--wallet.path", + alice_wallet_path, + "--wallet.name", + "default", + "--wallet.hotkey", + "default", + "--logging.trace", + ] + ) + # run validator in the background + + validator_process = await asyncio.create_subprocess_shell( + cmd, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE, + ) + + # Function to write output to the log file + async def validator_write_output(stream): + log_file = "validator.log" + with open(log_file, "a") as f: + while True: + line = await stream.readline() + if not line: + break + f.write(line.decode()) + f.flush() + + # Create tasks to read stdout and stderr concurrently + asyncio.create_task(validator_write_output(validator_process.stdout)) + asyncio.create_task(validator_write_output(validator_process.stderr)) + + await asyncio.sleep( + 5 + ) # wait for 5 seconds for the metagraph and subtensor to refresh with latest data + + # register validator with root network + alice_exec_command( + RootRegisterCommand, + [ + "root", + "register", + "--netuid", + "1", + "--wallet.name", + "default", + "--wallet.hotkey", + "default", + "--subtensor.chain_endpoint", + "ws://localhost:9945", + ], + ) + + alice_exec_command( + RootSetBoostCommand, + [ + "root", + "boost", + "--netuid", + "1", + "--increase", + "100", + "--wallet.name", + "default", + "--wallet.hotkey", + "default", + "--subtensor.chain_endpoint", + "ws://localhost:9945", + ], + ) + + # get latest metagraph + metagraph = bittensor.metagraph(netuid=1, network="ws://localhost:9945") + + # get current emissions + bob_neuron = metagraph.neurons[1] + assert bob_neuron.incentive == 0 + assert bob_neuron.consensus == 0 + assert bob_neuron.rank == 0 + assert bob_neuron.trust == 0 + + alice_neuron = metagraph.neurons[0] + assert alice_neuron.validator_permit is False + assert alice_neuron.dividends == 0 + assert alice_neuron.stake.tao == 9999.999999 + assert alice_neuron.validator_trust == 0 + + # wait until 360 blocks pass (subnet tempo) + wait_epoch(360, subtensor) + + # for some reason the weights do not get set through the template. Set weight manually. + alice_wallet = bittensor.wallet() + alice_wallet._hotkey = alice_keypair + subtensor._do_set_weights( + wallet=alice_wallet, + uids=[1], + vals=[65535], + netuid=1, + version_key=0, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + + # wait epoch until weight go into effect + wait_epoch(360, subtensor) + + # refresh metagraph + metagraph = bittensor.metagraph(netuid=1, network="ws://localhost:9945") + + # get current emissions and validate that Alice has gotten tao + bob_neuron = metagraph.neurons[1] + assert bob_neuron.incentive == 1 + assert bob_neuron.consensus == 1 + assert bob_neuron.rank == 1 + assert bob_neuron.trust == 1 + + alice_neuron = metagraph.neurons[0] + assert alice_neuron.validator_permit is True + assert alice_neuron.dividends == 1 + assert alice_neuron.stake.tao == 9999.999999 + assert alice_neuron.validator_trust == 1 + + +def wait_epoch(interval, subtensor): + current_block = subtensor.get_current_block() + next_tempo_block_start = (current_block - (current_block % interval)) + interval + while current_block < next_tempo_block_start: + time.sleep(1) # Wait for 1 second before checking the block number again + current_block = subtensor.get_current_block() + if current_block % 10 == 0: + print( + f"Current Block: {current_block} Next tempo at: {next_tempo_block_start}" + ) + logging.info( + f"Current Block: {current_block} Next tempo at: {next_tempo_block_start}" + ) From 0cad50177521472b536a5ca47126c38062681e0c Mon Sep 17 00:00:00 2001 From: opendansor Date: Tue, 11 Jun 2024 13:51:44 -0700 Subject: [PATCH 2/3] Update with comments --- tests/e2e_tests/multistep/test_axon.py | 13 +++++++++++++ tests/e2e_tests/multistep/test_dendrite.py | 10 ++++++++++ tests/e2e_tests/multistep/test_incentive.py | 19 +++++++++++++++++++ .../weights/test_commit_weights.py | 14 +++++++++++++- 4 files changed, 55 insertions(+), 1 deletion(-) diff --git a/tests/e2e_tests/multistep/test_axon.py b/tests/e2e_tests/multistep/test_axon.py index 770bed17e5..f23fb4da7b 100644 --- a/tests/e2e_tests/multistep/test_axon.py +++ b/tests/e2e_tests/multistep/test_axon.py @@ -15,6 +15,19 @@ repo_name, ) +""" +Test the axon mechanism. + +Verify that: +* axon is registered on network as a miner +* ip +* type +* port + +are set correctly, and that the miner is currently running + +""" + @pytest.mark.asyncio async def test_axon(local_chain): diff --git a/tests/e2e_tests/multistep/test_dendrite.py b/tests/e2e_tests/multistep/test_dendrite.py index 48b27e5bcd..8f7336e1de 100644 --- a/tests/e2e_tests/multistep/test_dendrite.py +++ b/tests/e2e_tests/multistep/test_dendrite.py @@ -21,6 +21,16 @@ logging.basicConfig(level=logging.INFO) +""" +Test the dendrites mechanism. + +Verify that: +* dendrite is registered on network as a validator +* stake successfully +* validator permit is set + +""" + @pytest.mark.asyncio async def test_dendrite(local_chain): diff --git a/tests/e2e_tests/multistep/test_incentive.py b/tests/e2e_tests/multistep/test_incentive.py index 837cb694b7..d4605faa6a 100644 --- a/tests/e2e_tests/multistep/test_incentive.py +++ b/tests/e2e_tests/multistep/test_incentive.py @@ -21,6 +21,25 @@ logging.basicConfig(level=logging.INFO) +""" +Test the incentive mechanism. + +Verify that for the miner: +* trust +* rank +* consensus +* incentive +are updated with proper values after an epoch has passed. + +For the validator verify that: +* validator_permit +* validator_trust +* dividends +* stake +are updated with proper values after an epoch has passed. + +""" + @pytest.mark.asyncio async def test_incentive(local_chain): diff --git a/tests/e2e_tests/subcommands/weights/test_commit_weights.py b/tests/e2e_tests/subcommands/weights/test_commit_weights.py index ad2ecb7b42..039fed3632 100644 --- a/tests/e2e_tests/subcommands/weights/test_commit_weights.py +++ b/tests/e2e_tests/subcommands/weights/test_commit_weights.py @@ -14,6 +14,18 @@ ) from tests.e2e_tests.utils import setup_wallet +""" +Test the Commit/Reveal weights mechanism. + +Verify that: +* Weights are commited +* weights are hashed with salt +--- after an epoch --- +* weights are un-hashed with salt +* weights are properly revealed + +""" + def test_commit_and_reveal_weights(local_chain): # Register root as Alice @@ -184,5 +196,5 @@ def test_commit_and_reveal_weights(local_chain): uids, weights_array ) assert ( - expected_weights[0] == revealed_weights.value[0][1] + expected_weights[0] == revealed_weights.value[0][1] ), f"Incorrect revealed weights. Expected: {expected_weights[0]}, Actual: {revealed_weights.value[0][1]}" From 760e7591ea13ade26100341d611e5c1fecec0292 Mon Sep 17 00:00:00 2001 From: opendansor Date: Tue, 11 Jun 2024 13:52:36 -0700 Subject: [PATCH 3/3] Update with comments --- tests/e2e_tests/subcommands/weights/test_commit_weights.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e_tests/subcommands/weights/test_commit_weights.py b/tests/e2e_tests/subcommands/weights/test_commit_weights.py index 039fed3632..53eec95da4 100644 --- a/tests/e2e_tests/subcommands/weights/test_commit_weights.py +++ b/tests/e2e_tests/subcommands/weights/test_commit_weights.py @@ -196,5 +196,5 @@ def test_commit_and_reveal_weights(local_chain): uids, weights_array ) assert ( - expected_weights[0] == revealed_weights.value[0][1] + expected_weights[0] == revealed_weights.value[0][1] ), f"Incorrect revealed weights. Expected: {expected_weights[0]}, Actual: {revealed_weights.value[0][1]}"