diff --git a/tests/e2e_tests/test_commit_reveal_v3.py b/tests/e2e_tests/test_commit_reveal_v3.py index 473c866f87..a31063cd47 100644 --- a/tests/e2e_tests/test_commit_reveal_v3.py +++ b/tests/e2e_tests/test_commit_reveal_v3.py @@ -53,9 +53,7 @@ async def test_commit_and_reveal_weights_cr3(local_chain, subtensor, alice_walle ), "Unable to enable commit reveal on the subnet" # Verify commit_reveal was enabled - assert subtensor.get_subnet_hyperparameters( - netuid=netuid, - ).commit_reveal_weights_enabled, "Failed to enable commit/reveal" + assert subtensor.commit_reveal_enabled(netuid), "Failed to enable commit/reveal" logging.console.info("Commit reveal enabled") # Change the weights rate limit on the subnet diff --git a/tests/e2e_tests/test_commit_weights.py b/tests/e2e_tests/test_commit_weights.py index e94baf3d6c..cb6b7fd885 100644 --- a/tests/e2e_tests/test_commit_weights.py +++ b/tests/e2e_tests/test_commit_weights.py @@ -45,9 +45,7 @@ async def test_commit_and_reveal_weights_legacy(local_chain, subtensor, alice_wa netuid, ), "Unable to enable commit reveal on the subnet" - assert subtensor.get_subnet_hyperparameters( - netuid=netuid, - ).commit_reveal_weights_enabled, "Failed to enable commit/reveal" + assert subtensor.commit_reveal_enabled(netuid), "Failed to enable commit/reveal" assert ( subtensor.get_subnet_hyperparameters(netuid=netuid).commit_reveal_period == 1 @@ -114,11 +112,9 @@ async def test_commit_and_reveal_weights_legacy(local_chain, subtensor, alice_wa assert commit_block > 0, f"Invalid block number: {commit_block}" # Query the WeightCommitRevealInterval storage map - reveal_periods = subtensor.query_module( - module="SubtensorModule", name="RevealPeriodEpochs", params=[netuid] - ) - periods = reveal_periods - assert periods > 0, "Invalid RevealPeriodEpochs" + assert ( + subtensor.get_subnet_reveal_period_epochs(netuid) > 0 + ), "Invalid RevealPeriodEpochs" # Wait until the reveal block range await wait_epoch(subtensor, netuid) @@ -187,9 +183,7 @@ async def test_commit_weights_uses_next_nonce(local_chain, subtensor, alice_wall netuid, ), "Unable to enable commit reveal on the subnet" - assert subtensor.get_subnet_hyperparameters( - netuid=netuid, - ).commit_reveal_weights_enabled, "Failed to enable commit/reveal" + assert subtensor.commit_reveal_enabled(netuid), "Failed to enable commit/reveal" assert ( subtensor.get_subnet_hyperparameters(netuid=netuid).commit_reveal_period == 1 diff --git a/tests/e2e_tests/test_delegate.py b/tests/e2e_tests/test_delegate.py new file mode 100644 index 0000000000..0bc980b281 --- /dev/null +++ b/tests/e2e_tests/test_delegate.py @@ -0,0 +1,330 @@ +import pytest + +from bittensor.core.chain_data.delegate_info import DelegateInfo, DelegatedInfo +from bittensor.utils.balance import Balance +from bittensor.utils.delegates_details import DelegatesDetails +from tests.e2e_tests.utils.chain_interactions import ( + decrease_take, + increase_take, + registry_set_identity, + sudo_set_admin_utils, +) + + +DEFAULT_DELEGATE_TAKE = 0.179995422293431 + + +def test_identity(subtensor, alice_wallet, bob_wallet): + """ + Tests: + - Check Delegate's default identity + - Update Delegate's identity + """ + + identities = subtensor.get_delegate_identities() + + assert alice_wallet.hotkey.ss58_address not in identities + + # Replace hotkey as it's the same as coldkey. + # It's required to edit identity later. + # Otherwise only subnet owner can do this. + alice_wallet.set_hotkey( + keypair=bob_wallet.hotkey, + encrypt=False, + overwrite=True, + ) + + subtensor.root_register( + alice_wallet, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + + identities = subtensor.get_delegate_identities() + + assert alice_wallet.hotkey.ss58_address not in identities + + success, error = registry_set_identity( + subtensor, + alice_wallet, + alice_wallet.hotkey.ss58_address, + display=b"Alice Display", + web=b"https://bittensor.com/", + ) + + assert error == "" + assert success is True + + identities = subtensor.get_delegate_identities() + + assert alice_wallet.hotkey.ss58_address in identities + + alice_identity = identities[alice_wallet.hotkey.ss58_address] + + assert alice_identity == DelegatesDetails( + additional=[], + display="Alice Display", + email="", + image="", + legal="", + pgp_fingerprint=None, + riot="", + twitter="", + web="https://bittensor.com/", + ) + + +def test_change_take(local_chain, subtensor, alice_wallet): + """ + Tests: + - Get default Delegate's take once registered in root subnet + - Increase and decreased Delegate's take + - Try corner cases (increase/decrease beyond allowed min/max) + """ + + success, error = decrease_take( + subtensor, + alice_wallet, + 0.1, + ) + + assert success is False + assert "`HotKeyAccountNotExists(Module)`" in error + + subtensor.root_register( + alice_wallet, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + + assert ( + subtensor.get_delegate_take(alice_wallet.hotkey.ss58_address) + == DEFAULT_DELEGATE_TAKE + ) + + success, error = increase_take( + subtensor, + alice_wallet, + 0.5, + ) + + assert success is False + assert "`DelegateTakeTooHigh(Module)`" in error + + # increase_take but try to change from 0.18 to 0.1 + success, error = increase_take( + subtensor, + alice_wallet, + 0.1, + ) + + assert "`DelegateTakeTooLow(Module)`" in error + assert success is False + + success, error = decrease_take( + subtensor, + alice_wallet, + 0.1, + ) + + assert success is True + assert error == "" + + take = subtensor.get_delegate_take(alice_wallet.hotkey.ss58_address) + + assert take == 0.09999237048905166 + + success, error = increase_take( + subtensor, + alice_wallet, + 0.15, + ) + + assert success is False + assert "`DelegateTxRateLimitExceeded(Module)`" in error + + take = subtensor.get_delegate_take(alice_wallet.hotkey.ss58_address) + + assert take == 0.09999237048905166 + + sudo_set_admin_utils( + local_chain, + alice_wallet, + call_function="sudo_set_tx_delegate_take_rate_limit", + call_params={ + "tx_rate_limit": 0, + }, + ) + + success, error = increase_take( + subtensor, + alice_wallet, + 0.15, + ) + + assert success is True + assert error == "" + + take = subtensor.get_delegate_take(alice_wallet.hotkey.ss58_address) + + assert take == 0.14999618524452582 + + +@pytest.mark.asyncio +async def test_delegates(subtensor, alice_wallet, bob_wallet): + """ + Tests: + - Check default Delegates + - Register Delegates + - Check if Hotkey is a Delegate + - Nominator Staking + """ + + assert subtensor.get_delegates() == [] + assert subtensor.get_delegated(alice_wallet.coldkey.ss58_address) == [] + assert subtensor.get_delegate_by_hotkey(alice_wallet.hotkey.ss58_address) is None + assert subtensor.get_delegate_by_hotkey(bob_wallet.hotkey.ss58_address) is None + + assert subtensor.is_hotkey_delegate(alice_wallet.hotkey.ss58_address) is False + assert subtensor.is_hotkey_delegate(bob_wallet.hotkey.ss58_address) is False + + subtensor.root_register( + alice_wallet, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + subtensor.root_register( + bob_wallet, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + + assert subtensor.is_hotkey_delegate(alice_wallet.hotkey.ss58_address) is True + assert subtensor.is_hotkey_delegate(bob_wallet.hotkey.ss58_address) is True + + alice_delegate = subtensor.get_delegate_by_hotkey(alice_wallet.hotkey.ss58_address) + + assert alice_delegate == DelegateInfo( + hotkey_ss58=alice_wallet.hotkey.ss58_address, + owner_ss58=alice_wallet.coldkey.ss58_address, + take=DEFAULT_DELEGATE_TAKE, + validator_permits=[], + registrations=[0], + return_per_1000=Balance(0), + total_daily_return=Balance(0), + total_stake={}, + nominators={}, + ) + + bob_delegate = subtensor.get_delegate_by_hotkey(bob_wallet.hotkey.ss58_address) + + assert bob_delegate == DelegateInfo( + hotkey_ss58=bob_wallet.hotkey.ss58_address, + owner_ss58=bob_wallet.coldkey.ss58_address, + take=DEFAULT_DELEGATE_TAKE, + validator_permits=[], + registrations=[0], + return_per_1000=Balance(0), + total_daily_return=Balance(0), + total_stake={}, + nominators={}, + ) + + delegates = subtensor.get_delegates() + + assert delegates == [ + bob_delegate, + alice_delegate, + ] + + assert subtensor.get_delegated(bob_wallet.coldkey.ss58_address) == [] + + subtensor.add_stake( + bob_wallet, + alice_wallet.hotkey.ss58_address, + netuid=0, + amount=Balance.from_tao(10_000), + wait_for_inclusion=True, + wait_for_finalization=True, + ) + + assert subtensor.get_delegated(bob_wallet.coldkey.ss58_address) == [ + DelegatedInfo( + hotkey_ss58=alice_wallet.hotkey.ss58_address, + owner_ss58=alice_wallet.coldkey.ss58_address, + take=DEFAULT_DELEGATE_TAKE, + validator_permits=[], + registrations=[0], + return_per_1000=Balance(0), + total_daily_return=Balance(0), + netuid=0, + stake=Balance.from_tao(9_999.99995), + ), + ] + + +def test_nominator_min_required_stake(local_chain, subtensor, alice_wallet, bob_wallet): + """ + Tests: + - Check default NominatorMinRequiredStake + - Add Stake to Nominate + - Update NominatorMinRequiredStake + - Check Nominator is removed + """ + + minimum_required_stake = subtensor.get_minimum_required_stake() + + assert minimum_required_stake == Balance(0) + + subtensor.root_register( + alice_wallet, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + subtensor.root_register( + bob_wallet, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + + success = subtensor.add_stake( + alice_wallet, + bob_wallet.hotkey.ss58_address, + netuid=0, + amount=Balance.from_tao(10_000), + wait_for_inclusion=True, + wait_for_finalization=True, + ) + + assert success is True + + stake = subtensor.get_stake( + alice_wallet.coldkey.ss58_address, + bob_wallet.hotkey.ss58_address, + netuid=0, + ) + + assert stake == Balance.from_tao(9_999.99995) + + # this will trigger clear_small_nominations + sudo_set_admin_utils( + local_chain, + alice_wallet, + call_function="sudo_set_nominator_min_required_stake", + call_params={ + "min_stake": "100000000000000", + }, + return_error_message=True, + ) + + minimum_required_stake = subtensor.get_minimum_required_stake() + + assert minimum_required_stake == Balance.from_tao(100_000) + + stake = subtensor.get_stake( + alice_wallet.coldkey.ss58_address, + bob_wallet.hotkey.ss58_address, + netuid=0, + ) + + assert stake == Balance(0) diff --git a/tests/e2e_tests/test_hotkeys.py b/tests/e2e_tests/test_hotkeys.py new file mode 100644 index 0000000000..a432aa702a --- /dev/null +++ b/tests/e2e_tests/test_hotkeys.py @@ -0,0 +1,187 @@ +import pytest + +from tests.e2e_tests.utils.chain_interactions import ( + set_children, + set_identity, + wait_epoch, +) + + +SET_CHILDREN_RATE_LIMIT = 150 + + +def test_identity(subtensor, alice_wallet): + """ + Tests: + - Check default identity + - Update identity + """ + + coldkey = alice_wallet.coldkeypub.ss58_address + + assert subtensor.query_identity(coldkey) == {} + + subtensor.burned_register( + alice_wallet, + netuid=1, + ) + + success, error = set_identity( + subtensor, + alice_wallet, + name="Alice", + url="https://www.example.com", + github_repo="https://github.com/opentensor/bittensor", + description="Local Chain", + ) + + assert error == "" + assert success is True + + assert subtensor.query_identity(coldkey) == { + "name": "Alice", + "url": "https://www.example.com", + "github_repo": "https://github.com/opentensor/bittensor", + "image": "", + "discord": "", + "description": "Local Chain", + "additional": "", + } + + +def test_hotkeys(subtensor, alice_wallet): + """ + Tests: + - Check if Hotkey exists + - Check if Hotkey is registered + """ + + coldkey = alice_wallet.coldkeypub.ss58_address + hotkey = alice_wallet.hotkey.ss58_address + + with pytest.raises(ValueError, match="Invalid checksum"): + subtensor.does_hotkey_exist("fake") + + assert subtensor.does_hotkey_exist(hotkey) is False + assert subtensor.get_hotkey_owner(hotkey) is None + + assert subtensor.is_hotkey_registered(hotkey) is False + assert subtensor.is_hotkey_registered_any(hotkey) is False + assert ( + subtensor.is_hotkey_registered_on_subnet( + hotkey, + netuid=1, + ) + is False + ) + + subtensor.burned_register( + alice_wallet, + netuid=1, + ) + + assert subtensor.does_hotkey_exist(hotkey) is True + assert subtensor.get_hotkey_owner(hotkey) == coldkey + + assert subtensor.is_hotkey_registered(hotkey) is True + assert subtensor.is_hotkey_registered_any(hotkey) is True + assert ( + subtensor.is_hotkey_registered_on_subnet( + hotkey, + netuid=1, + ) + is True + ) + + +@pytest.mark.asyncio +async def test_children(subtensor, alice_wallet, bob_wallet): + """ + Tests: + - Get default children (empty list) + - Update children list + - Trigger rate limit + - Clear children list + """ + + subtensor.burned_register( + alice_wallet, + netuid=1, + ) + subtensor.burned_register( + bob_wallet, + netuid=1, + ) + + success, children, error = subtensor.get_children( + alice_wallet.hotkey.ss58_address, + netuid=1, + ) + + assert error == "" + assert success is True + assert children == [] + + success, error = set_children( + subtensor, + alice_wallet, + netuid=1, + children=[ + ( + 2**64 - 1, + bob_wallet.hotkey.ss58_address, + ), + ], + ) + + assert error == "" + assert success is True + + await wait_epoch(subtensor, netuid=1) + + success, children, error = subtensor.get_children( + alice_wallet.hotkey.ss58_address, + netuid=1, + ) + + assert error == "" + assert success is True + assert children == [ + ( + 1.0, + bob_wallet.hotkey.ss58_address, + ) + ] + + success, error = set_children( + subtensor, + alice_wallet, + netuid=1, + children=[], + ) + + assert "`TxRateLimitExceeded(Module)`" in error + assert success is False + + subtensor.wait_for_block(subtensor.block + SET_CHILDREN_RATE_LIMIT) + + success, error = set_children( + subtensor, + alice_wallet, + netuid=1, + children=[], + ) + + assert error == "" + assert success is True + + await wait_epoch(subtensor, netuid=1) + + success, children, error = subtensor.get_children( + alice_wallet.hotkey.ss58_address, + netuid=1, + ) + + assert error == "" + assert success is True + assert children == [] diff --git a/tests/e2e_tests/test_metagraph.py b/tests/e2e_tests/test_metagraph.py index cb07f699da..69da571573 100644 --- a/tests/e2e_tests/test_metagraph.py +++ b/tests/e2e_tests/test_metagraph.py @@ -1,9 +1,14 @@ import os.path +import re import shutil import time +from bittensor.core.chain_data.metagraph_info import MetagraphInfo from bittensor.utils.balance import Balance from bittensor.utils.btlogging import logging +from tests.e2e_tests.utils.chain_interactions import ANY_BALANCE + +NULL_KEY = tuple(bytearray(32)) def neuron_to_dict(neuron): @@ -171,3 +176,254 @@ def test_metagraph(subtensor, alice_wallet, bob_wallet, dave_wallet): ), "Neurons don't match after save and load" logging.console.info("✅ Passed test_metagraph") + + +def test_metagraph_info(subtensor, alice_wallet): + """ + Tests: + - Check MetagraphInfo + - Register Neuron + - Register Subnet + - Check MetagraphInfo is updated + """ + + metagraph_info = subtensor.get_metagraph_info(netuid=1, block=1) + + assert metagraph_info == MetagraphInfo( + netuid=1, + name="apex", + symbol="α", + identity=None, + network_registered_at=0, + owner_hotkey=(NULL_KEY,), + owner_coldkey=(NULL_KEY,), + block=1, + tempo=100, + last_step=0, + blocks_since_last_step=1, + subnet_emission=Balance(0), + alpha_in=Balance.from_tao(10), + alpha_out=Balance.from_tao(2), + tao_in=ANY_BALANCE, + alpha_out_emission=Balance.from_tao(1), + alpha_in_emission=Balance(0), + tao_in_emission=Balance(0), + pending_alpha_emission=Balance.from_tao(0.820004577), + pending_root_emission=Balance(0), + subnet_volume=Balance(0), + moving_price=Balance.from_tao(0.000003000), + rho=10, + kappa=32767, + min_allowed_weights=0.0, + max_weights_limit=1.0, + weights_version=0, + weights_rate_limit=100, + activity_cutoff=5000, + max_validators=64, + num_uids=1, + max_uids=256, + burn=Balance.from_tao(1), + difficulty=5.421010862427522e-13, + registration_allowed=True, + pow_registration_allowed=False, + immunity_period=4096, + min_difficulty=5.421010862427522e-13, + max_difficulty=0.25, + min_burn=Balance.from_tao(0.0005), + max_burn=Balance.from_tao(100), + adjustment_alpha=0.0, + adjustment_interval=100, + target_regs_per_interval=2, + max_regs_per_block=1, + serving_rate_limit=50, + commit_reveal_weights_enabled=False, + commit_reveal_period=1, + liquid_alpha_enabled=False, + alpha_high=0.9000076295109484, + alpha_low=0.7000076295109483, + bonds_moving_avg=4.87890977618477e-14, + hotkeys=["5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM"], + coldkeys=["5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM"], + identities={}, + axons=( + { + "block": 0, + "version": 0, + "ip": 0, + "port": 0, + "ip_type": 0, + "protocol": 0, + "placeholder1": 0, + "placeholder2": 0, + }, + ), + active=(True,), + validator_permit=(False,), + pruning_score=[0.0], + last_update=(0,), + emission=[Balance(0)], + dividends=[0.0], + incentives=[0.0], + consensus=[0.0], + trust=[0.0], + rank=[0.0], + block_at_registration=(0,), + alpha_stake=[ANY_BALANCE], + tao_stake=[Balance(0)], + total_stake=[ANY_BALANCE], + tao_dividends_per_hotkey=[ + ("5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM", Balance(0)) + ], + alpha_dividends_per_hotkey=[ + ("5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM", Balance(0)) + ], + ) + + metagraph_infos = subtensor.get_all_metagraphs_info(block=1) + + assert metagraph_infos == [ + MetagraphInfo( + netuid=0, + name="root", + symbol="Τ", + identity=None, + network_registered_at=0, + owner_hotkey=(NULL_KEY,), + owner_coldkey=(NULL_KEY,), + block=1, + tempo=100, + last_step=0, + blocks_since_last_step=1, + subnet_emission=Balance(0), + alpha_in=Balance(0), + alpha_out=Balance(0), + tao_in=Balance(0), + alpha_out_emission=Balance(0), + alpha_in_emission=Balance(0), + tao_in_emission=Balance(0), + pending_alpha_emission=Balance(0), + pending_root_emission=Balance(0), + subnet_volume=Balance(0), + moving_price=Balance(0), + rho=10, + kappa=32767, + min_allowed_weights=0.0, + max_weights_limit=1.0, + weights_version=0, + weights_rate_limit=100, + activity_cutoff=5000, + max_validators=64, + num_uids=0, + max_uids=64, + burn=Balance.from_tao(1), + difficulty=5.421010862427522e-13, + registration_allowed=True, + pow_registration_allowed=False, + immunity_period=4096, + min_difficulty=5.421010862427522e-13, + max_difficulty=0.25, + min_burn=Balance.from_tao(0.0005), + max_burn=Balance.from_tao(100), + adjustment_alpha=0.0, + adjustment_interval=100, + target_regs_per_interval=1, + max_regs_per_block=1, + serving_rate_limit=50, + commit_reveal_weights_enabled=False, + commit_reveal_period=1, + liquid_alpha_enabled=False, + alpha_high=0.9000076295109484, + alpha_low=0.7000076295109483, + bonds_moving_avg=4.87890977618477e-14, + hotkeys=[], + coldkeys=[], + identities={}, + axons=(), + active=(), + validator_permit=(), + pruning_score=[], + last_update=(), + emission=[], + dividends=[], + incentives=[], + consensus=[], + trust=[], + rank=[], + block_at_registration=(), + alpha_stake=[], + tao_stake=[], + total_stake=[], + tao_dividends_per_hotkey=[], + alpha_dividends_per_hotkey=[], + ), + metagraph_info, + ] + + subtensor.burned_register( + alice_wallet, + netuid=1, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + + metagraph_info = subtensor.get_metagraph_info(netuid=1) + + assert metagraph_info.num_uids == 2 + assert metagraph_info.hotkeys == [ + "5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM", + alice_wallet.hotkey.ss58_address, + ] + assert metagraph_info.coldkeys == [ + "5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM", + alice_wallet.coldkey.ss58_address, + ] + assert metagraph_info.tao_dividends_per_hotkey == [ + ("5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM", Balance(0)), + (alice_wallet.hotkey.ss58_address, Balance(0)), + ] + assert metagraph_info.alpha_dividends_per_hotkey == [ + ("5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM", Balance(0)), + (alice_wallet.hotkey.ss58_address, Balance(0)), + ] + + subtensor.register_subnet( + alice_wallet, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + + block = subtensor.get_current_block() + metagraph_info = subtensor.get_metagraph_info(netuid=2, block=block) + + assert metagraph_info.owner_coldkey == (tuple(alice_wallet.hotkey.public_key),) + assert metagraph_info.owner_hotkey == (tuple(alice_wallet.coldkey.public_key),) + + metagraph_infos = subtensor.get_all_metagraphs_info(block) + + assert len(metagraph_infos) == 3 + assert metagraph_infos[-1] == metagraph_info + + metagraph_info = subtensor.get_metagraph_info(netuid=3) + + assert metagraph_info is None + + +def test_blocks(subtensor): + """ + Tests: + - Get current block + - Get block hash + - Wait for block + """ + + block = subtensor.get_current_block() + + assert block == subtensor.block + + block_hash = subtensor.get_block_hash(block) + + assert re.match("0x[a-z0-9]{64}", block_hash) + + subtensor.wait_for_block(block + 10) + + assert subtensor.get_current_block() == block + 10 diff --git a/tests/e2e_tests/test_neuron_certificate.py b/tests/e2e_tests/test_neuron_certificate.py index 674a4ce27c..8ada77dd35 100644 --- a/tests/e2e_tests/test_neuron_certificate.py +++ b/tests/e2e_tests/test_neuron_certificate.py @@ -1,9 +1,6 @@ import pytest from bittensor.core.axon import Axon from bittensor.utils.btlogging import logging -from tests.e2e_tests.utils.chain_interactions import ( - wait_interval, -) @pytest.mark.asyncio @@ -35,9 +32,13 @@ async def test_neuron_certificate(subtensor, alice_wallet): # Serve Alice's axon with a certificate axon = Axon(wallet=alice_wallet) encoded_certificate = "?FAKE_ALICE_CERT" - axon.serve(netuid=netuid, subtensor=subtensor, certificate=encoded_certificate) - - await wait_interval(tempo=1, subtensor=subtensor, netuid=netuid) + subtensor.serve_axon( + netuid, + axon, + certificate=encoded_certificate, + wait_for_inclusion=True, + wait_for_finalization=True, + ) # Verify we are getting the correct certificate assert ( diff --git a/tests/e2e_tests/test_set_weights.py b/tests/e2e_tests/test_set_weights.py index 61145b5fc2..d124238b5f 100644 --- a/tests/e2e_tests/test_set_weights.py +++ b/tests/e2e_tests/test_set_weights.py @@ -77,9 +77,9 @@ async def test_set_weights_uses_next_nonce(local_chain, subtensor, alice_wallet) netuid, ), "Unable to enable commit reveal on the subnet" - assert not subtensor.get_subnet_hyperparameters( - netuid=netuid, - ).commit_reveal_weights_enabled, "Failed to enable commit/reveal" + assert not subtensor.commit_reveal_enabled( + netuid, + ), "Failed to enable commit/reveal" assert ( subtensor.weights_rate_limit(netuid=netuid) > 0 @@ -96,6 +96,7 @@ async def test_set_weights_uses_next_nonce(local_chain, subtensor, alice_wallet) assert ( subtensor.get_subnet_hyperparameters(netuid=netuid).weights_rate_limit == 0 ), "Failed to set weights_rate_limit" + assert subtensor.get_hyperparameter("WeightsSetRateLimit", netuid) == 0 assert subtensor.weights_rate_limit(netuid=netuid) == 0 # Weights values diff --git a/tests/e2e_tests/test_staking.py b/tests/e2e_tests/test_staking.py new file mode 100644 index 0000000000..64d61f466d --- /dev/null +++ b/tests/e2e_tests/test_staking.py @@ -0,0 +1,247 @@ +import pytest + +from bittensor.core.chain_data.stake_info import StakeInfo +from bittensor.utils.balance import Balance +from tests.e2e_tests.utils.chain_interactions import ANY_BALANCE + + +def test_single_operation(subtensor, alice_wallet, bob_wallet): + """ + Tests: + - Staking using `add_stake` + - Unstaking using `unstake` + - Checks StakeInfo + """ + + subtensor.burned_register( + alice_wallet, + netuid=1, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + subtensor.burned_register( + bob_wallet, + netuid=1, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + + stake = subtensor.get_stake( + alice_wallet.coldkey.ss58_address, + bob_wallet.hotkey.ss58_address, + netuid=1, + ) + + assert stake == Balance(0) + + success = subtensor.add_stake( + alice_wallet, + bob_wallet.hotkey.ss58_address, + netuid=1, + amount=Balance.from_tao(10_000), + wait_for_inclusion=True, + wait_for_finalization=True, + ) + + assert success is True + + stake = subtensor.get_stake( + alice_wallet.coldkey.ss58_address, + bob_wallet.hotkey.ss58_address, + netuid=1, + ) + + assert stake > Balance(0) + + stakes = subtensor.get_stake_for_coldkey(alice_wallet.coldkey.ss58_address) + + assert stakes == [ + StakeInfo( + hotkey_ss58=bob_wallet.hotkey.ss58_address, + coldkey_ss58=alice_wallet.coldkey.ss58_address, + netuid=1, + stake=stake, + locked=Balance(0), + emission=Balance(0), + drain=0, + is_registered=True, + ), + ] + + stakes = subtensor.get_stake_info_for_coldkey(alice_wallet.coldkey.ss58_address) + + assert stakes == [ + StakeInfo( + hotkey_ss58=bob_wallet.hotkey.ss58_address, + coldkey_ss58=alice_wallet.coldkey.ss58_address, + netuid=1, + stake=stake, + locked=Balance(0), + emission=Balance(0), + drain=0, + is_registered=True, + ), + ] + + stakes = subtensor.get_stake_for_coldkey_and_hotkey( + alice_wallet.coldkey.ss58_address, + bob_wallet.hotkey.ss58_address, + ) + + assert stakes == { + 0: StakeInfo( + hotkey_ss58=bob_wallet.hotkey.ss58_address, + coldkey_ss58=alice_wallet.coldkey.ss58_address, + netuid=0, + stake=Balance(0), + locked=Balance(0), + emission=Balance(0), + drain=0, + is_registered=False, + ), + 1: StakeInfo( + hotkey_ss58=bob_wallet.hotkey.ss58_address, + coldkey_ss58=alice_wallet.coldkey.ss58_address, + netuid=1, + stake=stake, + locked=Balance.from_tao(0, netuid=1), + emission=Balance.from_tao(0, netuid=1), + drain=0, + is_registered=True, + ), + } + + success = subtensor.unstake( + alice_wallet, + bob_wallet.hotkey.ss58_address, + netuid=1, + amount=stake, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + + assert success is True + + stake = subtensor.get_stake( + alice_wallet.coldkey.ss58_address, + bob_wallet.hotkey.ss58_address, + netuid=1, + ) + + assert stake == Balance(0) + + +@pytest.mark.skip( + reason="add_stake_multiple and unstake_multiple doesn't return (just hangs)", +) +def test_batch_operations(subtensor, alice_wallet, bob_wallet): + """ + Tests: + - Staking using `add_stake_multiple` + - Unstaking using `unstake_multiple` + - Checks StakeInfo + - Checks Accounts Balance + """ + + netuids = [ + 2, + 3, + ] + + for _ in netuids: + subtensor.register_subnet( + alice_wallet, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + + for netuid in netuids: + subtensor.burned_register( + bob_wallet, + netuid, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + + for netuid in netuids: + stake = subtensor.get_stake( + alice_wallet.coldkey.ss58_address, + bob_wallet.hotkey.ss58_address, + netuid=netuid, + ) + + assert stake == Balance(0), f"netuid={netuid} stake={stake}" + + balances = subtensor.get_balances( + alice_wallet.coldkey.ss58_address, + bob_wallet.coldkey.ss58_address, + ) + + assert balances == { + alice_wallet.coldkey.ss58_address: ANY_BALANCE, + bob_wallet.coldkey.ss58_address: Balance.from_tao(999_998), + } + + alice_balance = balances[alice_wallet.coldkey.ss58_address] + + success = subtensor.add_stake_multiple( + alice_wallet, + hotkey_ss58s=[bob_wallet.hotkey.ss58_address for _ in netuids], + netuids=netuids, + amounts=[Balance.from_tao(10_000) for _ in netuids], + ) + + assert success is True + + stakes = [ + subtensor.get_stake( + alice_wallet.coldkey.ss58_address, + bob_wallet.hotkey.ss58_address, + netuid=netuid, + ) + for netuid in netuids + ] + + for netuid, stake in zip(netuids, stakes): + assert stake > Balance(0), f"netuid={netuid} stake={stake}" + + alice_balance -= len(netuids) * Balance.from_tao(10_000) + + balances = subtensor.get_balances( + alice_wallet.coldkey.ss58_address, + bob_wallet.coldkey.ss58_address, + ) + + assert balances == { + alice_wallet.coldkey.ss58_address: alice_balance, + bob_wallet.coldkey.ss58_address: Balance.from_tao(999_998), + } + + success = subtensor.unstake_multiple( + alice_wallet, + hotkey_ss58s=[bob_wallet.hotkey.ss58_address for _ in netuids], + netuids=netuids, + amounts=[Balance.from_tao(100) for _ in netuids], + ) + + assert success is True + + for netuid, old_stake in zip(netuids, stakes): + stake = subtensor.get_stake( + alice_wallet.coldkey.ss58_address, + bob_wallet.hotkey.ss58_address, + netuid=netuid, + ) + + assert stake < old_stake, f"netuid={netuid} stake={stake}" + + balances = subtensor.get_balances( + alice_wallet.coldkey.ss58_address, + bob_wallet.coldkey.ss58_address, + ) + + assert balances == { + alice_wallet.coldkey.ss58_address: ANY_BALANCE, + bob_wallet.coldkey.ss58_address: Balance.from_tao(999_998), + } + assert balances[alice_wallet.coldkey.ss58_address] > alice_balance diff --git a/tests/e2e_tests/test_subnets.py b/tests/e2e_tests/test_subnets.py new file mode 100644 index 0000000000..e9031d56c5 --- /dev/null +++ b/tests/e2e_tests/test_subnets.py @@ -0,0 +1,34 @@ +def test_subnets(subtensor, alice_wallet): + """ + Tests: + - Querying subnets + - Filtering subnets + - Checks default TxRateLimit + """ + + subnets = subtensor.all_subnets() + + assert len(subnets) == 2 + + subtensor.register_subnet( + alice_wallet, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + + subnets = subtensor.all_subnets() + + assert len(subnets) == 3 + + netuids = subtensor.filter_netuids_by_registered_hotkeys( + all_netuids=[0, 1, 2], + filter_for_netuids=[2], + all_hotkeys=[alice_wallet], + block=subtensor.block, + ) + + assert netuids == [2] + + tx_rate_limit = subtensor.tx_rate_limit() + + assert tx_rate_limit == 1000 diff --git a/tests/e2e_tests/utils/chain_interactions.py b/tests/e2e_tests/utils/chain_interactions.py index 6cf1d50bd6..eda33d129a 100644 --- a/tests/e2e_tests/utils/chain_interactions.py +++ b/tests/e2e_tests/utils/chain_interactions.py @@ -4,6 +4,7 @@ """ import asyncio +import unittest.mock from typing import Union, Optional, TYPE_CHECKING from bittensor.utils.btlogging import logging @@ -15,6 +16,12 @@ from async_substrate_interface import SubstrateInterface, ExtrinsicReceipt +ANY_BALANCE = unittest.mock.Mock( + rao=unittest.mock.ANY, + unit=unittest.mock.ANY, +) + + def sudo_set_hyperparameter_bool( substrate: "SubstrateInterface", wallet: "Wallet", @@ -211,3 +218,119 @@ async def root_set_subtensor_hyperparameter_values( return response.is_success, response.error_message return response.is_success, "" + + +def set_children(subtensor, wallet, netuid, children): + return subtensor.sign_and_send_extrinsic( + subtensor.substrate.compose_call( + call_module="SubtensorModule", + call_function="set_children", + call_params={ + "children": children, + "hotkey": wallet.hotkey.ss58_address, + "netuid": netuid, + }, + ), + wallet, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + + +def increase_take(subtensor, wallet, take): + return subtensor.sign_and_send_extrinsic( + subtensor.substrate.compose_call( + call_module="SubtensorModule", + call_function="increase_take", + call_params={ + "hotkey": wallet.hotkey.ss58_address, + "take": int(take * 0xFFFF), # u16 representation of the take + }, + ), + wallet, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + + +def decrease_take(subtensor, wallet, take): + return subtensor.sign_and_send_extrinsic( + subtensor.substrate.compose_call( + call_module="SubtensorModule", + call_function="decrease_take", + call_params={ + "hotkey": wallet.hotkey.ss58_address, + "take": int(take * 0xFFFF), # u16 representation of the take + }, + ), + wallet, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + + +def registry_set_identity(subtensor, wallet, identified, **info): + DEFAULT = { + "additional": [[]], + "display": {"Raw0": ""}, + "legal": {"Raw0": ""}, + "web": {"Raw0": ""}, + "riot": {"Raw0": ""}, + "email": {"Raw0": ""}, + "pgp_fingerprint": None, + "image": {"Raw0": ""}, + "info": {"Raw0": ""}, + "twitter": {"Raw0": ""}, + } + + info = DEFAULT.copy() | { + key: { + f"Raw{len(value)}": value, + } + for key, value in info.items() + } + + return subtensor.sign_and_send_extrinsic( + subtensor.substrate.compose_call( + call_module="Registry", + call_function="set_identity", + call_params={ + "info": info, + "identified": identified, + }, + ), + wallet, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + + +def set_identity( + subtensor, + wallet, + name="", + url="", + github_repo="", + image="", + discord="", + description="", + additional="", +): + return subtensor.sign_and_send_extrinsic( + subtensor.substrate.compose_call( + call_module="SubtensorModule", + call_function="set_identity", + call_params={ + "name": name, + "url": url, + "github_repo": github_repo, + "image": image, + "discord": discord, + "description": description, + "additional": additional, + }, + ), + wallet, + wait_for_inclusion=True, + wait_for_finalization=True, + )