From f4c2db77538bdac817e855f246f283f4bbc37a18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20=C5=BBy=C5=BAniewski?= Date: Wed, 26 Mar 2025 13:06:02 +0100 Subject: [PATCH 001/251] fix some unittests warnings --- .../extrinsics/test_registration.py | 151 +++++++++--------- tests/unit_tests/test_dendrite.py | 2 +- tests/unit_tests/test_metagraph.py | 8 +- tests/unit_tests/test_subtensor.py | 1 - 4 files changed, 82 insertions(+), 80 deletions(-) diff --git a/tests/unit_tests/extrinsics/test_registration.py b/tests/unit_tests/extrinsics/test_registration.py index efe66714bf..0f15b5bf43 100644 --- a/tests/unit_tests/extrinsics/test_registration.py +++ b/tests/unit_tests/extrinsics/test_registration.py @@ -80,40 +80,37 @@ def test_register_extrinsic_without_pow( mocker, ): # Arrange - with ( - mocker.patch.object( - mock_subtensor, "subnet_exists", return_value=subnet_exists - ), - mocker.patch.object( - mock_subtensor, - "get_neuron_for_pubkey_and_subnet", - return_value=mocker.MagicMock(is_null=neuron_is_null), - ), - mocker.patch("torch.cuda.is_available", return_value=cuda_available), - mocker.patch( - "bittensor.utils.registration.pow._get_block_with_retry", - return_value=(0, 0, "00ff11ee"), - ), - ): - # Act - result = registration.register_extrinsic( - subtensor=mock_subtensor, - wallet=mock_wallet, - netuid=123, - wait_for_inclusion=True, - wait_for_finalization=True, - max_allowed_attempts=3, - output_in_place=True, - cuda=True, - dev_id=0, - tpb=256, - num_processes=None, - update_interval=None, - log_verbose=False, - ) + mocker.patch.object(mock_subtensor, "subnet_exists", return_value=subnet_exists) + mocker.patch.object( + mock_subtensor, + "get_neuron_for_pubkey_and_subnet", + return_value=mocker.MagicMock(is_null=neuron_is_null), + ) + mocker.patch("torch.cuda.is_available", return_value=cuda_available) + mocker.patch( + "bittensor.utils.registration.pow._get_block_with_retry", + return_value=(0, 0, "00ff11ee"), + ) + + # Act + result = registration.register_extrinsic( + subtensor=mock_subtensor, + wallet=mock_wallet, + netuid=123, + wait_for_inclusion=True, + wait_for_finalization=True, + max_allowed_attempts=3, + output_in_place=True, + cuda=True, + dev_id=0, + tpb=256, + num_processes=None, + update_interval=None, + log_verbose=False, + ) - # Assert - assert result == expected_result, f"Test failed for test_id: {test_id}" + # Assert + assert result == expected_result, f"Test failed for test_id: {test_id}" @pytest.mark.parametrize( @@ -141,43 +138,47 @@ def test_register_extrinsic_with_pow( mocker, ): # Arrange - with mocker.patch( + mocker.patch( "bittensor.utils.registration.pow._solve_for_difficulty_fast", return_value=mock_pow_solution if pow_success else None, - ), mocker.patch( + ) + mocker.patch( "bittensor.utils.registration.pow._solve_for_difficulty_fast_cuda", return_value=mock_pow_solution if pow_success else None, - ), mocker.patch( + ) + mocker.patch( "bittensor.core.extrinsics.registration._do_pow_register", return_value=(registration_success, "HotKeyAlreadyRegisteredInSubNet"), - ), mocker.patch("torch.cuda.is_available", return_value=cuda): - # Act - if pow_success: - mock_pow_solution.is_stale.return_value = pow_stale - - if not pow_success and hotkey_registered: - mock_subtensor.is_hotkey_registered = mocker.MagicMock( - return_value=hotkey_registered - ) - - result = registration.register_extrinsic( - subtensor=mock_subtensor, - wallet=mock_wallet, - netuid=123, - wait_for_inclusion=True, - wait_for_finalization=True, - max_allowed_attempts=3, - output_in_place=True, - cuda=cuda, - dev_id=0, - tpb=256, - num_processes=None, - update_interval=None, - log_verbose=False, + ) + mocker.patch("torch.cuda.is_available", return_value=cuda) + + # Act + if pow_success: + mock_pow_solution.is_stale.return_value = pow_stale + + if not pow_success and hotkey_registered: + mock_subtensor.is_hotkey_registered = mocker.MagicMock( + return_value=hotkey_registered ) - # Assert - assert result == expected_result, f"Test failed for test_id: {test_id}." + result = registration.register_extrinsic( + subtensor=mock_subtensor, + wallet=mock_wallet, + netuid=123, + wait_for_inclusion=True, + wait_for_finalization=True, + max_allowed_attempts=3, + output_in_place=True, + cuda=cuda, + dev_id=0, + tpb=256, + num_processes=None, + update_interval=None, + log_verbose=False, + ) + + # Assert + assert result == expected_result, f"Test failed for test_id: {test_id}." @pytest.mark.parametrize( @@ -204,24 +205,26 @@ def test_burned_register_extrinsic( mocker, ): # Arrange - with mocker.patch.object( - mock_subtensor, "subnet_exists", return_value=subnet_exists - ), mocker.patch.object( + mocker.patch.object(mock_subtensor, "subnet_exists", return_value=subnet_exists) + mocker.patch.object( mock_subtensor, "get_neuron_for_pubkey_and_subnet", return_value=mocker.MagicMock(is_null=neuron_is_null), - ), mocker.patch( + ) + mocker.patch( "bittensor.core.extrinsics.registration._do_burned_register", return_value=(recycle_success, "Mock error message"), - ), mocker.patch.object( + ) + mocker.patch.object( mock_subtensor, "is_hotkey_registered", return_value=is_registered - ): - # Act - result = registration.burned_register_extrinsic( - subtensor=mock_subtensor, wallet=mock_wallet, netuid=123 - ) - # Assert - assert result == expected_result, f"Test failed for test_id: {test_id}" + ) + + # Act + result = registration.burned_register_extrinsic( + subtensor=mock_subtensor, wallet=mock_wallet, netuid=123 + ) + # Assert + assert result == expected_result, f"Test failed for test_id: {test_id}" def test_set_subnet_identity_extrinsic_is_success(mock_subtensor, mock_wallet, mocker): diff --git a/tests/unit_tests/test_dendrite.py b/tests/unit_tests/test_dendrite.py index efbf302b48..38b2f8929f 100644 --- a/tests/unit_tests/test_dendrite.py +++ b/tests/unit_tests/test_dendrite.py @@ -305,7 +305,7 @@ async def test_dendrite__call__success_response( ) mock_aio_response.post( f"http://127.0.0.1:666/SynapseDummy", - body=expected_synapse.json(), + body=expected_synapse.model_dump_json(), ) synapse = await setup_dendrite.call(axon_info, synapse=input_synapse) diff --git a/tests/unit_tests/test_metagraph.py b/tests/unit_tests/test_metagraph.py index f528212f45..4f5a0dbf68 100644 --- a/tests/unit_tests/test_metagraph.py +++ b/tests/unit_tests/test_metagraph.py @@ -129,10 +129,10 @@ def mock_subtensor(mocker): @pytest.fixture def metagraph_instance(mocker): metagraph = Metagraph(netuid=1337, sync=False) - metagraph._assign_neurons = mocker.AsyncMock() - metagraph._set_metagraph_attributes = mocker.AsyncMock() - metagraph._set_weights_and_bonds = mocker.AsyncMock() - metagraph._get_all_stakes_from_chain = mocker.AsyncMock() + metagraph._assign_neurons = mocker.Mock() + metagraph._set_metagraph_attributes = mocker.Mock() + metagraph._set_weights_and_bonds = mocker.Mock() + metagraph._get_all_stakes_from_chain = mocker.Mock() return metagraph diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index 3af7f70072..5c629624ae 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -2275,7 +2275,6 @@ def test_networks_during_connection(mock_substrate, mocker): sub.chain_endpoint = settings.NETWORK_MAP.get(network) -@pytest.mark.asyncio def test_get_stake_for_coldkey_and_hotkey(subtensor, mocker): netuids = [1, 2, 3] stake_info_dict = { From b4253ee1512bc986ca1547c92cbbe8c459df8028 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 17 Apr 2025 14:49:32 -0700 Subject: [PATCH 002/251] bumping version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 4bcba8dffa..06c0f4c6e8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,7 +34,7 @@ dependencies = [ "pydantic>=2.3, <3", "scalecodec==1.2.11", "uvicorn", - "bittensor-commit-reveal>=0.4.0", + "bittensor-drand>=0.5.0", "bittensor-wallet>=3.0.8", "async-substrate-interface>=1.1.0" ] From 756dacca9a7d5be6d31c39df7ae9edcbc4f3d59d Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 17 Apr 2025 14:49:53 -0700 Subject: [PATCH 003/251] update imports --- bittensor/core/async_subtensor.py | 2 +- bittensor/core/extrinsics/asyncex/commit_reveal.py | 2 +- bittensor/core/extrinsics/commit_reveal.py | 2 +- bittensor/core/subtensor.py | 2 +- bittensor/core/timelock.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 1bd3ce9820..6c43afc098 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -9,7 +9,7 @@ import numpy as np import scalecodec from async_substrate_interface import AsyncSubstrateInterface -from bittensor_commit_reveal import get_encrypted_commitment +from bittensor_drand import get_encrypted_commitment from bittensor_wallet.utils import SS58_FORMAT from numpy.typing import NDArray from scalecodec import GenericCall diff --git a/bittensor/core/extrinsics/asyncex/commit_reveal.py b/bittensor/core/extrinsics/asyncex/commit_reveal.py index d1b528e509..70c12fe0fb 100644 --- a/bittensor/core/extrinsics/asyncex/commit_reveal.py +++ b/bittensor/core/extrinsics/asyncex/commit_reveal.py @@ -3,7 +3,7 @@ from typing import Optional, Union, TYPE_CHECKING import numpy as np -from bittensor_commit_reveal import get_encrypted_commit +from bittensor_drand import get_encrypted_commit from numpy.typing import NDArray from bittensor.core.settings import version_as_int diff --git a/bittensor/core/extrinsics/commit_reveal.py b/bittensor/core/extrinsics/commit_reveal.py index 86795f1624..be66a71ea3 100644 --- a/bittensor/core/extrinsics/commit_reveal.py +++ b/bittensor/core/extrinsics/commit_reveal.py @@ -2,7 +2,7 @@ from typing import Union, TYPE_CHECKING, Optional -from bittensor_commit_reveal import get_encrypted_commit +from bittensor_drand import get_encrypted_commit import numpy as np from numpy.typing import NDArray diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index f7626297ca..fe1d0d1a68 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -8,7 +8,7 @@ from async_substrate_interface.errors import SubstrateRequestException from async_substrate_interface.sync_substrate import SubstrateInterface from async_substrate_interface.types import ScaleObj -from bittensor_commit_reveal import get_encrypted_commitment +from bittensor_drand import get_encrypted_commitment from numpy.typing import NDArray from bittensor.core.async_subtensor import ProposalVoteData diff --git a/bittensor/core/timelock.py b/bittensor/core/timelock.py index 24a18405f4..ed0b7c892e 100644 --- a/bittensor/core/timelock.py +++ b/bittensor/core/timelock.py @@ -57,7 +57,7 @@ class Person: import time from typing import Optional, Union -from bittensor_commit_reveal import ( +from bittensor_drand import ( encrypt as _btr_encrypt, decrypt as _btr_decrypt, get_latest_round, From 0c39a0dd86f8f98392d8f65d14f49769fde68bcd Mon Sep 17 00:00:00 2001 From: Benjamin Himes Date: Fri, 18 Apr 2025 15:51:18 +0200 Subject: [PATCH 004/251] Update subnet units --- bittensor/utils/balance.py | 915 ++++++++++++++++++------------------- 1 file changed, 445 insertions(+), 470 deletions(-) diff --git a/bittensor/utils/balance.py b/bittensor/utils/balance.py index 35aa708654..a23a2ac755 100644 --- a/bittensor/utils/balance.py +++ b/bittensor/utils/balance.py @@ -86,7 +86,7 @@ def __str__(self): """ Returns the Balance object as a string in the format "symbolvalue", where the value is in tao. """ - if self.unit == units[0]: + if self.unit == UNITS[0]: return f"{self.unit}{float(self.tao):,.9f}" else: return f"\u200e{float(self.tao):,.9f}{self.unit}\u200e" @@ -314,13 +314,13 @@ def from_rao(amount: int, netuid: int = 0) -> "Balance": @staticmethod def get_unit(netuid: int) -> str: - base = len(units) + base = len(UNITS) if netuid < base: - return units[netuid] + return UNITS[netuid] else: result = "" while netuid > 0: - result = units[netuid % base] + result + result = UNITS[netuid % base] + result netuid //= base return result @@ -360,472 +360,447 @@ def fixed_to_float( return integer_part + frac_float -units = [ - # Greek Alphabet (0-24) - "\u03c4", # τ (tau, 0) - "\u03b1", # α (alpha, 1) - "\u03b2", # β (beta, 2) - "\u03b3", # γ (gamma, 3) - "\u03b4", # δ (delta, 4) - "\u03b5", # ε (epsilon, 5) - "\u03b6", # ζ (zeta, 6) - "\u03b7", # η (eta, 7) - "\u03b8", # θ (theta, 8) - "\u03b9", # ι (iota, 9) - "\u03ba", # κ (kappa, 10) - "\u03bb", # λ (lambda, 11) - "\u03bc", # μ (mu, 12) - "\u03bd", # ν (nu, 13) - "\u03be", # ξ (xi, 14) - "\u03bf", # ο (omicron, 15) - "\u03c0", # π (pi, 16) - "\u03c1", # ρ (rho, 17) - "\u03c3", # σ (sigma, 18) - "t", # t (tau, 19) - "\u03c5", # υ (upsilon, 20) - "\u03c6", # φ (phi, 21) - "\u03c7", # χ (chi, 22) - "\u03c8", # ψ (psi, 23) - "\u03c9", # ω (omega, 24) - # Hebrew Alphabet (25-51) - "\u05d0", # א (aleph, 25) - "\u05d1", # ב (bet, 26) - "\u05d2", # ג (gimel, 27) - "\u05d3", # ד (dalet, 28) - "\u05d4", # ה (he, 29) - "\u05d5", # ו (vav, 30) - "\u05d6", # ז (zayin, 31) - "\u05d7", # ח (het, 32) - "\u05d8", # ט (tet, 33) - "\u05d9", # י (yod, 34) - "\u05da", # ך (final kaf, 35) - "\u05db", # כ (kaf, 36) - "\u05dc", # ל (lamed, 37) - "\u05dd", # ם (final mem, 38) - "\u05de", # מ (mem, 39) - "\u05df", # ן (final nun, 40) - "\u05e0", # נ (nun, 41) - "\u05e1", # ס (samekh, 42) - "\u05e2", # ע (ayin, 43) - "\u05e3", # ף (final pe, 44) - "\u05e4", # פ (pe, 45) - "\u05e5", # ץ (final tsadi, 46) - "\u05e6", # צ (tsadi, 47) - "\u05e7", # ק (qof, 48) - "\u05e8", # ר (resh, 49) - "\u05e9", # ש (shin, 50) - "\u05ea", # ת (tav, 51) - # Arabic Alphabet (52-81) - "\u0627", # ا (alif, 52) - "\u0628", # ب (ba, 53) - "\u062a", # ت (ta, 54) - "\u062b", # ث (tha, 55) - "\u062c", # ج (jeem, 56) - "\u062d", # ح (ha, 57) - "\u062e", # خ (kha, 58) - "\u062f", # د (dal, 59) - "\u0630", # ذ (dhal, 60) - "\u0631", # ر (ra, 61) - "\u0632", # ز (zay, 62) - "\u0633", # س (seen, 63) - "\u0634", # ش (sheen, 64) - "\u0635", # ص (sad, 65) - "\u0636", # ض (dad, 66) - "\u0637", # ط (ta, 67) - "\u0638", # ظ (dha, 68) - "\u0639", # ع (ain, 69) - "\u063a", # غ (ghain, 70) - "\u0641", # ف (fa, 71) - "\u0642", # ق (qaf, 72) - "\u0643", # ك (kaf, 73) - "\u0644", # ل (lam, 74) - "\u0645", # م (meem, 75) - "\u0646", # ن (noon, 76) - "\u0647", # ه (ha, 77) - "\u0648", # و (waw, 78) - "\u064a", # ي (ya, 79) - "\u0649", # ى (alef maksura, 80) - "\u064a", # ي (ya, 81) - # Runic Alphabet (82-90) - "\u16a0", # ᚠ (fehu, 82) - "\u16a2", # ᚢ (uruz, 83) - "\u16a6", # ᚦ (thurisaz, 84) - "\u16a8", # ᚨ (ansuz, 85) - "\u16b1", # ᚱ (raidho, 86) - "\u16b3", # ᚲ (kaunan, 87) - "\u16c7", # ᛇ (eihwaz, 88) - "\u16c9", # ᛉ (algiz, 89) - "\u16d2", # ᛒ (berkanan, 90) - # Ogham Alphabet (91-97) - "\u1680", #   (Space, 91) - "\u1681", # ᚁ (Beith, 92) - "\u1682", # ᚂ (Luis, 93) - "\u1683", # ᚃ (Fearn, 94) - "\u1684", # ᚄ (Sail, 95) - "\u1685", # ᚅ (Nion, 96) - "\u169b", # ᚛ (Forfeda, 97) - # Georgian Alphabet (98-103) - "\u10d0", # ა (ani, 98) - "\u10d1", # ბ (bani, 99) - "\u10d2", # გ (gani, 100) - "\u10d3", # დ (doni, 101) - "\u10d4", # ე (eni, 102) - "\u10d5", # ვ (vini, 103) - # Armenian Alphabet (104-110) - "\u0531", # Ա (Ayp, 104) - "\u0532", # Բ (Ben, 105) - "\u0533", # Գ (Gim, 106) - "\u0534", # Դ (Da, 107) - "\u0535", # Ե (Ech, 108) - "\u0536", # Զ (Za, 109) - "\u055e", # ՞ (Question mark, 110) - # Cyrillic Alphabet (111-116) - "\u0400", # Ѐ (Ie with grave, 111) - "\u0401", # Ё (Io, 112) - "\u0402", # Ђ (Dje, 113) - "\u0403", # Ѓ (Gje, 114) - "\u0404", # Є (Ukrainian Ie, 115) - "\u0405", # Ѕ (Dze, 116) - # Coptic Alphabet (117-122) - "\u2c80", # Ⲁ (Alfa, 117) - "\u2c81", # ⲁ (Small Alfa, 118) - "\u2c82", # Ⲃ (Vida, 119) - "\u2c83", # ⲃ (Small Vida, 120) - "\u2c84", # Ⲅ (Gamma, 121) - "\u2c85", # ⲅ (Small Gamma, 122) - # Brahmi Script (123-127) - "\U00011000", # 𑀀 (A, 123) - "\U00011001", # 𑀁 (Aa, 124) - "\U00011002", # 𑀂 (I, 125) - "\U00011003", # 𑀃 (Ii, 126) - "\U00011005", # 𑀅 (U, 127) - # Tifinagh Alphabet (128-133) - "\u2d30", # ⴰ (Ya, 128) - "\u2d31", # ⴱ (Yab, 129) - "\u2d32", # ⴲ (Yabh, 130) - "\u2d33", # ⴳ (Yag, 131) - "\u2d34", # ⴴ (Yagh, 132) - "\u2d35", # ⴵ (Yaj, 133) - # Glagolitic Alphabet (134-166) - "\u2c00", # Ⰰ (Az, 134) - "\u2c01", # Ⰱ (Buky, 135) - "\u2c02", # Ⰲ (Vede, 136) - "\u2c03", # Ⰳ (Glagoli, 137) - "\u2c04", # Ⰴ (Dobro, 138) - "\u2c05", # Ⰵ (Yest, 139) - "\u2c06", # Ⰶ (Zhivete, 140) - "\u2c07", # Ⰷ (Zemlja, 141) - "\u2c08", # Ⰸ (Izhe, 142) - "\u2c09", # Ⰹ (Initial Izhe, 143) - "\u2c0a", # Ⰺ (I, 144) - "\u2c0b", # Ⰻ (Djerv, 145) - "\u2c0c", # Ⰼ (Kako, 146) - "\u2c0d", # Ⰽ (Ljudije, 147) - "\u2c0e", # Ⰾ (Myse, 148) - "\u2c0f", # Ⰿ (Nash, 149) - "\u2c10", # Ⱀ (On, 150) - "\u2c11", # Ⱁ (Pokoj, 151) - "\u2c12", # Ⱂ (Rtsy, 152) - "\u2c13", # Ⱃ (Slovo, 153) - "\u2c14", # Ⱄ (Tvrido, 154) - "\u2c15", # Ⱅ (Uku, 155) - "\u2c16", # Ⱆ (Fert, 156) - "\u2c17", # Ⱇ (Xrivi, 157) - "\u2c18", # Ⱈ (Ot, 158) - "\u2c19", # Ⱉ (Cy, 159) - "\u2c1a", # Ⱊ (Shcha, 160) - "\u2c1b", # Ⱋ (Er, 161) - "\u2c1c", # Ⱌ (Yeru, 162) - "\u2c1d", # Ⱍ (Small Yer, 163) - "\u2c1e", # Ⱎ (Yo, 164) - "\u2c1f", # Ⱏ (Yu, 165) - "\u2c20", # Ⱐ (Ja, 166) - # Thai Alphabet (167-210) - "\u0e01", # ก (Ko Kai, 167) - "\u0e02", # ข (Kho Khai, 168) - "\u0e03", # ฃ (Kho Khuat, 169) - "\u0e04", # ค (Kho Khon, 170) - "\u0e05", # ฅ (Kho Rakhang, 171) - "\u0e06", # ฆ (Kho Khwai, 172) - "\u0e07", # ง (Ngo Ngu, 173) - "\u0e08", # จ (Cho Chan, 174) - "\u0e09", # ฉ (Cho Ching, 175) - "\u0e0a", # ช (Cho Chang, 176) - "\u0e0b", # ซ (So So, 177) - "\u0e0c", # ฌ (Cho Choe, 178) - "\u0e0d", # ญ (Yo Ying, 179) - "\u0e0e", # ฎ (Do Chada, 180) - "\u0e0f", # ฏ (To Patak, 181) - "\u0e10", # ฐ (Tho Than, 182) - "\u0e11", # ฑ (Tho Nangmontho, 183) - "\u0e12", # ฒ (Tho Phuthao, 184) - "\u0e13", # ณ (No Nen, 185) - "\u0e14", # ด (Do Dek, 186) - "\u0e15", # ต (To Tao, 187) - "\u0e16", # ถ (Tho Thung, 188) - "\u0e17", # ท (Tho Thahan, 189) - "\u0e18", # ธ (Tho Thong, 190) - "\u0e19", # น (No Nu, 191) - "\u0e1a", # บ (Bo Baimai, 192) - "\u0e1b", # ป (Po Pla, 193) - "\u0e1c", # ผ (Pho Phung, 194) - "\u0e1d", # ฝ (Fo Fa, 195) - "\u0e1e", # พ (Pho Phan, 196) - "\u0e1f", # ฟ (Fo Fan, 197) - "\u0e20", # ภ (Pho Samphao, 198) - "\u0e21", # ม (Mo Ma, 199) - "\u0e22", # ย (Yo Yak, 200) - "\u0e23", # ร (Ro Rua, 201) - "\u0e25", # ล (Lo Ling, 202) - "\u0e27", # ว (Wo Waen, 203) - "\u0e28", # ศ (So Sala, 204) - "\u0e29", # ษ (So Rusi, 205) - "\u0e2a", # ส (So Sua, 206) - "\u0e2b", # ห (Ho Hip, 207) - "\u0e2c", # ฬ (Lo Chula, 208) - "\u0e2d", # อ (O Ang, 209) - "\u0e2e", # ฮ (Ho Nokhuk, 210) - # Hangul Consonants (211-224) - "\u1100", # ㄱ (Giyeok, 211) - "\u1101", # ㄴ (Nieun, 212) - "\u1102", # ㄷ (Digeut, 213) - "\u1103", # ㄹ (Rieul, 214) - "\u1104", # ㅁ (Mieum, 215) - "\u1105", # ㅂ (Bieup, 216) - "\u1106", # ㅅ (Siot, 217) - "\u1107", # ㅇ (Ieung, 218) - "\u1108", # ㅈ (Jieut, 219) - "\u1109", # ㅊ (Chieut, 220) - "\u110a", # ㅋ (Kieuk, 221) - "\u110b", # ㅌ (Tieut, 222) - "\u110c", # ㅍ (Pieup, 223) - "\u110d", # ㅎ (Hieut, 224) - # Hangul Vowels (225-245) - "\u1161", # ㅏ (A, 225) - "\u1162", # ㅐ (Ae, 226) - "\u1163", # ㅑ (Ya, 227) - "\u1164", # ㅒ (Yae, 228) - "\u1165", # ㅓ (Eo, 229) - "\u1166", # ㅔ (E, 230) - "\u1167", # ㅕ (Yeo, 231) - "\u1168", # ㅖ (Ye, 232) - "\u1169", # ㅗ (O, 233) - "\u116a", # ㅘ (Wa, 234) - "\u116b", # ㅙ (Wae, 235) - "\u116c", # ㅚ (Oe, 236) - "\u116d", # ㅛ (Yo, 237) - "\u116e", # ㅜ (U, 238) - "\u116f", # ㅝ (Weo, 239) - "\u1170", # ㅞ (We, 240) - "\u1171", # ㅟ (Wi, 241) - "\u1172", # ㅠ (Yu, 242) - "\u1173", # ㅡ (Eu, 243) - "\u1174", # ㅢ (Ui, 244) - "\u1175", # ㅣ (I, 245) - # Ethiopic Alphabet (246-274) - "\u12a0", # አ (Glottal A, 246) - "\u12a1", # ኡ (Glottal U, 247) - "\u12a2", # ኢ (Glottal I, 248) - "\u12a3", # ኣ (Glottal Aa, 249) - "\u12a4", # ኤ (Glottal E, 250) - "\u12a5", # እ (Glottal Ie, 251) - "\u12a6", # ኦ (Glottal O, 252) - "\u12a7", # ኧ (Glottal Wa, 253) - "\u12c8", # ወ (Wa, 254) - "\u12c9", # ዉ (Wu, 255) - "\u12ca", # ዊ (Wi, 256) - "\u12cb", # ዋ (Waa, 257) - "\u12cc", # ዌ (We, 258) - "\u12cd", # ው (Wye, 259) - "\u12ce", # ዎ (Wo, 260) - "\u12b0", # ኰ (Ko, 261) - "\u12b1", # ኱ (Ku, 262) - "\u12b2", # ኲ (Ki, 263) - "\u12b3", # ኳ (Kua, 264) - "\u12b4", # ኴ (Ke, 265) - "\u12b5", # ኵ (Kwe, 266) - "\u12b6", # ኶ (Ko, 267) - "\u12a0", # ጐ (Go, 268) - "\u12a1", # ጑ (Gu, 269) - "\u12a2", # ጒ (Gi, 270) - "\u12a3", # መ (Gua, 271) - "\u12a4", # ጔ (Ge, 272) - "\u12a5", # ጕ (Gwe, 273) - "\u12a6", # ጖ (Go, 274) - # Devanagari Alphabet (275-318) - "\u0905", # अ (A, 275) - "\u0906", # आ (Aa, 276) - "\u0907", # इ (I, 277) - "\u0908", # ई (Ii, 278) - "\u0909", # उ (U, 279) - "\u090a", # ऊ (Uu, 280) - "\u090b", # ऋ (R, 281) - "\u090f", # ए (E, 282) - "\u0910", # ऐ (Ai, 283) - "\u0913", # ओ (O, 284) - "\u0914", # औ (Au, 285) - "\u0915", # क (Ka, 286) - "\u0916", # ख (Kha, 287) - "\u0917", # ग (Ga, 288) - "\u0918", # घ (Gha, 289) - "\u0919", # ङ (Nga, 290) - "\u091a", # च (Cha, 291) - "\u091b", # छ (Chha, 292) - "\u091c", # ज (Ja, 293) - "\u091d", # झ (Jha, 294) - "\u091e", # ञ (Nya, 295) - "\u091f", # ट (Ta, 296) - "\u0920", # ठ (Tha, 297) - "\u0921", # ड (Da, 298) - "\u0922", # ढ (Dha, 299) - "\u0923", # ण (Na, 300) - "\u0924", # त (Ta, 301) - "\u0925", # थ (Tha, 302) - "\u0926", # द (Da, 303) - "\u0927", # ध (Dha, 304) - "\u0928", # न (Na, 305) - "\u092a", # प (Pa, 306) - "\u092b", # फ (Pha, 307) - "\u092c", # ब (Ba, 308) - "\u092d", # भ (Bha, 309) - "\u092e", # म (Ma, 310) - "\u092f", # य (Ya, 311) - "\u0930", # र (Ra, 312) - "\u0932", # ल (La, 313) - "\u0935", # व (Va, 314) - "\u0936", # श (Sha, 315) - "\u0937", # ष (Ssa, 316) - "\u0938", # स (Sa, 317) - "\u0939", # ह (Ha, 318) - # Katakana Alphabet (319-364) - "\u30a2", # ア (A, 319) - "\u30a4", # イ (I, 320) - "\u30a6", # ウ (U, 321) - "\u30a8", # エ (E, 322) - "\u30aa", # オ (O, 323) - "\u30ab", # カ (Ka, 324) - "\u30ad", # キ (Ki, 325) - "\u30af", # ク (Ku, 326) - "\u30b1", # ケ (Ke, 327) - "\u30b3", # コ (Ko, 328) - "\u30b5", # サ (Sa, 329) - "\u30b7", # シ (Shi, 330) - "\u30b9", # ス (Su, 331) - "\u30bb", # セ (Se, 332) - "\u30bd", # ソ (So, 333) - "\u30bf", # タ (Ta, 334) - "\u30c1", # チ (Chi, 335) - "\u30c4", # ツ (Tsu, 336) - "\u30c6", # テ (Te, 337) - "\u30c8", # ト (To, 338) - "\u30ca", # ナ (Na, 339) - "\u30cb", # ニ (Ni, 340) - "\u30cc", # ヌ (Nu, 341) - "\u30cd", # ネ (Ne, 342) - "\u30ce", # ノ (No, 343) - "\u30cf", # ハ (Ha, 344) - "\u30d2", # ヒ (Hi, 345) - "\u30d5", # フ (Fu, 346) - "\u30d8", # ヘ (He, 347) - "\u30db", # ホ (Ho, 348) - "\u30de", # マ (Ma, 349) - "\u30df", # ミ (Mi, 350) - "\u30e0", # ム (Mu, 351) - "\u30e1", # メ (Me, 352) - "\u30e2", # モ (Mo, 353) - "\u30e4", # ヤ (Ya, 354) - "\u30e6", # ユ (Yu, 355) - "\u30e8", # ヨ (Yo, 356) - "\u30e9", # ラ (Ra, 357) - "\u30ea", # リ (Ri, 358) - "\u30eb", # ル (Ru, 359) - "\u30ec", # レ (Re, 360) - "\u30ed", # ロ (Ro, 361) - "\u30ef", # ワ (Wa, 362) - "\u30f2", # ヲ (Wo, 363) - "\u30f3", # ン (N, 364) - # Tifinagh Alphabet (365-400) - "\u2d30", # ⴰ (Ya, 365) - "\u2d31", # ⴱ (Yab, 366) - "\u2d32", # ⴲ (Yabh, 367) - "\u2d33", # ⴳ (Yag, 368) - "\u2d34", # ⴴ (Yagh, 369) - "\u2d35", # ⴵ (Yaj, 370) - "\u2d36", # ⴶ (Yach, 371) - "\u2d37", # ⴷ (Yad, 372) - "\u2d38", # ⴸ (Yadh, 373) - "\u2d39", # ⴹ (Yadh, emphatic, 374) - "\u2d3a", # ⴺ (Yaz, 375) - "\u2d3b", # ⴻ (Yazh, 376) - "\u2d3c", # ⴼ (Yaf, 377) - "\u2d3d", # ⴽ (Yak, 378) - "\u2d3e", # ⴾ (Yak, variant, 379) - "\u2d3f", # ⴿ (Yaq, 380) - "\u2d40", # ⵀ (Yah, 381) - "\u2d41", # ⵁ (Yahh, 382) - "\u2d42", # ⵂ (Yahl, 383) - "\u2d43", # ⵃ (Yahm, 384) - "\u2d44", # ⵄ (Yayn, 385) - "\u2d45", # ⵅ (Yakh, 386) - "\u2d46", # ⵆ (Yakl, 387) - "\u2d47", # ⵇ (Yahq, 388) - "\u2d48", # ⵈ (Yash, 389) - "\u2d49", # ⵉ (Yi, 390) - "\u2d4a", # ⵊ (Yij, 391) - "\u2d4b", # ⵋ (Yizh, 392) - "\u2d4c", # ⵌ (Yink, 393) - "\u2d4d", # ⵍ (Yal, 394) - "\u2d4e", # ⵎ (Yam, 395) - "\u2d4f", # ⵏ (Yan, 396) - "\u2d50", # ⵐ (Yang, 397) - "\u2d51", # ⵑ (Yany, 398) - "\u2d52", # ⵒ (Yap, 399) - "\u2d53", # ⵓ (Yu, 400) - # Sinhala Alphabet (401-444) - "\u0d85", # අ (A, 401) - "\u0d86", # ආ (Aa, 402) - "\u0d87", # ඉ (I, 403) - "\u0d88", # ඊ (Ii, 404) - "\u0d89", # උ (U, 405) - "\u0d8a", # ඌ (Uu, 406) - "\u0d8b", # ඍ (R, 407) - "\u0d8c", # ඎ (Rr, 408) - "\u0d8f", # ඏ (L, 409) - "\u0d90", # ඐ (Ll, 410) - "\u0d91", # එ (E, 411) - "\u0d92", # ඒ (Ee, 412) - "\u0d93", # ඓ (Ai, 413) - "\u0d94", # ඔ (O, 414) - "\u0d95", # ඕ (Oo, 415) - "\u0d96", # ඖ (Au, 416) - "\u0d9a", # ක (Ka, 417) - "\u0d9b", # ඛ (Kha, 418) - "\u0d9c", # ග (Ga, 419) - "\u0d9d", # ඝ (Gha, 420) - "\u0d9e", # ඞ (Nga, 421) - "\u0d9f", # ච (Cha, 422) - "\u0da0", # ඡ (Chha, 423) - "\u0da1", # ජ (Ja, 424) - "\u0da2", # ඣ (Jha, 425) - "\u0da3", # ඤ (Nya, 426) - "\u0da4", # ට (Ta, 427) - "\u0da5", # ඥ (Tha, 428) - "\u0da6", # ඦ (Da, 429) - "\u0da7", # ට (Dha, 430) - "\u0da8", # ඨ (Na, 431) - "\u0daa", # ඪ (Pa, 432) - "\u0dab", # ණ (Pha, 433) - "\u0dac", # ඬ (Ba, 434) - "\u0dad", # ත (Bha, 435) - "\u0dae", # ථ (Ma, 436) - "\u0daf", # ද (Ya, 437) - "\u0db0", # ධ (Ra, 438) - "\u0db1", # ඲ (La, 439) - "\u0db2", # ඳ (Va, 440) - "\u0db3", # ප (Sha, 441) - "\u0db4", # ඵ (Ssa, 442) - "\u0db5", # බ (Sa, 443) - "\u0db6", # භ (Ha, 444) +# lowercase is added for backwards compatibility to not break API +units = UNITS = [ + b"\xce\xa4".decode(), # Τ (Upper case Tau, 0) + b"\xce\xb1".decode(), # α (Alpha, 1) + b"\xce\xb2".decode(), # β (Beta, 2) + b"\xce\xb3".decode(), # γ (Gamma, 3) + b"\xce\xb4".decode(), # δ (Delta, 4) + b"\xce\xb5".decode(), # ε (Epsilon, 5) + b"\xce\xb6".decode(), # ζ (Zeta, 6) + b"\xce\xb7".decode(), # η (Eta, 7) + b"\xce\xb8".decode(), # θ (Theta, 8) + b"\xce\xb9".decode(), # ι (Iota, 9) + b"\xce\xba".decode(), # κ (Kappa, 10) + b"\xce\xbb".decode(), # λ (Lambda, 11) + b"\xce\xbc".decode(), # μ (Mu, 12) + b"\xce\xbd".decode(), # ν (Nu, 13) + b"\xce\xbe".decode(), # ξ (Xi, 14) + b"\xce\xbf".decode(), # ο (Omicron, 15) + b"\xcf\x80".decode(), # π (Pi, 16) + b"\xcf\x81".decode(), # ρ (Rho, 17) + b"\xcf\x83".decode(), # σ (Sigma, 18) + "t", # t (Tau, 19) + b"\xcf\x85".decode(), # υ (Upsilon, 20) + b"\xcf\x86".decode(), # φ (Phi, 21) + b"\xcf\x87".decode(), # χ (Chi, 22) + b"\xcf\x88".decode(), # ψ (Psi, 23) + b"\xcf\x89".decode(), # ω (Omega, 24) + b"\xd7\x90".decode(), # א (Aleph, 25) + b"\xd7\x91".decode(), # ב (Bet, 26) + b"\xd7\x92".decode(), # ג (Gimel, 27) + b"\xd7\x93".decode(), # ד (Dalet, 28) + b"\xd7\x94".decode(), # ה (He, 29) + b"\xd7\x95".decode(), # ו (Vav, 30) + b"\xd7\x96".decode(), # ז (Zayin, 31) + b"\xd7\x97".decode(), # ח (Het, 32) + b"\xd7\x98".decode(), # ט (Tet, 33) + b"\xd7\x99".decode(), # י (Yod, 34) + b"\xd7\x9a".decode(), # ך (Final Kaf, 35) + b"\xd7\x9b".decode(), # כ (Kaf, 36) + b"\xd7\x9c".decode(), # ל (Lamed, 37) + b"\xd7\x9d".decode(), # ם (Final Mem, 38) + b"\xd7\x9e".decode(), # מ (Mem, 39) + b"\xd7\x9f".decode(), # ן (Final Nun, 40) + b"\xd7\xa0".decode(), # נ (Nun, 41) + b"\xd7\xa1".decode(), # ס (Samekh, 42) + b"\xd7\xa2".decode(), # ע (Ayin, 43) + b"\xd7\xa3".decode(), # ף (Final Pe, 44) + b"\xd7\xa4".decode(), # פ (Pe, 45) + b"\xd7\xa5".decode(), # ץ (Final Tsadi, 46) + b"\xd7\xa6".decode(), # צ (Tsadi, 47) + b"\xd7\xa7".decode(), # ק (Qof, 48) + b"\xd7\xa8".decode(), # ר (Resh, 49) + b"\xd7\xa9".decode(), # ש (Shin, 50) + b"\xd7\xaa".decode(), # ת (Tav, 51) + b"\xd8\xa7".decode(), # ا (Alif, 52) + b"\xd8\xa8".decode(), # ب (Ba, 53) + b"\xd8\xaa".decode(), # ت (Ta, 54) + b"\xd8\xab".decode(), # ث (Tha, 55) + b"\xd8\xac".decode(), # ج (Jim, 56) + b"\xd8\xad".decode(), # ح (Ha, 57) + b"\xd8\xae".decode(), # خ (Kha, 58) + b"\xd8\xaf".decode(), # د (Dal, 59) + b"\xd8\xb0".decode(), # ذ (Dhal, 60) + b"\xd8\xb1".decode(), # ر (Ra, 61) + b"\xd8\xb2".decode(), # ز (Zay, 62) + b"\xd8\xb3".decode(), # س (Sin, 63) + b"\xd8\xb4".decode(), # ش (Shin, 64) + b"\xd8\xb5".decode(), # ص (Sad, 65) + b"\xd8\xb6".decode(), # ض (Dad, 66) + b"\xd8\xb7".decode(), # ط (Ta, 67) + b"\xd8\xb8".decode(), # ظ (Dha, 68) + b"\xd8\xb9".decode(), # ع (Ain, 69) + b"\xd8\xba".decode(), # غ (Ghayn, 70) + b"\xd9\x81".decode(), # ف (Fa, 71) + b"\xd9\x82".decode(), # ق (Qaf, 72) + b"\xd9\x83".decode(), # ك (Kaf, 73) + b"\xd9\x84".decode(), # ل (Lam, 74) + b"\xd9\x85".decode(), # م (Mim, 75) + b"\xd9\x86".decode(), # ن (Nun, 76) + b"\xd9\x87".decode(), # ه (Ha, 77) + b"\xd9\x88".decode(), # و (Waw, 78) + b"\xd9\x8a".decode(), # ي (Ya, 79) + b"\xd9\x89".decode(), # ى (Alef Maksura, 80) + b"\xe1\x9a\xa0".decode(), # ᚠ (Fehu, wealth, 81) + b"\xe1\x9a\xa2".decode(), # ᚢ (Uruz, strength, 82) + b"\xe1\x9a\xa6".decode(), # ᚦ (Thurisaz, giant, 83) + b"\xe1\x9a\xa8".decode(), # ᚨ (Ansuz, god, 84) + b"\xe1\x9a\xb1".decode(), # ᚱ (Raidho, ride, 85) + b"\xe1\x9a\xb3".decode(), # ᚲ (Kaunan, ulcer, 86) + b"\xd0\xab".decode(), # Ы (Cyrillic Yeru, 87) + b"\xe1\x9b\x89".decode(), # ᛉ (Algiz, protection, 88) + b"\xe1\x9b\x92".decode(), # ᛒ (Berkanan, birch, 89) + b"\xe1\x9a\x80".decode(), #   (Space, 90) + b"\xe1\x9a\x81".decode(), # ᚁ (Beith, birch, 91) + b"\xe1\x9a\x82".decode(), # ᚂ (Luis, rowan, 92) + b"\xe1\x9a\x83".decode(), # ᚃ (Fearn, alder, 93) + b"\xe1\x9a\x84".decode(), # ᚄ (Sail, willow, 94) + b"\xe1\x9a\x85".decode(), # ᚅ (Nion, ash, 95) + b"\xe1\x9a\x9b".decode(), # ᚛ (Forfeda, 96) + b"\xe1\x83\x90".decode(), # ა (Ani, 97) + b"\xe1\x83\x91".decode(), # ბ (Bani, 98) + b"\xe1\x83\x92".decode(), # გ (Gani, 99) + b"\xe1\x83\x93".decode(), # დ (Doni, 100) + b"\xe1\x83\x94".decode(), # ე (Eni, 101) + b"\xe1\x83\x95".decode(), # ვ (Vini, 102) + b"\xd4\xb1".decode(), # Ա (Ayp, 103) + b"\xd4\xb2".decode(), # Բ (Ben, 104) + b"\xd4\xb3".decode(), # Գ (Gim, 105) + b"\xd4\xb4".decode(), # Դ (Da, 106) + b"\xd4\xb5".decode(), # Ե (Ech, 107) + b"\xd4\xb6".decode(), # Զ (Za, 108) + b"\xd5\x9e".decode(), # ՞ (Question mark, 109) + b"\xd0\x80".decode(), # Ѐ (Ie with grave, 110) + b"\xd0\x81".decode(), # Ё (Io, 111) + b"\xd0\x82".decode(), # Ђ (Dje, 112) + b"\xd0\x83".decode(), # Ѓ (Gje, 113) + b"\xd0\x84".decode(), # Є (Ukrainian Ie, 114) + b"\xd0\x85".decode(), # Ѕ (Dze, 115) + b"\xd1\x8a".decode(), # Ъ (Hard sign, 116) + b"\xe2\xb2\x80".decode(), # Ⲁ (Alfa, 117) + b"\xe2\xb2\x81".decode(), # ⲁ (Small Alfa, 118) + b"\xe2\xb2\x82".decode(), # Ⲃ (Vida, 119) + b"\xe2\xb2\x83".decode(), # ⲃ (Small Vida, 120) + b"\xe2\xb2\x84".decode(), # Ⲅ (Gamma, 121) + b"\xe2\xb2\x85".decode(), # ⲅ (Small Gamma, 122) + b"\xf0\x91\x80\x80".decode(), # 𑀀 (A, 123) + b"\xf0\x91\x80\x81".decode(), # 𑀁 (Aa, 124) + b"\xf0\x91\x80\x82".decode(), # 𑀂 (I, 125) + b"\xf0\x91\x80\x83".decode(), # 𑀃 (Ii, 126) + b"\xf0\x91\x80\x85".decode(), # 𑀅 (U, 127) + b"\xe0\xb6\xb1".decode(), # ඲ (La, 128) + b"\xe0\xb6\xb2".decode(), # ඳ (Va, 129) + b"\xe0\xb6\xb3".decode(), # ප (Sha, 130) + b"\xe0\xb6\xb4".decode(), # ඵ (Ssa, 131) + b"\xe0\xb6\xb5".decode(), # බ (Sa, 132) + b"\xe0\xb6\xb6".decode(), # භ (Ha, 133) + b"\xe2\xb0\x80".decode(), # Ⰰ (Az, 134) + b"\xe2\xb0\x81".decode(), # Ⰱ (Buky, 135) + b"\xe2\xb0\x82".decode(), # Ⰲ (Vede, 136) + b"\xe2\xb0\x83".decode(), # Ⰳ (Glagoli, 137) + b"\xe2\xb0\x84".decode(), # Ⰴ (Dobro, 138) + b"\xe2\xb0\x85".decode(), # Ⰵ (Yest, 139) + b"\xe2\xb0\x86".decode(), # Ⰶ (Zhivete, 140) + b"\xe2\xb0\x87".decode(), # Ⰷ (Zemlja, 141) + b"\xe2\xb0\x88".decode(), # Ⰸ (Izhe, 142) + b"\xe2\xb0\x89".decode(), # Ⰹ (Initial Izhe, 143) + b"\xe2\xb0\x8a".decode(), # Ⰺ (I, 144) + b"\xe2\xb0\x8b".decode(), # Ⰻ (Djerv, 145) + b"\xe2\xb0\x8c".decode(), # Ⰼ (Kako, 146) + b"\xe2\xb0\x8d".decode(), # Ⰽ (Ljudije, 147) + b"\xe2\xb0\x8e".decode(), # Ⰾ (Myse, 148) + b"\xe2\xb0\x8f".decode(), # Ⰿ (Nash, 149) + b"\xe2\xb0\x90".decode(), # Ⱀ (On, 150) + b"\xe2\xb0\x91".decode(), # Ⱁ (Pokoj, 151) + b"\xe2\xb0\x92".decode(), # Ⱂ (Rtsy, 152) + b"\xe2\xb0\x93".decode(), # Ⱃ (Slovo, 153) + b"\xe2\xb0\x94".decode(), # Ⱄ (Tvrido, 154) + b"\xe2\xb0\x95".decode(), # Ⱅ (Uku, 155) + b"\xe2\xb0\x96".decode(), # Ⱆ (Fert, 156) + b"\xe2\xb0\x97".decode(), # Ⱇ (Xrivi, 157) + b"\xe2\xb0\x98".decode(), # Ⱈ (Ot, 158) + b"\xe2\xb0\x99".decode(), # Ⱉ (Cy, 159) + b"\xe2\xb0\x9a".decode(), # Ⱊ (Shcha, 160) + b"\xe2\xb0\x9b".decode(), # Ⱋ (Er, 161) + b"\xe2\xb0\x9c".decode(), # Ⱌ (Yeru, 162) + b"\xe2\xb0\x9d".decode(), # Ⱍ (Small Yer, 163) + b"\xe2\xb0\x9e".decode(), # Ⱎ (Yo, 164) + b"\xe2\xb0\x9f".decode(), # Ⱏ (Yu, 165) + b"\xe2\xb0\xa0".decode(), # Ⱐ (Ja, 166) + b"\xe0\xb8\x81".decode(), # ก (Ko Kai, 167) + b"\xe0\xb8\x82".decode(), # ข (Kho Khai, 168) + b"\xe0\xb8\x83".decode(), # ฃ (Kho Khuat, 169) + b"\xe0\xb8\x84".decode(), # ค (Kho Khon, 170) + b"\xe0\xb8\x85".decode(), # ฅ (Kho Rakhang, 171) + b"\xe0\xb8\x86".decode(), # ฆ (Kho Khwai, 172) + b"\xe0\xb8\x87".decode(), # ง (Ngo Ngu, 173) + b"\xe0\xb8\x88".decode(), # จ (Cho Chan, 174) + b"\xe0\xb8\x89".decode(), # ฉ (Cho Ching, 175) + b"\xe0\xb8\x8a".decode(), # ช (Cho Chang, 176) + b"\xe0\xb8\x8b".decode(), # ซ (So So, 177) + b"\xe0\xb8\x8c".decode(), # ฌ (Cho Choe, 178) + b"\xe0\xb8\x8d".decode(), # ญ (Yo Ying, 179) + b"\xe0\xb8\x8e".decode(), # ฎ (Do Chada, 180) + b"\xe0\xb8\x8f".decode(), # ฏ (To Patak, 181) + b"\xe0\xb8\x90".decode(), # ฐ (Tho Than, 182) + b"\xe0\xb8\x91".decode(), # ฑ (Tho Nangmontho, 183) + b"\xe0\xb8\x92".decode(), # ฒ (Tho Phuthao, 184) + b"\xe0\xb8\x93".decode(), # ณ (No Nen, 185) + b"\xe0\xb8\x94".decode(), # ด (Do Dek, 186) + b"\xe0\xb8\x95".decode(), # ต (To Tao, 187) + b"\xe0\xb8\x96".decode(), # ถ (Tho Thung, 188) + b"\xe0\xb8\x97".decode(), # ท (Tho Thahan, 189) + b"\xe0\xb8\x98".decode(), # ธ (Tho Thong, 190) + b"\xe0\xb8\x99".decode(), # น (No Nu, 191) + b"\xe0\xb8\x9a".decode(), # บ (Bo Baimai, 192) + b"\xe0\xb8\x9b".decode(), # ป (Po Pla, 193) + b"\xe0\xb8\x9c".decode(), # ผ (Pho Phung, 194) + b"\xe0\xb8\x9d".decode(), # ฝ (Fo Fa, 195) + b"\xe0\xb8\x9e".decode(), # พ (Pho Phan, 196) + b"\xe0\xb8\x9f".decode(), # ฟ (Fo Fan, 197) + b"\xe0\xb8\xa0".decode(), # ภ (Pho Samphao, 198) + b"\xe0\xb8\xa1".decode(), # ม (Mo Ma, 199) + b"\xe0\xb8\xa2".decode(), # ย (Yo Yak, 200) + b"\xe0\xb8\xa3".decode(), # ร (Ro Rua, 201) + b"\xe0\xb8\xa5".decode(), # ล (Lo Ling, 202) + b"\xe0\xb8\xa7".decode(), # ว (Wo Waen, 203) + b"\xe0\xb8\xa8".decode(), # ศ (So Sala, 204) + b"\xe0\xb8\xa9".decode(), # ษ (So Rusi, 205) + b"\xe0\xb8\xaa".decode(), # ส (So Sua, 206) + b"\xe0\xb8\xab".decode(), # ห (Ho Hip, 207) + b"\xe0\xb8\xac".decode(), # ฬ (Lo Chula, 208) + b"\xe0\xb8\xad".decode(), # อ (O Ang, 209) + b"\xe0\xb8\xae".decode(), # ฮ (Ho Nokhuk, 210) + b"\xe1\x84\x80".decode(), # ㄱ (Giyeok, 211) + b"\xe1\x84\x81".decode(), # ㄴ (Nieun, 212) + b"\xe1\x84\x82".decode(), # ㄷ (Digeut, 213) + b"\xe1\x84\x83".decode(), # ㄹ (Rieul, 214) + b"\xe1\x84\x84".decode(), # ㅁ (Mieum, 215) + b"\xe1\x84\x85".decode(), # ㅂ (Bieup, 216) + b"\xe1\x84\x86".decode(), # ㅅ (Siot, 217) + b"\xe1\x84\x87".decode(), # ㅇ (Ieung, 218) + b"\xe1\x84\x88".decode(), # ㅈ (Jieut, 219) + b"\xe1\x84\x89".decode(), # ㅊ (Chieut, 220) + b"\xe1\x84\x8a".decode(), # ㅋ (Kieuk, 221) + b"\xe1\x84\x8b".decode(), # ㅌ (Tieut, 222) + b"\xe1\x84\x8c".decode(), # ㅍ (Pieup, 223) + b"\xe1\x84\x8d".decode(), # ㅎ (Hieut, 224) + b"\xe1\x85\xa1".decode(), # ㅏ (A, 225) + b"\xe1\x85\xa2".decode(), # ㅐ (Ae, 226) + b"\xe1\x85\xa3".decode(), # ㅑ (Ya, 227) + b"\xe1\x85\xa4".decode(), # ㅒ (Yae, 228) + b"\xe1\x85\xa5".decode(), # ㅓ (Eo, 229) + b"\xe1\x85\xa6".decode(), # ㅔ (E, 230) + b"\xe1\x85\xa7".decode(), # ㅕ (Yeo, 231) + b"\xe1\x85\xa8".decode(), # ㅖ (Ye, 232) + b"\xe1\x85\xa9".decode(), # ㅗ (O, 233) + b"\xe1\x85\xaa".decode(), # ㅘ (Wa, 234) + b"\xe1\x85\xab".decode(), # ㅙ (Wae, 235) + b"\xe1\x85\xac".decode(), # ㅚ (Oe, 236) + b"\xe1\x85\xad".decode(), # ㅛ (Yo, 237) + b"\xe1\x85\xae".decode(), # ㅜ (U, 238) + b"\xe1\x85\xaf".decode(), # ㅝ (Weo, 239) + b"\xe1\x85\xb0".decode(), # ㅞ (We, 240) + b"\xe1\x85\xb1".decode(), # ㅟ (Wi, 241) + b"\xe1\x85\xb2".decode(), # ㅠ (Yu, 242) + b"\xe1\x85\xb3".decode(), # ㅡ (Eu, 243) + b"\xe1\x85\xb4".decode(), # ㅢ (Ui, 244) + b"\xe1\x85\xb5".decode(), # ㅣ (I, 245) + b"\xe1\x8a\xa0".decode(), # አ (Glottal A, 246) + b"\xe1\x8a\xa1".decode(), # ኡ (Glottal U, 247) + b"\xe1\x8a\xa2".decode(), # ኢ (Glottal I, 248) + b"\xe1\x8a\xa3".decode(), # ኣ (Glottal Aa, 249) + b"\xe1\x8a\xa4".decode(), # ኤ (Glottal E, 250) + b"\xe1\x8a\xa5".decode(), # እ (Glottal Ie, 251) + b"\xe1\x8a\xa6".decode(), # ኦ (Glottal O, 252) + b"\xe1\x8a\xa7".decode(), # ኧ (Glottal Wa, 253) + b"\xe1\x8b\x88".decode(), # ወ (Wa, 254) + b"\xe1\x8b\x89".decode(), # ዉ (Wu, 255) + b"\xe1\x8b\x8a".decode(), # ዊ (Wi, 256) + b"\xe1\x8b\x8b".decode(), # ዋ (Waa, 257) + b"\xe1\x8b\x8c".decode(), # ዌ (We, 258) + b"\xe1\x8b\x8d".decode(), # ው (Wye, 259) + b"\xe1\x8b\x8e".decode(), # ዎ (Wo, 260) + b"\xe1\x8a\xb0".decode(), # ኰ (Ko, 261) + b"\xe1\x8a\xb1".decode(), # ኱ (Ku, 262) + b"\xe1\x8a\xb2".decode(), # ኲ (Ki, 263) + b"\xe1\x8a\xb3".decode(), # ኳ (Kua, 264) + b"\xe1\x8a\xb4".decode(), # ኴ (Ke, 265) + b"\xe1\x8a\xb5".decode(), # ኵ (Kwe, 266) + b"\xe1\x8a\xb6".decode(), # ኶ (Ko, 267) + b"\xe1\x8a\x90".decode(), # ጐ (Go, 268) + b"\xe1\x8a\x91".decode(), # ጑ (Gu, 269) + b"\xe1\x8a\x92".decode(), # ጒ (Gi, 270) + b"\xe1\x8a\x93".decode(), # መ (Gua, 271) + b"\xe1\x8a\x94".decode(), # ጔ (Ge, 272) + b"\xe1\x8a\x95".decode(), # ጕ (Gwe, 273) + b"\xe1\x8a\x96".decode(), # ጖ (Go, 274) + b"\xe0\xa4\x85".decode(), # अ (A, 275) + b"\xe0\xa4\x86".decode(), # आ (Aa, 276) + b"\xe0\xa4\x87".decode(), # इ (I, 277) + b"\xe0\xa4\x88".decode(), # ई (Ii, 278) + b"\xe0\xa4\x89".decode(), # उ (U, 279) + b"\xe0\xa4\x8a".decode(), # ऊ (Uu, 280) + b"\xe0\xa4\x8b".decode(), # ऋ (R, 281) + b"\xe0\xa4\x8f".decode(), # ए (E, 282) + b"\xe0\xa4\x90".decode(), # ऐ (Ai, 283) + b"\xe0\xa4\x93".decode(), # ओ (O, 284) + b"\xe0\xa4\x94".decode(), # औ (Au, 285) + b"\xe0\xa4\x95".decode(), # क (Ka, 286) + b"\xe0\xa4\x96".decode(), # ख (Kha, 287) + b"\xe0\xa4\x97".decode(), # ग (Ga, 288) + b"\xe0\xa4\x98".decode(), # घ (Gha, 289) + b"\xe0\xa4\x99".decode(), # ङ (Nga, 290) + b"\xe0\xa4\x9a".decode(), # च (Cha, 291) + b"\xe0\xa4\x9b".decode(), # छ (Chha, 292) + b"\xe0\xa4\x9c".decode(), # ज (Ja, 293) + b"\xe0\xa4\x9d".decode(), # झ (Jha, 294) + b"\xe0\xa4\x9e".decode(), # ञ (Nya, 295) + b"\xe0\xa4\x9f".decode(), # ट (Ta, 296) + b"\xe0\xa4\xa0".decode(), # ठ (Tha, 297) + b"\xe0\xa4\xa1".decode(), # ड (Da, 298) + b"\xe0\xa4\xa2".decode(), # ढ (Dha, 299) + b"\xe0\xa4\xa3".decode(), # ण (Na, 300) + b"\xe0\xa4\xa4".decode(), # त (Ta, 301) + b"\xe0\xa4\xa5".decode(), # थ (Tha, 302) + b"\xe0\xa4\xa6".decode(), # द (Da, 303) + b"\xe0\xa4\xa7".decode(), # ध (Dha, 304) + b"\xe0\xa4\xa8".decode(), # न (Na, 305) + b"\xe0\xa4\xaa".decode(), # प (Pa, 306) + b"\xe0\xa4\xab".decode(), # फ (Pha, 307) + b"\xe0\xa4\xac".decode(), # ब (Ba, 308) + b"\xe0\xa4\xad".decode(), # भ (Bha, 309) + b"\xe0\xa4\xae".decode(), # म (Ma, 310) + b"\xe0\xa4\xaf".decode(), # य (Ya, 311) + b"\xe0\xa4\xb0".decode(), # र (Ra, 312) + b"\xe0\xa4\xb2".decode(), # ल (La, 313) + b"\xe0\xa4\xb5".decode(), # व (Va, 314) + b"\xe0\xa4\xb6".decode(), # श (Sha, 315) + b"\xe0\xa4\xb7".decode(), # ष (Ssa, 316) + b"\xe0\xa4\xb8".decode(), # स (Sa, 317) + b"\xe0\xa4\xb9".decode(), # ह (Ha, 318) + b"\xe3\x82\xa2".decode(), # ア (A, 319) + b"\xe3\x82\xa4".decode(), # イ (I, 320) + b"\xe3\x82\xa6".decode(), # ウ (U, 321) + b"\xe3\x82\xa8".decode(), # エ (E, 322) + b"\xe3\x82\xaa".decode(), # オ (O, 323) + b"\xe3\x82\xab".decode(), # カ (Ka, 324) + b"\xe3\x82\xad".decode(), # キ (Ki, 325) + b"\xe3\x82\xaf".decode(), # ク (Ku, 326) + b"\xe3\x82\xb1".decode(), # ケ (Ke, 327) + b"\xe3\x82\xb3".decode(), # コ (Ko, 328) + b"\xe3\x82\xb5".decode(), # サ (Sa, 329) + b"\xe3\x82\xb7".decode(), # シ (Shi, 330) + b"\xe3\x82\xb9".decode(), # ス (Su, 331) + b"\xe3\x82\xbb".decode(), # セ (Se, 332) + b"\xe3\x82\xbd".decode(), # ソ (So, 333) + b"\xe3\x82\xbf".decode(), # タ (Ta, 334) + b"\xe3\x83\x81".decode(), # チ (Chi, 335) + b"\xe3\x83\x84".decode(), # ツ (Tsu, 336) + b"\xe3\x83\x86".decode(), # テ (Te, 337) + b"\xe3\x83\x88".decode(), # ト (To, 338) + b"\xe3\x83\x8a".decode(), # ナ (Na, 339) + b"\xe3\x83\x8b".decode(), # ニ (Ni, 340) + b"\xe3\x83\x8c".decode(), # ヌ (Nu, 341) + b"\xe3\x83\x8d".decode(), # ネ (Ne, 342) + b"\xe3\x83\x8e".decode(), # ノ (No, 343) + b"\xe3\x83\x8f".decode(), # ハ (Ha, 344) + b"\xe3\x83\x92".decode(), # ヒ (Hi, 345) + b"\xe3\x83\x95".decode(), # フ (Fu, 346) + b"\xe3\x83\x98".decode(), # ヘ (He, 347) + b"\xe3\x83\x9b".decode(), # ホ (Ho, 348) + b"\xe3\x83\x9e".decode(), # マ (Ma, 349) + b"\xe3\x83\x9f".decode(), # ミ (Mi, 350) + b"\xe3\x83\xa0".decode(), # ム (Mu, 351) + b"\xe3\x83\xa1".decode(), # メ (Me, 352) + b"\xe3\x83\xa2".decode(), # モ (Mo, 353) + b"\xe3\x83\xa4".decode(), # ヤ (Ya, 354) + b"\xe3\x83\xa6".decode(), # ユ (Yu, 355) + b"\xe3\x83\xa8".decode(), # ヨ (Yo, 356) + b"\xe3\x83\xa9".decode(), # ラ (Ra, 357) + b"\xe3\x83\xaa".decode(), # リ (Ri, 358) + b"\xe3\x83\xab".decode(), # ル (Ru, 359) + b"\xe3\x83\xac".decode(), # レ (Re, 360) + b"\xe3\x83\xad".decode(), # ロ (Ro, 361) + b"\xe3\x83\xaf".decode(), # ワ (Wa, 362) + b"\xe3\x83\xb2".decode(), # ヲ (Wo, 363) + b"\xe3\x83\xb3".decode(), # ン (N, 364) + b"\xe2\xb4\xb0".decode(), # ⴰ (Ya, 365) + b"\xe2\xb4\xb1".decode(), # ⴱ (Yab, 366) + b"\xe2\xb4\xb2".decode(), # ⴲ (Yabh, 367) + b"\xe2\xb4\xb3".decode(), # ⴳ (Yag, 368) + b"\xe2\xb4\xb4".decode(), # ⴴ (Yagh, 369) + b"\xe2\xb4\xb5".decode(), # ⴵ (Yaj, 370) + b"\xe2\xb4\xb6".decode(), # ⴶ (Yach, 371) + b"\xe2\xb4\xb7".decode(), # ⴷ (Yad, 372) + b"\xe2\xb4\xb8".decode(), # ⴸ (Yadh, 373) + b"\xe2\xb4\xb9".decode(), # ⴹ (Yadh, emphatic, 374) + b"\xe2\xb4\xba".decode(), # ⴺ (Yaz, 375) + b"\xe2\xb4\xbb".decode(), # ⴻ (Yazh, 376) + b"\xe2\xb4\xbc".decode(), # ⴼ (Yaf, 377) + b"\xe2\xb4\xbd".decode(), # ⴽ (Yak, 378) + b"\xe2\xb4\xbe".decode(), # ⴾ (Yak, variant, 379) + b"\xe2\xb4\xbf".decode(), # ⴿ (Yaq, 380) + b"\xe2\xb5\x80".decode(), # ⵀ (Yah, 381) + b"\xe2\xb5\x81".decode(), # ⵁ (Yahh, 382) + b"\xe2\xb5\x82".decode(), # ⵂ (Yahl, 383) + b"\xe2\xb5\x83".decode(), # ⵃ (Yahm, 384) + b"\xe2\xb5\x84".decode(), # ⵄ (Yayn, 385) + b"\xe2\xb5\x85".decode(), # ⵅ (Yakh, 386) + b"\xe2\xb5\x86".decode(), # ⵆ (Yakl, 387) + b"\xe2\xb5\x87".decode(), # ⵇ (Yahq, 388) + b"\xe2\xb5\x88".decode(), # ⵈ (Yash, 389) + b"\xe2\xb5\x89".decode(), # ⵉ (Yi, 390) + b"\xe2\xb5\x8a".decode(), # ⵊ (Yij, 391) + b"\xe2\xb5\x8b".decode(), # ⵋ (Yizh, 392) + b"\xe2\xb5\x8c".decode(), # ⵌ (Yink, 393) + b"\xe2\xb5\x8d".decode(), # ⵍ (Yal, 394) + b"\xe2\xb5\x8e".decode(), # ⵎ (Yam, 395) + b"\xe2\xb5\x8f".decode(), # ⵏ (Yan, 396) + b"\xe2\xb5\x90".decode(), # ⵐ (Yang, 397) + b"\xe2\xb5\x91".decode(), # ⵑ (Yany, 398) + b"\xe2\xb5\x92".decode(), # ⵒ (Yap, 399) + b"\xe2\xb5\x93".decode(), # ⵓ (Yu, 400) + b"\xe0\xb6\x85".decode(), # අ (A, 401) + b"\xe0\xb6\x86".decode(), # ආ (Aa, 402) + b"\xe0\xb6\x87".decode(), # ඉ (I, 403) + b"\xe0\xb6\x88".decode(), # ඊ (Ii, 404) + b"\xe0\xb6\x89".decode(), # උ (U, 405) + b"\xe0\xb6\x8a".decode(), # ඌ (Uu, 406) + b"\xe0\xb6\x8b".decode(), # ඍ (R, 407) + b"\xe0\xb6\x8c".decode(), # ඎ (Rr, 408) + b"\xe0\xb6\x8f".decode(), # ඏ (L, 409) + b"\xe0\xb6\x90".decode(), # ඐ (Ll, 410) + b"\xe0\xb6\x91".decode(), # එ (E, 411) + b"\xe0\xb6\x92".decode(), # ඒ (Ee, 412) + b"\xe0\xb6\x93".decode(), # ඓ (Ai, 413) + b"\xe0\xb6\x94".decode(), # ඔ (O, 414) + b"\xe0\xb6\x95".decode(), # ඕ (Oo, 415) + b"\xe0\xb6\x96".decode(), # ඖ (Au, 416) + b"\xe0\xb6\x9a".decode(), # ක (Ka, 417) + b"\xe0\xb6\x9b".decode(), # ඛ (Kha, 418) + b"\xe0\xb6\x9c".decode(), # ග (Ga, 419) + b"\xe0\xb6\x9d".decode(), # ඝ (Gha, 420) + b"\xe0\xb6\x9e".decode(), # ඞ (Nga, 421) + b"\xe0\xb6\x9f".decode(), # ච (Cha, 422) + b"\xe0\xb6\xa0".decode(), # ඡ (Chha, 423) + b"\xe0\xb6\xa1".decode(), # ජ (Ja, 424) + b"\xe0\xb6\xa2".decode(), # ඣ (Jha, 425) + b"\xe0\xb6\xa3".decode(), # ඤ (Nya, 426) + b"\xe0\xb6\xa4".decode(), # ට (Ta, 427) + b"\xe0\xb6\xa5".decode(), # ඥ (Tha, 428) + b"\xe0\xb6\xa6".decode(), # ඦ (Da, 429) + b"\xe0\xb6\xa7".decode(), # ට (Dha, 430) + b"\xe0\xb6\xa8".decode(), # ඨ (Na, 431) + b"\xe0\xb6\xaa".decode(), # ඪ (Pa, 432) + b"\xe0\xb6\xab".decode(), # ණ (Pha, 433) + b"\xe0\xb6\xac".decode(), # ඬ (Ba, 434) + b"\xe0\xb6\xad".decode(), # ත (Bha, 435) + b"\xe0\xb6\xae".decode(), # ථ (Ma, 436) + b"\xe0\xb6\xaf".decode(), # ද (Ya, 437) + b"\xe0\xb6\xb0".decode(), # ධ (Ra, 438) ] From 81f1bcecf5e475d1d2b736336b6d47f9587dce0c Mon Sep 17 00:00:00 2001 From: Benjamin Himes Date: Fri, 18 Apr 2025 17:10:40 +0200 Subject: [PATCH 005/251] Fixed sn0 --- bittensor/utils/balance.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bittensor/utils/balance.py b/bittensor/utils/balance.py index a23a2ac755..d6c78d7d95 100644 --- a/bittensor/utils/balance.py +++ b/bittensor/utils/balance.py @@ -362,7 +362,9 @@ def fixed_to_float( # lowercase is added for backwards compatibility to not break API units = UNITS = [ - b"\xce\xa4".decode(), # Τ (Upper case Tau, 0) + chr( + 0x03C4 + ), # τ Note: the subnet symbol for sn 0 is b"\xce\xa4" / Τ / Tau — however the currency/balance is τ (Tao) b"\xce\xb1".decode(), # α (Alpha, 1) b"\xce\xb2".decode(), # β (Beta, 2) b"\xce\xb3".decode(), # γ (Gamma, 3) From 3aa05adc627757ff2d621cb18065f5151c875b93 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 18 Apr 2025 11:27:42 -0700 Subject: [PATCH 006/251] add `force_register_neuron` method --- bittensor/utils/mock/subtensor_mock.py | 130 +++++++++++++++++++++++++ 1 file changed, 130 insertions(+) diff --git a/bittensor/utils/mock/subtensor_mock.py b/bittensor/utils/mock/subtensor_mock.py index e2aeec8758..9773d525a4 100644 --- a/bittensor/utils/mock/subtensor_mock.py +++ b/bittensor/utils/mock/subtensor_mock.py @@ -1,6 +1,7 @@ from collections.abc import Mapping from dataclasses import dataclass from hashlib import sha256 +from random import randint from types import SimpleNamespace from typing import Any, Optional, Union, TypedDict from unittest.mock import MagicMock, patch @@ -358,6 +359,104 @@ def set_difficulty(self, netuid: int, difficulty: int) -> None: subtensor_state["Difficulty"][netuid][self.block_number] = difficulty + def _register_neuron(self, netuid: int, hotkey: str, coldkey: str) -> int: + subtensor_state = self.chain_state["SubtensorModule"] + if netuid not in subtensor_state["NetworksAdded"]: + raise Exception("Subnet does not exist") + + subnetwork_n = self._get_most_recent_storage( + subtensor_state["SubnetworkN"][netuid] + ) + + if subnetwork_n > 0 and any( + self._get_most_recent_storage(subtensor_state["Keys"][netuid][uid]) + == hotkey + for uid in range(subnetwork_n) + ): + # already_registered + raise Exception("Hotkey already registered") + else: + # Not found + if subnetwork_n >= self._get_most_recent_storage( + subtensor_state["MaxAllowedUids"][netuid] + ): + # Subnet full, replace neuron randomly + uid = randint(0, subnetwork_n - 1) + else: + # Subnet not full, add new neuron + # Append as next uid and increment subnetwork_n + uid = subnetwork_n + subtensor_state["SubnetworkN"][netuid][self.block_number] = ( + subnetwork_n + 1 + ) + + subtensor_state["Stake"][hotkey] = {} + subtensor_state["Stake"][hotkey][coldkey] = {} + subtensor_state["Stake"][hotkey][coldkey][self.block_number] = 0 + + subtensor_state["Uids"][netuid][hotkey] = {} + subtensor_state["Uids"][netuid][hotkey][self.block_number] = uid + + subtensor_state["Keys"][netuid][uid] = {} + subtensor_state["Keys"][netuid][uid][self.block_number] = hotkey + + subtensor_state["Owner"][hotkey] = {} + subtensor_state["Owner"][hotkey][self.block_number] = coldkey + + subtensor_state["Active"][netuid][uid] = {} + subtensor_state["Active"][netuid][uid][self.block_number] = True + + subtensor_state["LastUpdate"][netuid][uid] = {} + subtensor_state["LastUpdate"][netuid][uid][self.block_number] = ( + self.block_number + ) + + subtensor_state["Rank"][netuid][uid] = {} + subtensor_state["Rank"][netuid][uid][self.block_number] = 0.0 + + subtensor_state["Emission"][netuid][uid] = {} + subtensor_state["Emission"][netuid][uid][self.block_number] = 0.0 + + subtensor_state["Incentive"][netuid][uid] = {} + subtensor_state["Incentive"][netuid][uid][self.block_number] = 0.0 + + subtensor_state["Consensus"][netuid][uid] = {} + subtensor_state["Consensus"][netuid][uid][self.block_number] = 0.0 + + subtensor_state["Trust"][netuid][uid] = {} + subtensor_state["Trust"][netuid][uid][self.block_number] = 0.0 + + subtensor_state["ValidatorTrust"][netuid][uid] = {} + subtensor_state["ValidatorTrust"][netuid][uid][self.block_number] = 0.0 + + subtensor_state["Dividends"][netuid][uid] = {} + subtensor_state["Dividends"][netuid][uid][self.block_number] = 0.0 + + subtensor_state["PruningScores"][netuid][uid] = {} + subtensor_state["PruningScores"][netuid][uid][self.block_number] = 0.0 + + subtensor_state["ValidatorPermit"][netuid][uid] = {} + subtensor_state["ValidatorPermit"][netuid][uid][self.block_number] = False + + subtensor_state["Weights"][netuid][uid] = {} + subtensor_state["Weights"][netuid][uid][self.block_number] = [] + + subtensor_state["Bonds"][netuid][uid] = {} + subtensor_state["Bonds"][netuid][uid][self.block_number] = [] + + subtensor_state["Axons"][netuid][hotkey] = {} + subtensor_state["Axons"][netuid][hotkey][self.block_number] = {} + + subtensor_state["Prometheus"][netuid][hotkey] = {} + subtensor_state["Prometheus"][netuid][hotkey][self.block_number] = {} + + if hotkey not in subtensor_state["IsNetworkMember"]: + subtensor_state["IsNetworkMember"][hotkey] = {} + subtensor_state["IsNetworkMember"][hotkey][netuid] = {} + subtensor_state["IsNetworkMember"][hotkey][netuid][self.block_number] = True + + return uid + @staticmethod def _convert_to_balance(balance: Union["Balance", float, int]) -> "Balance": if isinstance(balance, float): @@ -368,6 +467,37 @@ def _convert_to_balance(balance: Union["Balance", float, int]) -> "Balance": return balance + def force_register_neuron( + self, + netuid: int, + hotkey: str, + coldkey: str, + stake: Union["Balance", float, int] = Balance(0), + balance: Union["Balance", float, int] = Balance(0), + ) -> int: + """ + Force register a neuron on the mock chain, returning the UID. + """ + stake = self._convert_to_balance(stake) + balance = self._convert_to_balance(balance) + + subtensor_state = self.chain_state["SubtensorModule"] + if netuid not in subtensor_state["NetworksAdded"]: + raise Exception("Subnet does not exist") + + uid = self._register_neuron(netuid=netuid, hotkey=hotkey, coldkey=coldkey) + + subtensor_state["TotalStake"][self.block_number] = ( + self._get_most_recent_storage(subtensor_state["TotalStake"]) + stake.rao + ) + subtensor_state["Stake"][hotkey][coldkey][self.block_number] = stake.rao + + if balance.rao > 0: + self.force_set_balance(coldkey, balance) + self.force_set_balance(coldkey, balance) + + return uid + def force_set_balance( self, ss58_address: str, balance: Union["Balance", float, int] = Balance(0) ) -> tuple[bool, Optional[str]]: From fd1e2d8b1b8a4cdc719a564e732f9e96d6a155e0 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 18 Apr 2025 11:28:06 -0700 Subject: [PATCH 007/251] add test --- .../test_subtensor_integration.py | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/tests/integration_tests/test_subtensor_integration.py b/tests/integration_tests/test_subtensor_integration.py index 46cb382671..db8bf19453 100644 --- a/tests/integration_tests/test_subtensor_integration.py +++ b/tests/integration_tests/test_subtensor_integration.py @@ -1,10 +1,10 @@ import pytest -from bittensor import NeuronInfo -from bittensor.core.chain_data.axon_info import AxonInfo +from bittensor.core.chain_data import AxonInfo, NeuronInfo from bittensor.core.subtensor import Subtensor from bittensor.utils.balance import Balance from tests.helpers.helpers import FakeWebsocket +from bittensor.utils.mock.subtensor_mock import MockSubtensor @pytest.fixture @@ -126,3 +126,27 @@ async def test_get_neuron_for_pubkey_and_subnet(mocker): assert isinstance(result.total_stake, Balance) assert isinstance(result.axon_info, AxonInfo) assert result.is_null is False + + +def test_mock_subtensor_force_register_neuron(): + """Tests the force_register_neuron method of the MockSubtensor class.""" + # Preps + test_netuid = 1 + subtensor = MockSubtensor() + subtensor.create_subnet(netuid=test_netuid) + + uid1 = subtensor.force_register_neuron(test_netuid, "hk1", "cc1") + uid2 = subtensor.force_register_neuron(test_netuid, "hk2", "cc2") + + # Calls + neurons = subtensor.neurons(test_netuid) + neuron1 = subtensor.neuron_for_uid(uid1, test_netuid) + neuron2 = subtensor.neuron_for_uid(uid2, test_netuid) + + # Assertions + assert len(neurons) == 2 + assert [neuron1, neuron2] == neurons + assert neuron1.hotkey == "hk1" + assert neuron1.coldkey == "cc1" + assert neuron2.hotkey == "hk2" + assert neuron2.coldkey == "cc2" From 05b433a22682bc311018d788f40a2bab40abab45 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 18 Apr 2025 11:28:12 -0700 Subject: [PATCH 008/251] imports --- tests/integration_tests/test_metagraph_integration.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/integration_tests/test_metagraph_integration.py b/tests/integration_tests/test_metagraph_integration.py index efcb49d267..005b1ae0c3 100644 --- a/tests/integration_tests/test_metagraph_integration.py +++ b/tests/integration_tests/test_metagraph_integration.py @@ -1,10 +1,11 @@ +import os from unittest import mock -import bittensor import torch -import os -from bittensor.utils.mock import MockSubtensor + +import bittensor from bittensor.core.metagraph import METAGRAPH_STATE_DICT_NDARRAY_KEYS, get_save_dir +from bittensor.utils.mock import MockSubtensor _subtensor_mock: MockSubtensor = MockSubtensor() From 384efe5e12b72dbb02d3bb3ccc146dc09781751f Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 18 Apr 2025 11:51:47 -0700 Subject: [PATCH 009/251] add workflow --- .../monitor_requirements_size_master.yml | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 .github/workflows/monitor_requirements_size_master.yml diff --git a/.github/workflows/monitor_requirements_size_master.yml b/.github/workflows/monitor_requirements_size_master.yml new file mode 100644 index 0000000000..7d86171d5e --- /dev/null +++ b/.github/workflows/monitor_requirements_size_master.yml @@ -0,0 +1,76 @@ +# This workflow measures the disk size of a virtual environment +# after installing the Bittensor SDK across multiple Python versions. +# It runs only when a new pull request targets the master branch, +# and posts a comment with the results. + +name: Monitor SDK Requirements Size + +on: + pull_request: + types: [opened] + branches: [master] + +jobs: + measure-venv: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + + name: Python ${{ matrix.python-version }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Create virtualenv and install Bittensor SDK + run: | + python -m venv venv + source venv/bin/activate + pip install --upgrade pip + pip install . + + - name: Measure venv folder size + id: venv-size + run: | + du -sm venv | cut -f1 > venv_size_mb.txt + echo "venv_size=$(cat venv_size_mb.txt)" >> $GITHUB_OUTPUT + + - name: Save size for summary + run: | + echo "Python ${{ matrix.python-version }}: ${{ steps.venv-size.outputs.venv_size }} MB" >> $GITHUB_WORKSPACE/venv_sizes.txt + + - name: Upload summary file as artifact + uses: actions/upload-artifact@v4 + with: + name: venv_sizes + path: $GITHUB_WORKSPACE/venv_sizes.txt + + comment-on-pr: + if: github.event_name == 'pull_request' && github.base_ref == 'master' + runs-on: ubuntu-latest + needs: measure-venv + + steps: + - name: Download summary + uses: actions/download-artifact@v4 + with: + name: venv_sizes + path: . + + - name: Read summary and comment it + uses: actions/github-script@v7 + with: + script: | + const fs = require('fs'); + const comment = fs.readFileSync('venv_sizes.txt', 'utf8'); + github.rest.issues.createComment({ + issue_number: context.payload.pull_request.number, + owner: context.repo.owner, + repo: context.repo.name, + body: `Bittensor SDK virtual environment sizes by Python version:**\n\n\`\`\`\n${comment}\n\`\`\`` + }); From b33b87dbc73a23cde8a6940c125e48d01c54ee51 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 18 Apr 2025 12:00:32 -0700 Subject: [PATCH 010/251] update --- .../monitor_requirements_size_master.yml | 68 +++++++++---------- 1 file changed, 33 insertions(+), 35 deletions(-) diff --git a/.github/workflows/monitor_requirements_size_master.yml b/.github/workflows/monitor_requirements_size_master.yml index 7d86171d5e..6527b8abe4 100644 --- a/.github/workflows/monitor_requirements_size_master.yml +++ b/.github/workflows/monitor_requirements_size_master.yml @@ -7,7 +7,7 @@ name: Monitor SDK Requirements Size on: pull_request: - types: [opened] + types: [opened, synchronize] branches: [master] jobs: @@ -16,61 +16,59 @@ jobs: strategy: matrix: python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] - - name: Python ${{ matrix.python-version }} + outputs: + py39: ${{ steps.set-output.outputs.py39 }} + py310: ${{ steps.set-output.outputs.py310 }} + py311: ${{ steps.set-output.outputs.py311 }} + py312: ${{ steps.set-output.outputs.py312 }} + py313: ${{ steps.set-output.outputs.py313 }} steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - - name: Create virtualenv and install Bittensor SDK + - name: Create virtualenv and install run: | python -m venv venv source venv/bin/activate pip install --upgrade pip pip install . - - name: Measure venv folder size - id: venv-size - run: | - du -sm venv | cut -f1 > venv_size_mb.txt - echo "venv_size=$(cat venv_size_mb.txt)" >> $GITHUB_OUTPUT - - - name: Save size for summary + - name: Measure venv size + id: set-output run: | - echo "Python ${{ matrix.python-version }}: ${{ steps.venv-size.outputs.venv_size }} MB" >> $GITHUB_WORKSPACE/venv_sizes.txt - - - name: Upload summary file as artifact - uses: actions/upload-artifact@v4 - with: - name: venv_sizes - path: $GITHUB_WORKSPACE/venv_sizes.txt + SIZE=$(du -sm venv | cut -f1) + VERSION=${{ matrix.python-version }} + echo "Detected size: $SIZE MB for Python $VERSION" + case "$VERSION" in + 3.9) echo "py39=$SIZE" >> $GITHUB_OUTPUT ;; + 3.10) echo "py310=$SIZE" >> $GITHUB_OUTPUT ;; + 3.11) echo "py311=$SIZE" >> $GITHUB_OUTPUT ;; + 3.12) echo "py312=$SIZE" >> $GITHUB_OUTPUT ;; + 3.13) echo "py313=$SIZE" >> $GITHUB_OUTPUT ;; + esac comment-on-pr: if: github.event_name == 'pull_request' && github.base_ref == 'master' - runs-on: ubuntu-latest needs: measure-venv - + runs-on: ubuntu-latest steps: - - name: Download summary - uses: actions/download-artifact@v4 - with: - name: venv_sizes - path: . - - - name: Read summary and comment it + - name: Comment on PR uses: actions/github-script@v7 with: script: | - const fs = require('fs'); - const comment = fs.readFileSync('venv_sizes.txt', 'utf8'); + const sizes = { + "3.9": "${{ needs.measure-venv.outputs.py39 || 'N/A' }}", + "3.10": "${{ needs.measure-venv.outputs.py310 || 'N/A' }}", + "3.11": "${{ needs.measure-venv.outputs.py311 || 'N/A' }}", + "3.12": "${{ needs.measure-venv.outputs.py312 || 'N/A' }}", + "3.13": "${{ needs.measure-venv.outputs.py313 || 'N/A' }}" + }; + const lines = Object.entries(sizes).map(([ver, size]) => `Python ${ver}: ${size} MB`); github.rest.issues.createComment({ issue_number: context.payload.pull_request.number, owner: context.repo.owner, repo: context.repo.name, - body: `Bittensor SDK virtual environment sizes by Python version:**\n\n\`\`\`\n${comment}\n\`\`\`` + body: `Bittensor SDK virtual environment sizes by Python version:**\n\n\`\`\`\n${lines.join("\n")}\n\`\`\`` }); From 9f6bba33cf575ef789a12bdaa7df83b63efad5a6 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 18 Apr 2025 12:02:11 -0700 Subject: [PATCH 011/251] trigger --- .github/workflows/monitor_requirements_size_master.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/monitor_requirements_size_master.yml b/.github/workflows/monitor_requirements_size_master.yml index 6527b8abe4..a16f157828 100644 --- a/.github/workflows/monitor_requirements_size_master.yml +++ b/.github/workflows/monitor_requirements_size_master.yml @@ -5,10 +5,16 @@ name: Monitor SDK Requirements Size +#on: +# pull_request: +# types: [opened] +# branches: [master] + on: pull_request: - types: [opened, synchronize] - branches: [master] + types: [opened, synchronize, reopened] + branches: + - master jobs: measure-venv: From 48192d588e6b00ee5c754f8da4bee44cf32135c7 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 18 Apr 2025 12:10:13 -0700 Subject: [PATCH 012/251] trigger --- .../monitor_requirements_size_master.yml | 39 ++++++++++--------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/.github/workflows/monitor_requirements_size_master.yml b/.github/workflows/monitor_requirements_size_master.yml index a16f157828..eec56956f1 100644 --- a/.github/workflows/monitor_requirements_size_master.yml +++ b/.github/workflows/monitor_requirements_size_master.yml @@ -60,21 +60,24 @@ jobs: needs: measure-venv runs-on: ubuntu-latest steps: - - name: Comment on PR - uses: actions/github-script@v7 - with: - script: | - const sizes = { - "3.9": "${{ needs.measure-venv.outputs.py39 || 'N/A' }}", - "3.10": "${{ needs.measure-venv.outputs.py310 || 'N/A' }}", - "3.11": "${{ needs.measure-venv.outputs.py311 || 'N/A' }}", - "3.12": "${{ needs.measure-venv.outputs.py312 || 'N/A' }}", - "3.13": "${{ needs.measure-venv.outputs.py313 || 'N/A' }}" - }; - const lines = Object.entries(sizes).map(([ver, size]) => `Python ${ver}: ${size} MB`); - github.rest.issues.createComment({ - issue_number: context.payload.pull_request.number, - owner: context.repo.owner, - repo: context.repo.name, - body: `Bittensor SDK virtual environment sizes by Python version:**\n\n\`\`\`\n${lines.join("\n")}\n\`\`\`` - }); + - name: Post comment to PR + uses: actions/github-script@v7 + with: + script: | + const sizes = { + "3.9": "${{ needs.measure-venv.outputs.py39 || 'N/A' }}", + "3.10": "${{ needs.measure-venv.outputs.py310 || 'N/A' }}", + "3.11": "${{ needs.measure-venv.outputs.py311 || 'N/A' }}", + "3.12": "${{ needs.measure-venv.outputs.py312 || 'N/A' }}", + "3.13": "${{ needs.measure-venv.outputs.py313 || 'N/A' }}" + }; + const lines = Object.entries(sizes).map(([ver, size]) => `Python ${ver}: ${size} MB`); + const body = `Bittensor SDK virtual environment sizes by Python version:**\n\n\`\`\`\n${lines.join("\n")}\n\`\`\``; + + const { owner, repo } = context.repo; + github.rest.issues.createComment({ + issue_number: context.payload.pull_request.number, + owner, + repo, + body + }); From b78c3c173b88812b02c1a9c8a34a7fc4fd541efd Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 18 Apr 2025 12:11:02 -0700 Subject: [PATCH 013/251] no py 3.13 for test (faster) --- .github/workflows/monitor_requirements_size_master.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/monitor_requirements_size_master.yml b/.github/workflows/monitor_requirements_size_master.yml index eec56956f1..52dc5eb4a1 100644 --- a/.github/workflows/monitor_requirements_size_master.yml +++ b/.github/workflows/monitor_requirements_size_master.yml @@ -21,13 +21,13 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + python-version: ["3.9", "3.10", "3.11", "3.12"] outputs: py39: ${{ steps.set-output.outputs.py39 }} py310: ${{ steps.set-output.outputs.py310 }} py311: ${{ steps.set-output.outputs.py311 }} py312: ${{ steps.set-output.outputs.py312 }} - py313: ${{ steps.set-output.outputs.py313 }} +# py313: ${{ steps.set-output.outputs.py313 }} steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 @@ -52,7 +52,7 @@ jobs: 3.10) echo "py310=$SIZE" >> $GITHUB_OUTPUT ;; 3.11) echo "py311=$SIZE" >> $GITHUB_OUTPUT ;; 3.12) echo "py312=$SIZE" >> $GITHUB_OUTPUT ;; - 3.13) echo "py313=$SIZE" >> $GITHUB_OUTPUT ;; +# 3.13) echo "py313=$SIZE" >> $GITHUB_OUTPUT ;; esac comment-on-pr: @@ -69,7 +69,7 @@ jobs: "3.10": "${{ needs.measure-venv.outputs.py310 || 'N/A' }}", "3.11": "${{ needs.measure-venv.outputs.py311 || 'N/A' }}", "3.12": "${{ needs.measure-venv.outputs.py312 || 'N/A' }}", - "3.13": "${{ needs.measure-venv.outputs.py313 || 'N/A' }}" +# "3.13": "${{ needs.measure-venv.outputs.py313 || 'N/A' }}" }; const lines = Object.entries(sizes).map(([ver, size]) => `Python ${ver}: ${size} MB`); const body = `Bittensor SDK virtual environment sizes by Python version:**\n\n\`\`\`\n${lines.join("\n")}\n\`\`\``; From 8a15139456069465f549ab51e28d833fe28639a4 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 18 Apr 2025 12:12:04 -0700 Subject: [PATCH 014/251] no py 3.13 for test (faster) --- .github/workflows/monitor_requirements_size_master.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/monitor_requirements_size_master.yml b/.github/workflows/monitor_requirements_size_master.yml index 52dc5eb4a1..5cd80bf142 100644 --- a/.github/workflows/monitor_requirements_size_master.yml +++ b/.github/workflows/monitor_requirements_size_master.yml @@ -27,7 +27,7 @@ jobs: py310: ${{ steps.set-output.outputs.py310 }} py311: ${{ steps.set-output.outputs.py311 }} py312: ${{ steps.set-output.outputs.py312 }} -# py313: ${{ steps.set-output.outputs.py313 }} + steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 @@ -52,7 +52,6 @@ jobs: 3.10) echo "py310=$SIZE" >> $GITHUB_OUTPUT ;; 3.11) echo "py311=$SIZE" >> $GITHUB_OUTPUT ;; 3.12) echo "py312=$SIZE" >> $GITHUB_OUTPUT ;; -# 3.13) echo "py313=$SIZE" >> $GITHUB_OUTPUT ;; esac comment-on-pr: @@ -69,7 +68,6 @@ jobs: "3.10": "${{ needs.measure-venv.outputs.py310 || 'N/A' }}", "3.11": "${{ needs.measure-venv.outputs.py311 || 'N/A' }}", "3.12": "${{ needs.measure-venv.outputs.py312 || 'N/A' }}", -# "3.13": "${{ needs.measure-venv.outputs.py313 || 'N/A' }}" }; const lines = Object.entries(sizes).map(([ver, size]) => `Python ${ver}: ${size} MB`); const body = `Bittensor SDK virtual environment sizes by Python version:**\n\n\`\`\`\n${lines.join("\n")}\n\`\`\``; From deb00149dfde7d7e7ecc58001c51a566fbf95af8 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 18 Apr 2025 12:18:50 -0700 Subject: [PATCH 015/251] more --- .../monitor_requirements_size_master.yml | 47 +++++++++++-------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/.github/workflows/monitor_requirements_size_master.yml b/.github/workflows/monitor_requirements_size_master.yml index 5cd80bf142..3bf2fba602 100644 --- a/.github/workflows/monitor_requirements_size_master.yml +++ b/.github/workflows/monitor_requirements_size_master.yml @@ -59,23 +59,30 @@ jobs: needs: measure-venv runs-on: ubuntu-latest steps: - - name: Post comment to PR - uses: actions/github-script@v7 - with: - script: | - const sizes = { - "3.9": "${{ needs.measure-venv.outputs.py39 || 'N/A' }}", - "3.10": "${{ needs.measure-venv.outputs.py310 || 'N/A' }}", - "3.11": "${{ needs.measure-venv.outputs.py311 || 'N/A' }}", - "3.12": "${{ needs.measure-venv.outputs.py312 || 'N/A' }}", - }; - const lines = Object.entries(sizes).map(([ver, size]) => `Python ${ver}: ${size} MB`); - const body = `Bittensor SDK virtual environment sizes by Python version:**\n\n\`\`\`\n${lines.join("\n")}\n\`\`\``; - - const { owner, repo } = context.repo; - github.rest.issues.createComment({ - issue_number: context.payload.pull_request.number, - owner, - repo, - body - }); + - name: Post venv size summary to PR + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const sizes = { + "3.9": "${{ needs.measure-venv.outputs.py39 || 'N/A' }}", + "3.10": "${{ needs.measure-venv.outputs.py310 || 'N/A' }}", + "3.11": "${{ needs.measure-venv.outputs.py311 || 'N/A' }}", + "3.12": "${{ needs.measure-venv.outputs.py312 || 'N/A' }}", + }; + + const body = [ + '**Bittensor SDK virtual environment sizes by Python version:**', + '', + '```' + ] + .concat(Object.entries(sizes).map(([v, s]) => `Python ${v}: ${s} MB`)) + .concat(['```']) + .join('\n'); + + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body + }); From 3feedf747fb656c11e755e24cceda01f6516132e Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 18 Apr 2025 12:25:04 -0700 Subject: [PATCH 016/251] add permissions --- .github/workflows/monitor_requirements_size_master.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/monitor_requirements_size_master.yml b/.github/workflows/monitor_requirements_size_master.yml index 3bf2fba602..0a2f390d72 100644 --- a/.github/workflows/monitor_requirements_size_master.yml +++ b/.github/workflows/monitor_requirements_size_master.yml @@ -16,6 +16,10 @@ on: branches: - master +permissions: + pull-requests: write + contents: read + jobs: measure-venv: runs-on: ubuntu-latest From 543ff034cd6c3ac3aa0e313ac72266937f6f976b Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 18 Apr 2025 12:27:50 -0700 Subject: [PATCH 017/251] bring back 3.13 --- .github/workflows/monitor_requirements_size_master.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/monitor_requirements_size_master.yml b/.github/workflows/monitor_requirements_size_master.yml index 0a2f390d72..53833806fb 100644 --- a/.github/workflows/monitor_requirements_size_master.yml +++ b/.github/workflows/monitor_requirements_size_master.yml @@ -25,12 +25,13 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.9", "3.10", "3.11", "3.12"] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] outputs: py39: ${{ steps.set-output.outputs.py39 }} py310: ${{ steps.set-output.outputs.py310 }} py311: ${{ steps.set-output.outputs.py311 }} py312: ${{ steps.set-output.outputs.py312 }} + py313: ${{ steps.set-output.outputs.py313 }} steps: - uses: actions/checkout@v4 @@ -56,6 +57,7 @@ jobs: 3.10) echo "py310=$SIZE" >> $GITHUB_OUTPUT ;; 3.11) echo "py311=$SIZE" >> $GITHUB_OUTPUT ;; 3.12) echo "py312=$SIZE" >> $GITHUB_OUTPUT ;; + 3.13) echo "py313=$SIZE" >> $GITHUB_OUTPUT ;; esac comment-on-pr: @@ -73,6 +75,7 @@ jobs: "3.10": "${{ needs.measure-venv.outputs.py310 || 'N/A' }}", "3.11": "${{ needs.measure-venv.outputs.py311 || 'N/A' }}", "3.12": "${{ needs.measure-venv.outputs.py312 || 'N/A' }}", + "3.13": "${{ needs.measure-venv.outputs.py313 || 'N/A' }}", }; const body = [ From f47050c53c1a265cf31a887cd03aad0ad4f5780b Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 18 Apr 2025 12:34:07 -0700 Subject: [PATCH 018/251] just for new master PR --- .github/workflows/monitor_requirements_size_master.yml | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/.github/workflows/monitor_requirements_size_master.yml b/.github/workflows/monitor_requirements_size_master.yml index 53833806fb..a567b4f7bc 100644 --- a/.github/workflows/monitor_requirements_size_master.yml +++ b/.github/workflows/monitor_requirements_size_master.yml @@ -5,16 +5,10 @@ name: Monitor SDK Requirements Size -#on: -# pull_request: -# types: [opened] -# branches: [master] - on: pull_request: - types: [opened, synchronize, reopened] - branches: - - master + types: [opened] + branches: [master] permissions: pull-requests: write From a0c67c899dffb07ae908d677c0555d49cd9643a0 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 18 Apr 2025 12:40:24 -0700 Subject: [PATCH 019/251] check PR update --- .github/workflows/monitor_requirements_size_master.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/monitor_requirements_size_master.yml b/.github/workflows/monitor_requirements_size_master.yml index a567b4f7bc..301767ce7e 100644 --- a/.github/workflows/monitor_requirements_size_master.yml +++ b/.github/workflows/monitor_requirements_size_master.yml @@ -2,7 +2,6 @@ # after installing the Bittensor SDK across multiple Python versions. # It runs only when a new pull request targets the master branch, # and posts a comment with the results. - name: Monitor SDK Requirements Size on: From 39b37f631ed4ba090a32d50d8b8dc2149ab65072 Mon Sep 17 00:00:00 2001 From: Benjamin Himes Date: Tue, 22 Apr 2025 14:36:47 +0200 Subject: [PATCH 020/251] Resolves #2695 --- bittensor/core/async_subtensor.py | 2 +- bittensor/core/subtensor.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 1bd3ce9820..5f68d211b4 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -4177,7 +4177,7 @@ async def unstake( removed. hotkey_ss58 (Optional[str]): The ``SS58`` address of the hotkey account to unstake from. netuid (Optional[int]): The unique identifier of the subnet. - amount (Balance): The amount of TAO to unstake. If not specified, unstakes all. + amount (Balance): The amount of alpha to unstake. If not specified, unstakes all. wait_for_inclusion (bool): Waits for the transaction to be included in a block. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. safe_staking (bool): If true, enables price safety checks to protect against fluctuating prices. The unstake diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index f7626297ca..f123adfec2 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -3444,7 +3444,7 @@ def unstake( removed. hotkey_ss58 (Optional[str]): The ``SS58`` address of the hotkey account to unstake from. netuid (Optional[int]): The unique identifier of the subnet. - amount (Balance): The amount of TAO to unstake. If not specified, unstakes all. + amount (Balance): The amount of alpha to unstake. If not specified, unstakes all. wait_for_inclusion (bool): Waits for the transaction to be included in a block. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. safe_staking (bool): If true, enables price safety checks to protect against fluctuating prices. The unstake From 9289737bc6ad671950b78e6005f94a2e450d3e4c Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 23 Apr 2025 13:53:59 -0700 Subject: [PATCH 021/251] add `SelectiveMetagraphIndex` and improve `MetagraphInfo` --- bittensor/core/chain_data/metagraph_info.py | 200 ++++++++++++++++---- 1 file changed, 168 insertions(+), 32 deletions(-) diff --git a/bittensor/core/chain_data/metagraph_info.py b/bittensor/core/chain_data/metagraph_info.py index 422a0607b3..8cb8968cb5 100644 --- a/bittensor/core/chain_data/metagraph_info.py +++ b/bittensor/core/chain_data/metagraph_info.py @@ -1,3 +1,5 @@ +from enum import Enum + from dataclasses import dataclass from typing import Optional, Union @@ -12,8 +14,10 @@ # to balance with unit (just shortcut) -def _tbwu(val: int, netuid: Optional[int] = 0) -> Balance: +def _tbwu(val: Optional[int], netuid: Optional[int] = 0) -> Optional[Balance]: """Returns a Balance object from a value and unit.""" + if val is None: + return None return Balance.from_rao(val, netuid) @@ -49,8 +53,8 @@ class MetagraphInfo(InfoBase): network_registered_at: int # Keys for owner. - owner_hotkey: str # hotkey - owner_coldkey: str # coldkey + owner_hotkey: Optional[str] # hotkey + owner_coldkey: Optional[str] # coldkey # Tempo terms. block: int # block at call. @@ -146,9 +150,20 @@ def _from_dict(cls, decoded: dict) -> "MetagraphInfo": _netuid = decoded["netuid"] # Name and symbol - decoded.update({"name": bytes(decoded.get("name")).decode()}) - decoded.update({"symbol": bytes(decoded.get("symbol")).decode()}) - for key in ["identities", "identity"]: + if name := decoded.get("name"): + decoded.update({"name": bytes(name).decode()}) + + if symbol := decoded.get("symbol"): + decoded.update({"symbol": bytes(symbol).decode()}) + + ii_list = [] + if decoded.get("identity"): + ii_list.append("identity") + + if decoded.get("identities"): + ii_list.append("identities") + + for key in ii_list: raw_data = decoded.get(key) processed = process_nested(raw_data, _chr_str) decoded.update({key: processed}) @@ -162,8 +177,8 @@ def _from_dict(cls, decoded: dict) -> "MetagraphInfo": identity=decoded["identity"], network_registered_at=decoded["network_registered_at"], # Keys for owner. - owner_hotkey=decoded["owner_hotkey"], - owner_coldkey=decoded["owner_coldkey"], + owner_hotkey=decode_account_id(decoded["owner_hotkey"][0]) if decoded.get("owner_hotkey") else None, + owner_coldkey=decode_account_id(decoded["owner_coldkey"][0]) if decoded.get("owner_coldkey") else None, # Tempo terms. block=decoded["block"], tempo=decoded["tempo"], @@ -182,13 +197,19 @@ def _from_dict(cls, decoded: dict) -> "MetagraphInfo": subnet_volume=_tbwu(decoded["subnet_volume"], _netuid), moving_price=Balance.from_tao( fixed_to_float(decoded.get("moving_price"), 32) - ), + ) + if decoded.get("moving_price") + else None, # Hparams for epoch rho=decoded["rho"], kappa=decoded["kappa"], # Validator params - min_allowed_weights=u16tf(decoded["min_allowed_weights"]), - max_weights_limit=u16tf(decoded["max_weights_limit"]), + min_allowed_weights=u16tf(decoded["min_allowed_weights"]) + if decoded["min_allowed_weights"] + else None, + max_weights_limit=u16tf(decoded["max_weights_limit"]) + if decoded["max_weights_limit"] + else None, weights_version=decoded["weights_version"], weights_rate_limit=decoded["weights_rate_limit"], activity_cutoff=decoded["activity_cutoff"], @@ -197,15 +218,21 @@ def _from_dict(cls, decoded: dict) -> "MetagraphInfo": num_uids=decoded["num_uids"], max_uids=decoded["max_uids"], burn=_tbwu(decoded["burn"]), - difficulty=u64tf(decoded["difficulty"]), + difficulty=u64tf(decoded["difficulty"]) if decoded["difficulty"] else None, registration_allowed=decoded["registration_allowed"], pow_registration_allowed=decoded["pow_registration_allowed"], immunity_period=decoded["immunity_period"], - min_difficulty=u64tf(decoded["min_difficulty"]), - max_difficulty=u64tf(decoded["max_difficulty"]), + min_difficulty=u64tf(decoded["min_difficulty"]) + if decoded["min_difficulty"] + else None, + max_difficulty=u64tf(decoded["max_difficulty"]) + if decoded["max_difficulty"] + else None, min_burn=_tbwu(decoded["min_burn"]), max_burn=_tbwu(decoded["max_burn"]), - adjustment_alpha=u64tf(decoded["adjustment_alpha"]), + adjustment_alpha=u64tf(decoded["adjustment_alpha"]) + if decoded["adjustment_alpha"] + else None, adjustment_interval=decoded["adjustment_interval"], target_regs_per_interval=decoded["target_regs_per_interval"], max_regs_per_block=decoded["max_regs_per_block"], @@ -215,40 +242,70 @@ def _from_dict(cls, decoded: dict) -> "MetagraphInfo": commit_reveal_period=decoded["commit_reveal_period"], # Bonds liquid_alpha_enabled=decoded["liquid_alpha_enabled"], - alpha_high=u16tf(decoded["alpha_high"]), - alpha_low=u16tf(decoded["alpha_low"]), - bonds_moving_avg=u64tf(decoded["bonds_moving_avg"]), + alpha_high=u16tf(decoded["alpha_high"]) if decoded["alpha_high"] else None, + alpha_low=u16tf(decoded["alpha_low"]) if decoded["alpha_low"] else None, + bonds_moving_avg=u64tf(decoded["bonds_moving_avg"]) + if decoded["bonds_moving_avg"] + else None, # Metagraph info. - hotkeys=[decode_account_id(ck) for ck in decoded.get("hotkeys", [])], - coldkeys=[decode_account_id(hk) for hk in decoded.get("coldkeys", [])], + hotkeys=[decode_account_id(ck) for ck in decoded.get("hotkeys", [])] + if decoded.get("hotkeys") + else None, + coldkeys=[decode_account_id(hk) for hk in decoded.get("coldkeys", [])] + if decoded.get("coldkeys") + else None, identities=decoded["identities"], axons=decoded.get("axons", []), active=decoded["active"], validator_permit=decoded["validator_permit"], - pruning_score=[u16tf(ps) for ps in decoded.get("pruning_score", [])], + pruning_score=[u16tf(ps) for ps in decoded.get("pruning_score", [])] + if decoded.get("pruning_score") + else None, last_update=decoded["last_update"], - emission=[_tbwu(em, _netuid) for em in decoded.get("emission", [])], - dividends=[u16tf(dv) for dv in decoded.get("dividends", [])], - incentives=[u16tf(ic) for ic in decoded.get("incentives", [])], - consensus=[u16tf(cs) for cs in decoded.get("consensus", [])], - trust=[u16tf(tr) for tr in decoded.get("trust", [])], - rank=[u16tf(rk) for rk in decoded.get("rank", [])], + emission=[_tbwu(em, _netuid) for em in decoded.get("emission", [])] + if decoded.get("emission") + else None, + dividends=[u16tf(dv) for dv in decoded.get("dividends", [])] + if decoded.get("dividends") + else None, + incentives=[u16tf(ic) for ic in decoded.get("incentives", [])] + if decoded.get("incentives") + else None, + consensus=[u16tf(cs) for cs in decoded.get("consensus", [])] + if decoded.get("consensus") + else None, + trust=[u16tf(tr) for tr in decoded.get("trust", [])] + if decoded.get("trust") + else None, + rank=[u16tf(rk) for rk in decoded.get("rank", [])] + if decoded.get("rank") + else None, block_at_registration=decoded["block_at_registration"], - alpha_stake=[_tbwu(ast, _netuid) for ast in decoded["alpha_stake"]], + alpha_stake=[_tbwu(ast, _netuid) for ast in decoded["alpha_stake"]] + if decoded.get("alpha_stake") + else None, tao_stake=[ _tbwu(ts) * settings.ROOT_TAO_STAKE_WEIGHT for ts in decoded["tao_stake"] - ], - total_stake=[_tbwu(ts, _netuid) for ts in decoded["total_stake"]], + ] + if decoded.get("tao_stake") + else None, + total_stake=[_tbwu(ts, _netuid) for ts in decoded["total_stake"]] + if decoded.get("total_stake") + else None, # Dividend break down tao_dividends_per_hotkey=[ (decode_account_id(alpha[0]), _tbwu(alpha[1])) for alpha in decoded["tao_dividends_per_hotkey"] - ], + ] + if decoded.get("tao_dividends_per_hotkey") + else None, alpha_dividends_per_hotkey=[ (decode_account_id(adphk[0]), _tbwu(adphk[1], _netuid)) for adphk in decoded["alpha_dividends_per_hotkey"] - ], + ] + if decoded.get("alpha_dividends_per_hotkey") + else None, ) @@ -306,3 +363,82 @@ class MetagraphInfoParams: tempo: int weights_rate_limit: int weights_version: int + + +class SelectiveMetagraphIndex(Enum): + Netuid = 0 + Name = 1 + Symbol = 2 + Identity = 3 + NetworkRegisteredAt = 4 + OwnerHotkey = 5 + OwnerColdkey = 6 + Block = 7 + Tempo = 8 + LastStep = 9 + BlocksSinceLastStep = 10 + SubnetEmission = 11 + AlphaIn = 12 + AlphaOut = 13 + TaoIn = 14 + AlphaOutEmission = 15 + AlphaInEmission = 16 + TaoInEmission = 17 + PendingAlphaEmission = 18 + PendingRootEmission = 19 + SubnetVolume = 20 + MovingPrice = 21 + Rho = 22 + Kappa = 23 + MinAllowedWeights = 24 + MaxWeightsLimit = 25 + WeightsVersion = 26 + WeightsRateLimit = 27 + ActivityCutoff = 28 + MaxValidators = 29 + NumUids = 30 + MaxUids = 31 + Burn = 32 + Difficulty = 33 + RegistrationAllowed = 34 + PowRegistrationAllowed = 35 + ImmunityPeriod = 36 + MinDifficulty = 37 + MaxDifficulty = 38 + MinBurn = 39 + MaxBurn = 40 + AdjustmentAlpha = 41 + AdjustmentInterval = 42 + TargetRegsPerInterval = 43 + MaxRegsPerBlock = 44 + ServingRateLimit = 45 + CommitRevealWeightsEnabled = 46 + CommitRevealPeriod = 47 + LiquidAlphaEnabled = 48 + AlphaHigh = 49 + AlphaLow = 50 + BondsMovingAvg = 51 + Hotkeys = 52 + Coldkeys = 53 + Identities = 54 + Axons = 55 + Active = 56 + ValidatorPermit = 57 + PruningScore = 58 + LastUpdate = 59 + Emission = 60 + Dividends = 61 + Incentives = 62 + Consensus = 63 + Trust = 64 + Rank = 65 + BlockAtRegistration = 66 + AlphaStake = 67 + TaoStake = 68 + TotalStake = 69 + TaoDividendsPerHotkey = 70 + AlphaDividendsPerHotkey = 71 + + @staticmethod + def all_indices() -> list[int]: + return [member.value for member in SelectiveMetagraphIndex] From 1436f2477ea663b2b60bb98fb6ae9b9c504f0384 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 23 Apr 2025 13:54:38 -0700 Subject: [PATCH 022/251] update import in `chain_data` module --- bittensor/core/chain_data/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bittensor/core/chain_data/__init__.py b/bittensor/core/chain_data/__init__.py index 03b5fbe251..9e1852e1d5 100644 --- a/bittensor/core/chain_data/__init__.py +++ b/bittensor/core/chain_data/__init__.py @@ -16,6 +16,7 @@ MetagraphInfoEmissions, MetagraphInfoPool, MetagraphInfoParams, + SelectiveMetagraphIndex, ) from .neuron_info import NeuronInfo from .neuron_info_lite import NeuronInfoLite @@ -50,6 +51,7 @@ ProposalCallData, ProposalVoteData, ScheduledColdkeySwapInfo, + SelectiveMetagraphIndex, StakeInfo, SubnetHyperparameters, SubnetIdentity, From 2b7d8f22812661646b6b91322a8e57d26017214e Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 23 Apr 2025 14:06:16 -0700 Subject: [PATCH 023/251] fix bug for MetagraphInfo field: owner_hotkey and owner_coldkey --- bittensor/core/chain_data/metagraph_info.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/bittensor/core/chain_data/metagraph_info.py b/bittensor/core/chain_data/metagraph_info.py index 8cb8968cb5..e2670a7b90 100644 --- a/bittensor/core/chain_data/metagraph_info.py +++ b/bittensor/core/chain_data/metagraph_info.py @@ -177,8 +177,12 @@ def _from_dict(cls, decoded: dict) -> "MetagraphInfo": identity=decoded["identity"], network_registered_at=decoded["network_registered_at"], # Keys for owner. - owner_hotkey=decode_account_id(decoded["owner_hotkey"][0]) if decoded.get("owner_hotkey") else None, - owner_coldkey=decode_account_id(decoded["owner_coldkey"][0]) if decoded.get("owner_coldkey") else None, + owner_hotkey=decode_account_id(decoded["owner_hotkey"][0]) + if decoded.get("owner_hotkey") + else None, + owner_coldkey=decode_account_id(decoded["owner_coldkey"][0]) + if decoded.get("owner_coldkey") + else None, # Tempo terms. block=decoded["block"], tempo=decoded["tempo"], From 05e0770c540efbe79fcb95d96ab04423e00b5b4d Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 23 Apr 2025 14:06:51 -0700 Subject: [PATCH 024/251] improve `Subtensor.get_metagraph_info` --- bittensor/core/subtensor.py | 42 +++++++++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index f7626297ca..14a1c10dd6 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -14,17 +14,18 @@ from bittensor.core.async_subtensor import ProposalVoteData from bittensor.core.axon import Axon from bittensor.core.chain_data import ( + DelegatedInfo, DelegateInfo, DynamicInfo, MetagraphInfo, NeuronInfo, NeuronInfoLite, + SelectiveMetagraphIndex, StakeInfo, + SubnetInfo, + SubnetIdentity, SubnetHyperparameters, WeightCommitInfo, - SubnetIdentity, - SubnetInfo, - DelegatedInfo, decode_account_id, ) from bittensor.core.chain_data.chain_identity import ChainIdentity @@ -1130,24 +1131,43 @@ def get_minimum_required_stake(self) -> Balance: return Balance.from_rao(getattr(result, "value", 0)) def get_metagraph_info( - self, netuid: int, block: Optional[int] = None + self, + netuid: int, + field_indices: Optional[list["SelectiveMetagraphIndex"]] = None, + block: Optional[int] = None, ) -> Optional[MetagraphInfo]: """ - Retrieves the MetagraphInfo dataclass from the node for a single subnet (netuid) + Retrieves full or partial metagraph information for the specified subnet (netuid). Arguments: - netuid: The NetUID of the subnet. - block: the block number at which to retrieve the hyperparameter. Do not specify if using block_hash or - reuse_block + netuid (int): The NetUID of the subnet to query. + field_indices (Optional[list[SelectiveMetagraphIndex]]): An optional list of SelectiveMetagraphIndex values + specifying which fields to retrieve. If not provided, all available fields will be returned. + block (Optional[int]):The block number at which to query the data. If not specified, the current block or + one determined via reuse_block or block_hash will be used. Returns: - MetagraphInfo dataclass + Optional[MetagraphInfo]: A MetagraphInfo object containing the requested subnet data, or None if the subnet + with the given netuid does not exist. + + Example: + meta_info = subtensor.get_metagraph_info(netuid=2) + + partial_meta_info = subtensor.get_metagraph_info( + netuid=2, + field_indices=[SelectiveMetagraphIndex.Name, SelectiveMetagraphIndex.OwnerHotkeys] + ) """ + indices = SelectiveMetagraphIndex.all_indices() + + if field_indices and isinstance(field_indices, list): + indices = [f.value for f in field_indices] + block_hash = self.determine_block_hash(block) query = self.substrate.runtime_call( "SubnetInfoRuntimeApi", - "get_metagraph", - params=[netuid], + "get_selective_metagraph", + params=[netuid, indices], block_hash=block_hash, ) if query.value is None: From bb60333fda7d08fbbf5fa1305c36a8fa544cec28 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 23 Apr 2025 14:06:57 -0700 Subject: [PATCH 025/251] improve `AsyncSubtensor.get_metagraph_info` --- bittensor/core/async_subtensor.py | 32 ++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 1bd3ce9820..7a5e508b1e 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -16,17 +16,18 @@ from bittensor.core.chain_data import ( DelegateInfo, - StakeInfo, + DynamicInfo, MetagraphInfo, NeuronInfoLite, NeuronInfo, ProposalVoteData, + StakeInfo, + SelectiveMetagraphIndex, SubnetHyperparameters, SubnetIdentity, SubnetInfo, WeightCommitInfo, decode_account_id, - DynamicInfo, ) from bittensor.core.chain_data.chain_identity import ChainIdentity from bittensor.core.chain_data.delegate_info import DelegatedInfo @@ -1470,15 +1471,18 @@ async def get_minimum_required_stake(self): async def get_metagraph_info( self, netuid: int, + field_indices: Optional[list["SelectiveMetagraphIndex"]] = None, block: Optional[int] = None, block_hash: Optional[str] = None, reuse_block: bool = False, ) -> Optional[MetagraphInfo]: """ - Retrieves the MetagraphInfo dataclass from the node for a single subnet (netuid) + Retrieves full or partial metagraph information for the specified subnet (netuid). Arguments: - netuid: The NetUID of the subnet. + netuid (int): The NetUID of the subnet to query. + field_indices (Optional[list[SelectiveMetagraphIndex]]): An optional list of SelectiveMetagraphIndex values + specifying which fields to retrieve. If not provided, all available fields will be returned. block: the block number at which to retrieve the hyperparameter. Do not specify if using block_hash or reuse_block block_hash: The hash of blockchain block number for the query. Do not specify if using @@ -1486,16 +1490,30 @@ async def get_metagraph_info( reuse_block: Whether to reuse the last-used block hash. Do not set if using block_hash or block. Returns: - MetagraphInfo dataclass + Optional[MetagraphInfo]: A MetagraphInfo object containing the requested subnet data, or None if the subnet + with the given netuid does not exist. + + Example: + meta_info = subtensor.get_metagraph_info(netuid=2) + + partial_meta_info = subtensor.get_metagraph_info( + netuid=2, + field_indices=[SelectiveMetagraphIndex.Name, SelectiveMetagraphIndex.OwnerHotkeys] + ) """ + indices = SelectiveMetagraphIndex.all_indices() + + if field_indices and isinstance(field_indices, list): + indices = [f.value for f in field_indices] + block_hash = await self.determine_block_hash(block, block_hash, reuse_block) if not block_hash and reuse_block: block_hash = self.substrate.last_block_hash query = await self.substrate.runtime_call( "SubnetInfoRuntimeApi", - "get_metagraph", - params=[netuid], + "get_selective_metagraph", + params=[netuid, indices], block_hash=block_hash, ) if query.value is None: From 3c7759029b5223c429b67af89f8d495ac69967ae Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 23 Apr 2025 14:19:05 -0700 Subject: [PATCH 026/251] add the check if `field_indices` is correct data --- bittensor/core/async_subtensor.py | 11 +++++++++-- bittensor/core/subtensor.py | 11 +++++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 7a5e508b1e..4dcaa744ad 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -1503,8 +1503,15 @@ async def get_metagraph_info( """ indices = SelectiveMetagraphIndex.all_indices() - if field_indices and isinstance(field_indices, list): - indices = [f.value for f in field_indices] + if field_indices: + if isinstance(field_indices, list) and all( + isinstance(f, SelectiveMetagraphIndex) for f in field_indices + ): + indices = [f.value for f in field_indices] + else: + raise ValueError( + "`field_indices` must be a list of SelectiveMetagraphIndex items." + ) block_hash = await self.determine_block_hash(block, block_hash, reuse_block) if not block_hash and reuse_block: diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 14a1c10dd6..ef82dc7c4c 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -1160,8 +1160,15 @@ def get_metagraph_info( """ indices = SelectiveMetagraphIndex.all_indices() - if field_indices and isinstance(field_indices, list): - indices = [f.value for f in field_indices] + if field_indices: + if isinstance(field_indices, list) and all( + isinstance(f, SelectiveMetagraphIndex) for f in field_indices + ): + indices = [f.value for f in field_indices] + else: + raise ValueError( + "`field_indices` must be a list of SelectiveMetagraphIndex items." + ) block_hash = self.determine_block_hash(block) query = self.substrate.runtime_call( From 66e8688e3a189122d44fd3ebda219e72bb906c6a Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 23 Apr 2025 14:20:53 -0700 Subject: [PATCH 027/251] fix e2e --- tests/e2e_tests/test_metagraph.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/e2e_tests/test_metagraph.py b/tests/e2e_tests/test_metagraph.py index 3338a0959e..55e475484b 100644 --- a/tests/e2e_tests/test_metagraph.py +++ b/tests/e2e_tests/test_metagraph.py @@ -206,8 +206,8 @@ def test_metagraph_info(subtensor, alice_wallet, bob_wallet): symbol="α", identity=None, network_registered_at=0, - owner_hotkey=(NULL_KEY,), - owner_coldkey=(NULL_KEY,), + owner_hotkey=NULL_KEY, + owner_coldkey=NULL_KEY, block=1, tempo=100, last_step=0, @@ -299,8 +299,8 @@ def test_metagraph_info(subtensor, alice_wallet, bob_wallet): symbol="Τ", identity=None, network_registered_at=0, - owner_hotkey=(NULL_KEY,), - owner_coldkey=(NULL_KEY,), + owner_hotkey=NULL_KEY, + owner_coldkey=NULL_KEY, block=1, tempo=100, last_step=0, From c59f3520e53e057eb12674b3c6f80753ed1a30e2 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 23 Apr 2025 16:19:31 -0700 Subject: [PATCH 028/251] Ensure null checks use "is not None" consistently --- bittensor/core/chain_data/metagraph_info.py | 62 +++++++++++---------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/bittensor/core/chain_data/metagraph_info.py b/bittensor/core/chain_data/metagraph_info.py index e2670a7b90..b6e7536150 100644 --- a/bittensor/core/chain_data/metagraph_info.py +++ b/bittensor/core/chain_data/metagraph_info.py @@ -157,10 +157,10 @@ def _from_dict(cls, decoded: dict) -> "MetagraphInfo": decoded.update({"symbol": bytes(symbol).decode()}) ii_list = [] - if decoded.get("identity"): + if decoded.get("identity") is not None: ii_list.append("identity") - if decoded.get("identities"): + if decoded.get("identities") is not None: ii_list.append("identities") for key in ii_list: @@ -178,10 +178,10 @@ def _from_dict(cls, decoded: dict) -> "MetagraphInfo": network_registered_at=decoded["network_registered_at"], # Keys for owner. owner_hotkey=decode_account_id(decoded["owner_hotkey"][0]) - if decoded.get("owner_hotkey") + if decoded.get("owner_hotkey") is not None else None, owner_coldkey=decode_account_id(decoded["owner_coldkey"][0]) - if decoded.get("owner_coldkey") + if decoded.get("owner_coldkey") is not None else None, # Tempo terms. block=decoded["block"], @@ -202,17 +202,17 @@ def _from_dict(cls, decoded: dict) -> "MetagraphInfo": moving_price=Balance.from_tao( fixed_to_float(decoded.get("moving_price"), 32) ) - if decoded.get("moving_price") + if decoded.get("moving_price") is not None else None, # Hparams for epoch rho=decoded["rho"], kappa=decoded["kappa"], # Validator params min_allowed_weights=u16tf(decoded["min_allowed_weights"]) - if decoded["min_allowed_weights"] + if decoded.get("min_allowed_weights") is not None else None, max_weights_limit=u16tf(decoded["max_weights_limit"]) - if decoded["max_weights_limit"] + if decoded["max_weights_limit"] is not None else None, weights_version=decoded["weights_version"], weights_rate_limit=decoded["weights_rate_limit"], @@ -222,20 +222,22 @@ def _from_dict(cls, decoded: dict) -> "MetagraphInfo": num_uids=decoded["num_uids"], max_uids=decoded["max_uids"], burn=_tbwu(decoded["burn"]), - difficulty=u64tf(decoded["difficulty"]) if decoded["difficulty"] else None, + difficulty=u64tf(decoded["difficulty"]) + if decoded["difficulty"] is not None + else None, registration_allowed=decoded["registration_allowed"], pow_registration_allowed=decoded["pow_registration_allowed"], immunity_period=decoded["immunity_period"], min_difficulty=u64tf(decoded["min_difficulty"]) - if decoded["min_difficulty"] + if decoded["min_difficulty"] is not None else None, max_difficulty=u64tf(decoded["max_difficulty"]) - if decoded["max_difficulty"] + if decoded["max_difficulty"] is not None else None, min_burn=_tbwu(decoded["min_burn"]), max_burn=_tbwu(decoded["max_burn"]), adjustment_alpha=u64tf(decoded["adjustment_alpha"]) - if decoded["adjustment_alpha"] + if decoded["adjustment_alpha"] is not None else None, adjustment_interval=decoded["adjustment_interval"], target_regs_per_interval=decoded["target_regs_per_interval"], @@ -246,69 +248,73 @@ def _from_dict(cls, decoded: dict) -> "MetagraphInfo": commit_reveal_period=decoded["commit_reveal_period"], # Bonds liquid_alpha_enabled=decoded["liquid_alpha_enabled"], - alpha_high=u16tf(decoded["alpha_high"]) if decoded["alpha_high"] else None, - alpha_low=u16tf(decoded["alpha_low"]) if decoded["alpha_low"] else None, + alpha_high=u16tf(decoded["alpha_high"]) + if decoded["alpha_high"] is not None + else None, + alpha_low=u16tf(decoded["alpha_low"]) + if decoded["alpha_low"] is not None + else None, bonds_moving_avg=u64tf(decoded["bonds_moving_avg"]) - if decoded["bonds_moving_avg"] + if decoded["bonds_moving_avg"] is not None else None, # Metagraph info. hotkeys=[decode_account_id(ck) for ck in decoded.get("hotkeys", [])] - if decoded.get("hotkeys") + if decoded.get("hotkeys") is not None else None, coldkeys=[decode_account_id(hk) for hk in decoded.get("coldkeys", [])] - if decoded.get("coldkeys") + if decoded.get("coldkeys") is not None else None, identities=decoded["identities"], axons=decoded.get("axons", []), active=decoded["active"], validator_permit=decoded["validator_permit"], pruning_score=[u16tf(ps) for ps in decoded.get("pruning_score", [])] - if decoded.get("pruning_score") + if decoded.get("pruning_score") is not None else None, last_update=decoded["last_update"], emission=[_tbwu(em, _netuid) for em in decoded.get("emission", [])] - if decoded.get("emission") + if decoded.get("emission") is not None else None, dividends=[u16tf(dv) for dv in decoded.get("dividends", [])] - if decoded.get("dividends") + if decoded.get("dividends") is not None else None, incentives=[u16tf(ic) for ic in decoded.get("incentives", [])] - if decoded.get("incentives") + if decoded.get("incentives") is not None else None, consensus=[u16tf(cs) for cs in decoded.get("consensus", [])] - if decoded.get("consensus") + if decoded.get("consensus") is not None else None, trust=[u16tf(tr) for tr in decoded.get("trust", [])] - if decoded.get("trust") + if decoded.get("trust") is not None else None, rank=[u16tf(rk) for rk in decoded.get("rank", [])] - if decoded.get("rank") + if decoded.get("rank") is not None else None, block_at_registration=decoded["block_at_registration"], alpha_stake=[_tbwu(ast, _netuid) for ast in decoded["alpha_stake"]] - if decoded.get("alpha_stake") + if decoded.get("alpha_stake") is not None else None, tao_stake=[ _tbwu(ts) * settings.ROOT_TAO_STAKE_WEIGHT for ts in decoded["tao_stake"] ] - if decoded.get("tao_stake") + if decoded.get("tao_stake") is not None else None, total_stake=[_tbwu(ts, _netuid) for ts in decoded["total_stake"]] - if decoded.get("total_stake") + if decoded.get("total_stake") is not None else None, # Dividend break down tao_dividends_per_hotkey=[ (decode_account_id(alpha[0]), _tbwu(alpha[1])) for alpha in decoded["tao_dividends_per_hotkey"] ] - if decoded.get("tao_dividends_per_hotkey") + if decoded.get("tao_dividends_per_hotkey") is not None else None, alpha_dividends_per_hotkey=[ (decode_account_id(adphk[0]), _tbwu(adphk[1], _netuid)) for adphk in decoded["alpha_dividends_per_hotkey"] ] - if decoded.get("alpha_dividends_per_hotkey") + if decoded.get("alpha_dividends_per_hotkey") is not None else None, ) From 92a1928e7e00396e7a36e65ce5de66a36553f1f1 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 23 Apr 2025 16:19:55 -0700 Subject: [PATCH 029/251] fix e2e test --- tests/e2e_tests/test_metagraph.py | 38 +++++++++++++++---------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/tests/e2e_tests/test_metagraph.py b/tests/e2e_tests/test_metagraph.py index 55e475484b..85fe43c3db 100644 --- a/tests/e2e_tests/test_metagraph.py +++ b/tests/e2e_tests/test_metagraph.py @@ -206,22 +206,22 @@ def test_metagraph_info(subtensor, alice_wallet, bob_wallet): symbol="α", identity=None, network_registered_at=0, - owner_hotkey=NULL_KEY, - owner_coldkey=NULL_KEY, + owner_hotkey="5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM", + owner_coldkey="5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM", 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(1), + alpha_in=Balance.from_tao(10).set_unit(netuid=alice_subnet_netuid), + alpha_out=Balance.from_tao(1).set_unit(netuid=alice_subnet_netuid), tao_in=Balance.from_tao(10), - alpha_out_emission=Balance(0), - alpha_in_emission=Balance(0), + alpha_out_emission=Balance(0).set_unit(netuid=alice_subnet_netuid), + alpha_in_emission=Balance(0).set_unit(netuid=alice_subnet_netuid), tao_in_emission=Balance(0), - pending_alpha_emission=Balance(0), + pending_alpha_emission=Balance(0).set_unit(netuid=alice_subnet_netuid), pending_root_emission=Balance(0), - subnet_volume=Balance(0), + subnet_volume=Balance(0).set_unit(netuid=alice_subnet_netuid), moving_price=Balance(0), rho=10, kappa=32767, @@ -272,16 +272,16 @@ def test_metagraph_info(subtensor, alice_wallet, bob_wallet): validator_permit=(False,), pruning_score=[0.0], last_update=(0,), - emission=[Balance(0)], + emission=[Balance(0).set_unit(alice_subnet_netuid)], dividends=[0.0], incentives=[0.0], consensus=[0.0], trust=[0.0], rank=[0.0], block_at_registration=(0,), - alpha_stake=[Balance.from_tao(1.0)], + alpha_stake=[Balance.from_tao(1.0).set_unit(alice_subnet_netuid)], tao_stake=[Balance(0)], - total_stake=[Balance.from_tao(1.0)], + total_stake=[Balance.from_tao(1.0).set_unit(alice_subnet_netuid)], tao_dividends_per_hotkey=[ ("5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM", Balance(0)) ], @@ -299,18 +299,18 @@ def test_metagraph_info(subtensor, alice_wallet, bob_wallet): symbol="Τ", identity=None, network_registered_at=0, - owner_hotkey=NULL_KEY, - owner_coldkey=NULL_KEY, + owner_hotkey="5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM", + owner_coldkey="5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM", block=1, tempo=100, last_step=0, blocks_since_last_step=1, subnet_emission=Balance(0), - alpha_in=Balance(0), - alpha_out=Balance(0), + alpha_in=Balance(0).set_unit(netuid=alice_subnet_netuid), + alpha_out=Balance(0).set_unit(netuid=alice_subnet_netuid), tao_in=Balance(0), - alpha_out_emission=Balance(0), - alpha_in_emission=Balance(0), + alpha_out_emission=Balance(0).set_unit(netuid=alice_subnet_netuid), + alpha_in_emission=Balance(0).set_unit(netuid=alice_subnet_netuid), tao_in_emission=Balance(0), pending_alpha_emission=Balance(0), pending_root_emission=Balance(0), @@ -420,8 +420,8 @@ def test_metagraph_info(subtensor, alice_wallet, bob_wallet): netuid=alice_subnet_netuid, block=block ) - assert metagraph_info.owner_coldkey == (tuple(alice_wallet.hotkey.public_key),) - assert metagraph_info.owner_hotkey == (tuple(alice_wallet.coldkey.public_key),) + assert metagraph_info.owner_coldkey == alice_wallet.hotkey.ss58_address + assert metagraph_info.owner_hotkey == alice_wallet.coldkey.ss58_address metagraph_infos = subtensor.get_all_metagraphs_info(block) From de3dc0c4a61331a3df1466b329d49093c76370c6 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 23 Apr 2025 16:43:33 -0700 Subject: [PATCH 030/251] add sync unit tests --- tests/unit_tests/test_subtensor.py | 115 ++++++++++++++++++++++++----- 1 file changed, 97 insertions(+), 18 deletions(-) diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index 3bbb8230c3..072188d984 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -1,20 +1,3 @@ -# The MIT License (MIT) -# Copyright © 2024 Opentensor Foundation -# -# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated -# documentation files (the "Software"), to deal in the Software without restriction, including without limitation -# the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, -# and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all copies or substantial portions of -# the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO -# THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -# DEALINGS IN THE SOFTWARE. - import argparse import unittest.mock as mock import datetime @@ -31,7 +14,7 @@ from bittensor.core import subtensor as subtensor_module from bittensor.core.async_subtensor import AsyncSubtensor, logging from bittensor.core.axon import Axon -from bittensor.core.chain_data import SubnetHyperparameters +from bittensor.core.chain_data import SubnetHyperparameters, SelectiveMetagraphIndex from bittensor.core.extrinsics.serving import do_serve_axon from bittensor.core.settings import version_as_int from bittensor.core.subtensor import Subtensor @@ -3409,3 +3392,99 @@ def test_start_call(subtensor, mocker): wait_for_finalization=False, ) assert result == mocked_extrinsic.return_value + + +def test_get_metagraph_info_all_fields(subtensor, mocker): + """Test get_metagraph_info with all fields (default behavior).""" + # Preps + netuid = 1 + mock_value = {"mock": "data"} + + mock_runtime_call = mocker.patch.object( + subtensor.substrate, + "runtime_call", + return_value=mocker.Mock(value=mock_value), + ) + mock_from_dict = mocker.patch.object( + subtensor_module.MetagraphInfo, "from_dict", return_value="parsed_metagraph" + ) + + # Call + result = subtensor.get_metagraph_info(netuid=netuid) + + # Asserts + assert result == "parsed_metagraph" + mock_runtime_call.assert_called_once_with( + "SubnetInfoRuntimeApi", + "get_selective_metagraph", + params=[netuid, SelectiveMetagraphIndex.all_indices()], + block_hash=subtensor.determine_block_hash(None), + ) + mock_from_dict.assert_called_once_with(mock_value) + + +def test_get_metagraph_info_specific_fields(subtensor, mocker): + """Test get_metagraph_info with specific fields.""" + # Preps + netuid = 1 + mock_value = {"mock": "data"} + fields = [SelectiveMetagraphIndex.Name, SelectiveMetagraphIndex.OwnerHotkey] + + mock_runtime_call = mocker.patch.object( + subtensor.substrate, + "runtime_call", + return_value=mocker.Mock(value=mock_value), + ) + mock_from_dict = mocker.patch.object( + subtensor_module.MetagraphInfo, "from_dict", return_value="parsed_metagraph" + ) + + # Call + result = subtensor.get_metagraph_info(netuid=netuid, field_indices=fields) + + # Asserts + assert result == "parsed_metagraph" + mock_runtime_call.assert_called_once_with( + "SubnetInfoRuntimeApi", + "get_selective_metagraph", + params=[netuid, [f.value for f in fields]], + block_hash=subtensor.determine_block_hash(None), + ) + mock_from_dict.assert_called_once_with(mock_value) + + +@pytest.mark.parametrize( + "wrong_fields", + [ + [ + "invalid", + ], + [SelectiveMetagraphIndex.Active, 1], + [1, 2, 3], + ], +) +def test_get_metagraph_info_invalid_field_indices(subtensor, wrong_fields): + """Test get_metagraph_info raises ValueError on invalid field_indices.""" + with pytest.raises( + ValueError, + match="`field_indices` must be a list of SelectiveMetagraphIndex items.", + ): + subtensor.get_metagraph_info(netuid=1, field_indices=wrong_fields) + + +def test_get_metagraph_info_subnet_not_exist(subtensor, mocker): + """Test get_metagraph_info returns None when subnet doesn't exist.""" + netuid = 1 + mocker.patch.object( + subtensor.substrate, + "runtime_call", + return_value=mocker.Mock(value=None), + ) + + mocked_logger = mocker.Mock() + mocker.patch("bittensor.core.subtensor.logging.error", new=mocked_logger) + + result = subtensor.get_metagraph_info(netuid=netuid) + + assert result is None + mocked_logger.assert_called_once_with(f"Subnet {netuid} does not exist.") From 2a1a9d25ed591b7efaced3b7e647447c07a25641 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 23 Apr 2025 16:43:43 -0700 Subject: [PATCH 031/251] add async unit tests --- tests/unit_tests/test_async_subtensor.py | 111 ++++++++++++++++++++++- 1 file changed, 107 insertions(+), 4 deletions(-) diff --git a/tests/unit_tests/test_async_subtensor.py b/tests/unit_tests/test_async_subtensor.py index ca2a82f603..8b9c58ab73 100644 --- a/tests/unit_tests/test_async_subtensor.py +++ b/tests/unit_tests/test_async_subtensor.py @@ -8,10 +8,13 @@ from bittensor import u64_normalized_float from bittensor.core import async_subtensor from bittensor.core.async_subtensor import AsyncSubtensor -from bittensor.core.chain_data.chain_identity import ChainIdentity -from bittensor.core.chain_data.neuron_info import NeuronInfo -from bittensor.core.chain_data.stake_info import StakeInfo -from bittensor.core.chain_data import proposal_vote_data +from bittensor.core.chain_data import ( + proposal_vote_data, + ChainIdentity, + NeuronInfo, + StakeInfo, + SelectiveMetagraphIndex, +) from bittensor.utils import U64_MAX from bittensor.utils.balance import Balance from tests.helpers.helpers import assert_submit_signed_extrinsic @@ -3059,3 +3062,103 @@ async def test_start_call(subtensor, mocker): wait_for_finalization=False, ) assert result == mocked_extrinsic.return_value + + +@pytest.mark.asyncio +async def test_get_metagraph_info_all_fields(subtensor, mocker): + """Test get_metagraph_info with all fields (default behavior).""" + # Preps + netuid = 1 + mock_value = {"mock": "data"} + + mock_runtime_call = mocker.patch.object( + subtensor.substrate, + "runtime_call", + return_value=mocker.AsyncMock(value=mock_value), + ) + mock_from_dict = mocker.patch.object( + async_subtensor.MetagraphInfo, "from_dict", return_value="parsed_metagraph" + ) + + # Call + result = await subtensor.get_metagraph_info(netuid=netuid) + + # Asserts + assert result == "parsed_metagraph" + mock_runtime_call.assert_awaited_once_with( + "SubnetInfoRuntimeApi", + "get_selective_metagraph", + params=[netuid, SelectiveMetagraphIndex.all_indices()], + block_hash=await subtensor.determine_block_hash(None), + ) + mock_from_dict.assert_called_once_with(mock_value) + + +@pytest.mark.asyncio +async def test_get_metagraph_info_specific_fields(subtensor, mocker): + """Test get_metagraph_info with specific fields.""" + # Preps + netuid = 1 + mock_value = {"mock": "data"} + fields = [SelectiveMetagraphIndex.Name, SelectiveMetagraphIndex.OwnerHotkey] + + mock_runtime_call = mocker.patch.object( + subtensor.substrate, + "runtime_call", + return_value=mocker.AsyncMock(value=mock_value), + ) + mock_from_dict = mocker.patch.object( + async_subtensor.MetagraphInfo, "from_dict", return_value="parsed_metagraph" + ) + + # Call + result = await subtensor.get_metagraph_info(netuid=netuid, field_indices=fields) + + # Asserts + assert result == "parsed_metagraph" + mock_runtime_call.assert_awaited_once_with( + "SubnetInfoRuntimeApi", + "get_selective_metagraph", + params=[netuid, [f.value for f in fields]], + block_hash=await subtensor.determine_block_hash(None), + ) + mock_from_dict.assert_called_once_with(mock_value) + + +@pytest.mark.parametrize( + "wrong_fields", + [ + [ + "invalid", + ], + [SelectiveMetagraphIndex.Active, 1], + [1, 2, 3], + ], +) +@pytest.mark.asyncio +async def test_get_metagraph_info_invalid_field_indices(subtensor, wrong_fields): + """Test get_metagraph_info raises ValueError on invalid field_indices.""" + with pytest.raises( + ValueError, + match="`field_indices` must be a list of SelectiveMetagraphIndex items.", + ): + await subtensor.get_metagraph_info(netuid=1, field_indices=wrong_fields) + + +@pytest.mark.asyncio +async def test_get_metagraph_info_subnet_not_exist(subtensor, mocker): + """Test get_metagraph_info returns None when subnet doesn't exist.""" + netuid = 1 + mocker.patch.object( + subtensor.substrate, + "runtime_call", + return_value=mocker.AsyncMock(value=None), + ) + + mocked_logger = mocker.Mock() + mocker.patch("bittensor.core.subtensor.logging.error", new=mocked_logger) + + result = await subtensor.get_metagraph_info(netuid=netuid) + + assert result is None + mocked_logger.assert_called_once_with(f"Subnet {netuid} does not exist.") From 46315e229d60c0b23423b68cf1e5d3632edd14d0 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 23 Apr 2025 17:41:05 -0700 Subject: [PATCH 032/251] commented test + TODO --- .../test_subtensor_integration.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/tests/integration_tests/test_subtensor_integration.py b/tests/integration_tests/test_subtensor_integration.py index db8bf19453..0e7c62151a 100644 --- a/tests/integration_tests/test_subtensor_integration.py +++ b/tests/integration_tests/test_subtensor_integration.py @@ -40,13 +40,14 @@ async def test_get_all_subnets_info(mocker): assert result[1].blocks_since_epoch == 88 -@pytest.mark.asyncio -async def test_metagraph(mocker): - subtensor = await prepare_test(mocker, "metagraph") - result = subtensor.metagraph(1) - assert result.n == 1 - assert result.netuid == 1 - assert result.block == 3264143 +# TODO: Improve integration tests workflow (https://github.com/opentensor/bittensor/issues/2435#issuecomment-2825858004) +# @pytest.mark.asyncio +# async def test_metagraph(mocker): +# subtensor = await prepare_test(mocker, "metagraph") +# result = subtensor.metagraph(1) +# assert result.n == 1 +# assert result.netuid == 1 +# assert result.block == 3264143 @pytest.mark.asyncio From 0bc61a5290a82d7c96a2e3e39138ac62764d3c5e Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 23 Apr 2025 18:39:13 -0700 Subject: [PATCH 033/251] add netuid to result for always --- bittensor/core/async_subtensor.py | 6 +++--- bittensor/core/subtensor.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 4dcaa744ad..8426bffcd4 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -1501,13 +1501,13 @@ async def get_metagraph_info( field_indices=[SelectiveMetagraphIndex.Name, SelectiveMetagraphIndex.OwnerHotkeys] ) """ - indices = SelectiveMetagraphIndex.all_indices() + indexes = SelectiveMetagraphIndex.all_indices() if field_indices: if isinstance(field_indices, list) and all( isinstance(f, SelectiveMetagraphIndex) for f in field_indices ): - indices = [f.value for f in field_indices] + indexes = [f.value for f in field_indices] else: raise ValueError( "`field_indices` must be a list of SelectiveMetagraphIndex items." @@ -1520,7 +1520,7 @@ async def get_metagraph_info( query = await self.substrate.runtime_call( "SubnetInfoRuntimeApi", "get_selective_metagraph", - params=[netuid, indices], + params=[netuid, indexes if 0 in indexes else [0] + indexes], block_hash=block_hash, ) if query.value is None: diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index ef82dc7c4c..9fd500c16e 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -1158,13 +1158,13 @@ def get_metagraph_info( field_indices=[SelectiveMetagraphIndex.Name, SelectiveMetagraphIndex.OwnerHotkeys] ) """ - indices = SelectiveMetagraphIndex.all_indices() + indexes = SelectiveMetagraphIndex.all_indices() if field_indices: if isinstance(field_indices, list) and all( isinstance(f, SelectiveMetagraphIndex) for f in field_indices ): - indices = [f.value for f in field_indices] + indexes = [f.value for f in field_indices] else: raise ValueError( "`field_indices` must be a list of SelectiveMetagraphIndex items." @@ -1174,7 +1174,7 @@ def get_metagraph_info( query = self.substrate.runtime_call( "SubnetInfoRuntimeApi", "get_selective_metagraph", - params=[netuid, indices], + params=[netuid, indexes if 0 in indexes else [0] + indexes], block_hash=block_hash, ) if query.value is None: From 630dda1481d929059c70321a4b825de6b7230bbb Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 23 Apr 2025 18:45:54 -0700 Subject: [PATCH 034/251] add one more e2e test for get_metagraph_info with field_indices argument --- tests/e2e_tests/test_metagraph.py | 229 ++++++++++++++++++++++++++++++ 1 file changed, 229 insertions(+) diff --git a/tests/e2e_tests/test_metagraph.py b/tests/e2e_tests/test_metagraph.py index 85fe43c3db..104c2ffceb 100644 --- a/tests/e2e_tests/test_metagraph.py +++ b/tests/e2e_tests/test_metagraph.py @@ -4,6 +4,7 @@ import time from bittensor.core.chain_data.metagraph_info import MetagraphInfo +from bittensor.core.chain_data import SelectiveMetagraphIndex from bittensor.utils.balance import Balance from bittensor.utils.btlogging import logging from tests.e2e_tests.utils.e2e_test_utils import wait_to_start_call @@ -434,6 +435,234 @@ def test_metagraph_info(subtensor, alice_wallet, bob_wallet): assert metagraph_info is None +def test_metagraph_info_with_indexes(subtensor, alice_wallet, bob_wallet): + """ + Tests: + - Check MetagraphInfo + - Register Neuron + - Register Subnet + - Check MetagraphInfo is updated + """ + + alice_subnet_netuid = subtensor.get_total_subnets() # 2 + subtensor.register_subnet(alice_wallet, True, True) + + field_indices = [ + SelectiveMetagraphIndex.Name, + SelectiveMetagraphIndex.Active, + SelectiveMetagraphIndex.OwnerHotkey, + SelectiveMetagraphIndex.OwnerColdkey, + SelectiveMetagraphIndex.Axons, + ] + + metagraph_info = subtensor.get_metagraph_info( + netuid=alice_subnet_netuid, field_indices=field_indices + ) + + assert metagraph_info == MetagraphInfo( + netuid=alice_subnet_netuid, + name="omron", + owner_hotkey=alice_wallet.hotkey.ss58_address, + owner_coldkey=alice_wallet.coldkey.ss58_address, + active=(True,), + axons=( + { + "block": 0, + "ip": 0, + "ip_type": 0, + "placeholder1": 0, + "placeholder2": 0, + "port": 0, + "protocol": 0, + "version": 0, + }, + ), + symbol=None, + identity=None, + network_registered_at=None, + block=None, + tempo=None, + last_step=None, + blocks_since_last_step=None, + subnet_emission=None, + alpha_in=None, + alpha_out=None, + tao_in=None, + alpha_out_emission=None, + alpha_in_emission=None, + tao_in_emission=None, + pending_alpha_emission=None, + pending_root_emission=None, + subnet_volume=None, + moving_price=None, + rho=None, + kappa=None, + min_allowed_weights=None, + max_weights_limit=None, + weights_version=None, + weights_rate_limit=None, + activity_cutoff=None, + max_validators=None, + num_uids=None, + max_uids=None, + burn=None, + difficulty=None, + registration_allowed=None, + pow_registration_allowed=None, + immunity_period=None, + min_difficulty=None, + max_difficulty=None, + min_burn=None, + max_burn=None, + adjustment_alpha=None, + adjustment_interval=None, + target_regs_per_interval=None, + max_regs_per_block=None, + serving_rate_limit=None, + commit_reveal_weights_enabled=None, + commit_reveal_period=None, + liquid_alpha_enabled=None, + alpha_high=None, + alpha_low=None, + bonds_moving_avg=None, + hotkeys=None, + coldkeys=None, + identities=None, + validator_permit=None, + pruning_score=None, + last_update=None, + emission=None, + dividends=None, + incentives=None, + consensus=None, + trust=None, + rank=None, + block_at_registration=None, + alpha_stake=None, + tao_stake=None, + total_stake=None, + tao_dividends_per_hotkey=None, + alpha_dividends_per_hotkey=None, + ) + + assert wait_to_start_call(subtensor, alice_wallet, alice_subnet_netuid) + + assert subtensor.burned_register( + bob_wallet, + netuid=alice_subnet_netuid, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + + fields = [ + SelectiveMetagraphIndex.Name, + SelectiveMetagraphIndex.Active, + SelectiveMetagraphIndex.OwnerHotkey, + SelectiveMetagraphIndex.OwnerColdkey, + SelectiveMetagraphIndex.Axons, + ] + + metagraph_info = subtensor.get_metagraph_info( + netuid=alice_subnet_netuid, field_indices=fields + ) + + assert metagraph_info == MetagraphInfo( + netuid=alice_subnet_netuid, + name="omron", + owner_hotkey=alice_wallet.hotkey.ss58_address, + owner_coldkey=alice_wallet.coldkey.ss58_address, + active=(True, True), + axons=( + { + "block": 0, + "ip": 0, + "ip_type": 0, + "placeholder1": 0, + "placeholder2": 0, + "port": 0, + "protocol": 0, + "version": 0, + }, + { + "block": 0, + "ip": 0, + "ip_type": 0, + "placeholder1": 0, + "placeholder2": 0, + "port": 0, + "protocol": 0, + "version": 0, + }, + ), + symbol=None, + identity=None, + network_registered_at=None, + block=None, + tempo=None, + last_step=None, + blocks_since_last_step=None, + subnet_emission=None, + alpha_in=None, + alpha_out=None, + tao_in=None, + alpha_out_emission=None, + alpha_in_emission=None, + tao_in_emission=None, + pending_alpha_emission=None, + pending_root_emission=None, + subnet_volume=None, + moving_price=None, + rho=None, + kappa=None, + min_allowed_weights=None, + max_weights_limit=None, + weights_version=None, + weights_rate_limit=None, + activity_cutoff=None, + max_validators=None, + num_uids=None, + max_uids=None, + burn=None, + difficulty=None, + registration_allowed=None, + pow_registration_allowed=None, + immunity_period=None, + min_difficulty=None, + max_difficulty=None, + min_burn=None, + max_burn=None, + adjustment_alpha=None, + adjustment_interval=None, + target_regs_per_interval=None, + max_regs_per_block=None, + serving_rate_limit=None, + commit_reveal_weights_enabled=None, + commit_reveal_period=None, + liquid_alpha_enabled=None, + alpha_high=None, + alpha_low=None, + bonds_moving_avg=None, + hotkeys=None, + coldkeys=None, + identities=None, + validator_permit=None, + pruning_score=None, + last_update=None, + emission=None, + dividends=None, + incentives=None, + consensus=None, + trust=None, + rank=None, + block_at_registration=None, + alpha_stake=None, + tao_stake=None, + total_stake=None, + tao_dividends_per_hotkey=None, + alpha_dividends_per_hotkey=None, + ) + + def test_blocks(subtensor): """ Tests: From c25917f400f367d572b488ca8506f72bc392ac13 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 23 Apr 2025 18:50:51 -0700 Subject: [PATCH 035/251] fix unit tests regarding 0 index in the result --- tests/unit_tests/test_async_subtensor.py | 2 +- tests/unit_tests/test_subtensor.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit_tests/test_async_subtensor.py b/tests/unit_tests/test_async_subtensor.py index 8b9c58ab73..07d3c4e98c 100644 --- a/tests/unit_tests/test_async_subtensor.py +++ b/tests/unit_tests/test_async_subtensor.py @@ -3119,7 +3119,7 @@ async def test_get_metagraph_info_specific_fields(subtensor, mocker): mock_runtime_call.assert_awaited_once_with( "SubnetInfoRuntimeApi", "get_selective_metagraph", - params=[netuid, [f.value for f in fields]], + params=[netuid, [0] + [f.value for f in fields]], block_hash=await subtensor.determine_block_hash(None), ) mock_from_dict.assert_called_once_with(mock_value) diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index 072188d984..0d5f418170 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -3447,7 +3447,7 @@ def test_get_metagraph_info_specific_fields(subtensor, mocker): mock_runtime_call.assert_called_once_with( "SubnetInfoRuntimeApi", "get_selective_metagraph", - params=[netuid, [f.value for f in fields]], + params=[netuid, [0] + [f.value for f in fields]], block_hash=subtensor.determine_block_hash(None), ) mock_from_dict.assert_called_once_with(mock_value) From c44a5ce19beee12825dcaa9a8ccaf81168890b6c Mon Sep 17 00:00:00 2001 From: leopardracer <136604165+leopardracer@users.noreply.github.com> Date: Thu, 24 Apr 2025 20:30:17 +0300 Subject: [PATCH 036/251] Update test_commit_weights.py --- tests/e2e_tests/test_commit_weights.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e_tests/test_commit_weights.py b/tests/e2e_tests/test_commit_weights.py index 1b39dd411e..5015208a1b 100644 --- a/tests/e2e_tests/test_commit_weights.py +++ b/tests/e2e_tests/test_commit_weights.py @@ -153,7 +153,7 @@ async def test_commit_and_reveal_weights_legacy(local_chain, subtensor, alice_wa @pytest.mark.asyncio async def test_commit_weights_uses_next_nonce(local_chain, subtensor, alice_wallet): """ - Tests that commiting weights doesn't re-use a nonce in the transaction pool. + Tests that committing weights doesn't re-use a nonce in the transaction pool. Steps: 1. Register a subnet through Alice From 0d88cfd4921e7b5f0e8491f723d77efa7dd65370 Mon Sep 17 00:00:00 2001 From: leopardracer <136604165+leopardracer@users.noreply.github.com> Date: Thu, 24 Apr 2025 20:31:30 +0300 Subject: [PATCH 037/251] Update chain_interactions.py --- tests/e2e_tests/utils/chain_interactions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e_tests/utils/chain_interactions.py b/tests/e2e_tests/utils/chain_interactions.py index c5871d3155..1914f69ac4 100644 --- a/tests/e2e_tests/utils/chain_interactions.py +++ b/tests/e2e_tests/utils/chain_interactions.py @@ -19,7 +19,7 @@ def get_dynamic_balance(rao: int, netuid: int = 0): - """Returns a Balance object with the given rao and netuid for testing purposes with synamic values.""" + """Returns a Balance object with the given rao and netuid for testing purposes with dynamic values.""" return Balance(rao).set_unit(netuid) From 227b9d1d7bc31239928bd0b677ecccb6339e65d6 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 24 Apr 2025 12:10:23 -0700 Subject: [PATCH 038/251] fix argument name --- bittensor/core/async_subtensor.py | 16 ++++++++-------- bittensor/core/subtensor.py | 16 ++++++++-------- tests/e2e_tests/test_metagraph.py | 4 ++-- tests/unit_tests/test_async_subtensor.py | 8 ++++---- tests/unit_tests/test_subtensor.py | 8 ++++---- 5 files changed, 26 insertions(+), 26 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 8426bffcd4..7a7db978cc 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -1471,7 +1471,7 @@ async def get_minimum_required_stake(self): async def get_metagraph_info( self, netuid: int, - field_indices: Optional[list["SelectiveMetagraphIndex"]] = None, + field_indexes: Optional[list["SelectiveMetagraphIndex"]] = None, block: Optional[int] = None, block_hash: Optional[str] = None, reuse_block: bool = False, @@ -1481,7 +1481,7 @@ async def get_metagraph_info( Arguments: netuid (int): The NetUID of the subnet to query. - field_indices (Optional[list[SelectiveMetagraphIndex]]): An optional list of SelectiveMetagraphIndex values + field_indexes (Optional[list[SelectiveMetagraphIndex]]): An optional list of SelectiveMetagraphIndex values specifying which fields to retrieve. If not provided, all available fields will be returned. block: the block number at which to retrieve the hyperparameter. Do not specify if using block_hash or reuse_block @@ -1498,19 +1498,19 @@ async def get_metagraph_info( partial_meta_info = subtensor.get_metagraph_info( netuid=2, - field_indices=[SelectiveMetagraphIndex.Name, SelectiveMetagraphIndex.OwnerHotkeys] + field_indexes=[SelectiveMetagraphIndex.Name, SelectiveMetagraphIndex.OwnerHotkeys] ) """ indexes = SelectiveMetagraphIndex.all_indices() - if field_indices: - if isinstance(field_indices, list) and all( - isinstance(f, SelectiveMetagraphIndex) for f in field_indices + if field_indexes: + if isinstance(field_indexes, list) and all( + isinstance(f, SelectiveMetagraphIndex) for f in field_indexes ): - indexes = [f.value for f in field_indices] + indexes = [f.value for f in field_indexes] else: raise ValueError( - "`field_indices` must be a list of SelectiveMetagraphIndex items." + "`field_indexes` must be a list of SelectiveMetagraphIndex items." ) block_hash = await self.determine_block_hash(block, block_hash, reuse_block) diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 9fd500c16e..71a35d7ca9 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -1133,7 +1133,7 @@ def get_minimum_required_stake(self) -> Balance: def get_metagraph_info( self, netuid: int, - field_indices: Optional[list["SelectiveMetagraphIndex"]] = None, + field_indexes: Optional[list["SelectiveMetagraphIndex"]] = None, block: Optional[int] = None, ) -> Optional[MetagraphInfo]: """ @@ -1141,7 +1141,7 @@ def get_metagraph_info( Arguments: netuid (int): The NetUID of the subnet to query. - field_indices (Optional[list[SelectiveMetagraphIndex]]): An optional list of SelectiveMetagraphIndex values + field_indexes (Optional[list[SelectiveMetagraphIndex]]): An optional list of SelectiveMetagraphIndex values specifying which fields to retrieve. If not provided, all available fields will be returned. block (Optional[int]):The block number at which to query the data. If not specified, the current block or one determined via reuse_block or block_hash will be used. @@ -1155,19 +1155,19 @@ def get_metagraph_info( partial_meta_info = subtensor.get_metagraph_info( netuid=2, - field_indices=[SelectiveMetagraphIndex.Name, SelectiveMetagraphIndex.OwnerHotkeys] + field_indexes=[SelectiveMetagraphIndex.Name, SelectiveMetagraphIndex.OwnerHotkeys] ) """ indexes = SelectiveMetagraphIndex.all_indices() - if field_indices: - if isinstance(field_indices, list) and all( - isinstance(f, SelectiveMetagraphIndex) for f in field_indices + if field_indexes: + if isinstance(field_indexes, list) and all( + isinstance(f, SelectiveMetagraphIndex) for f in field_indexes ): - indexes = [f.value for f in field_indices] + indexes = [f.value for f in field_indexes] else: raise ValueError( - "`field_indices` must be a list of SelectiveMetagraphIndex items." + "`field_indexes` must be a list of SelectiveMetagraphIndex items." ) block_hash = self.determine_block_hash(block) diff --git a/tests/e2e_tests/test_metagraph.py b/tests/e2e_tests/test_metagraph.py index 104c2ffceb..2f8cab751a 100644 --- a/tests/e2e_tests/test_metagraph.py +++ b/tests/e2e_tests/test_metagraph.py @@ -456,7 +456,7 @@ def test_metagraph_info_with_indexes(subtensor, alice_wallet, bob_wallet): ] metagraph_info = subtensor.get_metagraph_info( - netuid=alice_subnet_netuid, field_indices=field_indices + netuid=alice_subnet_netuid, field_indexes=field_indices ) assert metagraph_info == MetagraphInfo( @@ -563,7 +563,7 @@ def test_metagraph_info_with_indexes(subtensor, alice_wallet, bob_wallet): ] metagraph_info = subtensor.get_metagraph_info( - netuid=alice_subnet_netuid, field_indices=fields + netuid=alice_subnet_netuid, field_indexes=fields ) assert metagraph_info == MetagraphInfo( diff --git a/tests/unit_tests/test_async_subtensor.py b/tests/unit_tests/test_async_subtensor.py index 07d3c4e98c..6d16858761 100644 --- a/tests/unit_tests/test_async_subtensor.py +++ b/tests/unit_tests/test_async_subtensor.py @@ -3112,7 +3112,7 @@ async def test_get_metagraph_info_specific_fields(subtensor, mocker): ) # Call - result = await subtensor.get_metagraph_info(netuid=netuid, field_indices=fields) + result = await subtensor.get_metagraph_info(netuid=netuid, field_indexes=fields) # Asserts assert result == "parsed_metagraph" @@ -3137,12 +3137,12 @@ async def test_get_metagraph_info_specific_fields(subtensor, mocker): ) @pytest.mark.asyncio async def test_get_metagraph_info_invalid_field_indices(subtensor, wrong_fields): - """Test get_metagraph_info raises ValueError on invalid field_indices.""" + """Test get_metagraph_info raises ValueError on invalid field_indexes.""" with pytest.raises( ValueError, - match="`field_indices` must be a list of SelectiveMetagraphIndex items.", + match="`field_indexes` must be a list of SelectiveMetagraphIndex items.", ): - await subtensor.get_metagraph_info(netuid=1, field_indices=wrong_fields) + await subtensor.get_metagraph_info(netuid=1, field_indexes=wrong_fields) @pytest.mark.asyncio diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index 0d5f418170..349d345da0 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -3440,7 +3440,7 @@ def test_get_metagraph_info_specific_fields(subtensor, mocker): ) # Call - result = subtensor.get_metagraph_info(netuid=netuid, field_indices=fields) + result = subtensor.get_metagraph_info(netuid=netuid, field_indexes=fields) # Asserts assert result == "parsed_metagraph" @@ -3464,12 +3464,12 @@ def test_get_metagraph_info_specific_fields(subtensor, mocker): ], ) def test_get_metagraph_info_invalid_field_indices(subtensor, wrong_fields): - """Test get_metagraph_info raises ValueError on invalid field_indices.""" + """Test get_metagraph_info raises ValueError on invalid field_indexes.""" with pytest.raises( ValueError, - match="`field_indices` must be a list of SelectiveMetagraphIndex items.", + match="`field_indexes` must be a list of SelectiveMetagraphIndex items.", ): - subtensor.get_metagraph_info(netuid=1, field_indices=wrong_fields) + subtensor.get_metagraph_info(netuid=1, field_indexes=wrong_fields) def test_get_metagraph_info_subnet_not_exist(subtensor, mocker): From 190faa9f41cdf42830ca7ef062622039acd3eb6d Mon Sep 17 00:00:00 2001 From: Benjamin Himes Date: Thu, 24 Apr 2025 21:14:25 +0200 Subject: [PATCH 039/251] Update docstring --- bittensor/core/async_subtensor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 8426bffcd4..f49614ca02 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -1494,9 +1494,9 @@ async def get_metagraph_info( with the given netuid does not exist. Example: - meta_info = subtensor.get_metagraph_info(netuid=2) + meta_info = await subtensor.get_metagraph_info(netuid=2) - partial_meta_info = subtensor.get_metagraph_info( + partial_meta_info = await subtensor.get_metagraph_info( netuid=2, field_indices=[SelectiveMetagraphIndex.Name, SelectiveMetagraphIndex.OwnerHotkeys] ) From 2112f2316ff767bc249a242391c8989d026604a0 Mon Sep 17 00:00:00 2001 From: Benjamin Himes Date: Thu, 24 Apr 2025 21:14:40 +0200 Subject: [PATCH 040/251] Readability. --- bittensor/core/chain_data/metagraph_info.py | 228 ++++++++++++-------- 1 file changed, 139 insertions(+), 89 deletions(-) diff --git a/bittensor/core/chain_data/metagraph_info.py b/bittensor/core/chain_data/metagraph_info.py index b6e7536150..3ad189c6fd 100644 --- a/bittensor/core/chain_data/metagraph_info.py +++ b/bittensor/core/chain_data/metagraph_info.py @@ -177,12 +177,16 @@ def _from_dict(cls, decoded: dict) -> "MetagraphInfo": identity=decoded["identity"], network_registered_at=decoded["network_registered_at"], # Keys for owner. - owner_hotkey=decode_account_id(decoded["owner_hotkey"][0]) - if decoded.get("owner_hotkey") is not None - else None, - owner_coldkey=decode_account_id(decoded["owner_coldkey"][0]) - if decoded.get("owner_coldkey") is not None - else None, + owner_hotkey=( + decode_account_id(decoded["owner_hotkey"][0]) + if decoded.get("owner_hotkey") is not None + else None + ), + owner_coldkey=( + decode_account_id(decoded["owner_coldkey"][0]) + if decoded.get("owner_coldkey") is not None + else None + ), # Tempo terms. block=decoded["block"], tempo=decoded["tempo"], @@ -199,21 +203,25 @@ def _from_dict(cls, decoded: dict) -> "MetagraphInfo": pending_alpha_emission=_tbwu(decoded["pending_alpha_emission"], _netuid), pending_root_emission=_tbwu(decoded["pending_root_emission"]), subnet_volume=_tbwu(decoded["subnet_volume"], _netuid), - moving_price=Balance.from_tao( - fixed_to_float(decoded.get("moving_price"), 32) - ) - if decoded.get("moving_price") is not None - else None, + moving_price=( + Balance.from_tao(fixed_to_float(decoded.get("moving_price"), 32)) + if decoded.get("moving_price") is not None + else None + ), # Hparams for epoch rho=decoded["rho"], kappa=decoded["kappa"], # Validator params - min_allowed_weights=u16tf(decoded["min_allowed_weights"]) - if decoded.get("min_allowed_weights") is not None - else None, - max_weights_limit=u16tf(decoded["max_weights_limit"]) - if decoded["max_weights_limit"] is not None - else None, + min_allowed_weights=( + u16tf(decoded["min_allowed_weights"]) + if decoded.get("min_allowed_weights") is not None + else None + ), + max_weights_limit=( + u16tf(decoded["max_weights_limit"]) + if decoded["max_weights_limit"] is not None + else None + ), weights_version=decoded["weights_version"], weights_rate_limit=decoded["weights_rate_limit"], activity_cutoff=decoded["activity_cutoff"], @@ -222,23 +230,31 @@ def _from_dict(cls, decoded: dict) -> "MetagraphInfo": num_uids=decoded["num_uids"], max_uids=decoded["max_uids"], burn=_tbwu(decoded["burn"]), - difficulty=u64tf(decoded["difficulty"]) - if decoded["difficulty"] is not None - else None, + difficulty=( + u64tf(decoded["difficulty"]) + if decoded["difficulty"] is not None + else None + ), registration_allowed=decoded["registration_allowed"], pow_registration_allowed=decoded["pow_registration_allowed"], immunity_period=decoded["immunity_period"], - min_difficulty=u64tf(decoded["min_difficulty"]) - if decoded["min_difficulty"] is not None - else None, - max_difficulty=u64tf(decoded["max_difficulty"]) - if decoded["max_difficulty"] is not None - else None, + min_difficulty=( + u64tf(decoded["min_difficulty"]) + if decoded["min_difficulty"] is not None + else None + ), + max_difficulty=( + u64tf(decoded["max_difficulty"]) + if decoded["max_difficulty"] is not None + else None + ), min_burn=_tbwu(decoded["min_burn"]), max_burn=_tbwu(decoded["max_burn"]), - adjustment_alpha=u64tf(decoded["adjustment_alpha"]) - if decoded["adjustment_alpha"] is not None - else None, + adjustment_alpha=( + u64tf(decoded["adjustment_alpha"]) + if decoded["adjustment_alpha"] is not None + else None + ), adjustment_interval=decoded["adjustment_interval"], target_regs_per_interval=decoded["target_regs_per_interval"], max_regs_per_block=decoded["max_regs_per_block"], @@ -248,74 +264,108 @@ def _from_dict(cls, decoded: dict) -> "MetagraphInfo": commit_reveal_period=decoded["commit_reveal_period"], # Bonds liquid_alpha_enabled=decoded["liquid_alpha_enabled"], - alpha_high=u16tf(decoded["alpha_high"]) - if decoded["alpha_high"] is not None - else None, - alpha_low=u16tf(decoded["alpha_low"]) - if decoded["alpha_low"] is not None - else None, - bonds_moving_avg=u64tf(decoded["bonds_moving_avg"]) - if decoded["bonds_moving_avg"] is not None - else None, + alpha_high=( + u16tf(decoded["alpha_high"]) + if decoded["alpha_high"] is not None + else None + ), + alpha_low=( + u16tf(decoded["alpha_low"]) + if decoded["alpha_low"] is not None + else None + ), + bonds_moving_avg=( + u64tf(decoded["bonds_moving_avg"]) + if decoded["bonds_moving_avg"] is not None + else None + ), # Metagraph info. - hotkeys=[decode_account_id(ck) for ck in decoded.get("hotkeys", [])] - if decoded.get("hotkeys") is not None - else None, - coldkeys=[decode_account_id(hk) for hk in decoded.get("coldkeys", [])] - if decoded.get("coldkeys") is not None - else None, + hotkeys=( + [decode_account_id(ck) for ck in decoded.get("hotkeys", [])] + if decoded.get("hotkeys") is not None + else None + ), + coldkeys=( + [decode_account_id(hk) for hk in decoded.get("coldkeys", [])] + if decoded.get("coldkeys") is not None + else None + ), identities=decoded["identities"], axons=decoded.get("axons", []), active=decoded["active"], validator_permit=decoded["validator_permit"], - pruning_score=[u16tf(ps) for ps in decoded.get("pruning_score", [])] - if decoded.get("pruning_score") is not None - else None, + pruning_score=( + [u16tf(ps) for ps in decoded.get("pruning_score", [])] + if decoded.get("pruning_score") is not None + else None + ), last_update=decoded["last_update"], - emission=[_tbwu(em, _netuid) for em in decoded.get("emission", [])] - if decoded.get("emission") is not None - else None, - dividends=[u16tf(dv) for dv in decoded.get("dividends", [])] - if decoded.get("dividends") is not None - else None, - incentives=[u16tf(ic) for ic in decoded.get("incentives", [])] - if decoded.get("incentives") is not None - else None, - consensus=[u16tf(cs) for cs in decoded.get("consensus", [])] - if decoded.get("consensus") is not None - else None, - trust=[u16tf(tr) for tr in decoded.get("trust", [])] - if decoded.get("trust") is not None - else None, - rank=[u16tf(rk) for rk in decoded.get("rank", [])] - if decoded.get("rank") is not None - else None, + emission=( + [_tbwu(em, _netuid) for em in decoded.get("emission", [])] + if decoded.get("emission") is not None + else None + ), + dividends=( + [u16tf(dv) for dv in decoded.get("dividends", [])] + if decoded.get("dividends") is not None + else None + ), + incentives=( + [u16tf(ic) for ic in decoded.get("incentives", [])] + if decoded.get("incentives") is not None + else None + ), + consensus=( + [u16tf(cs) for cs in decoded.get("consensus", [])] + if decoded.get("consensus") is not None + else None + ), + trust=( + [u16tf(tr) for tr in decoded.get("trust", [])] + if decoded.get("trust") is not None + else None + ), + rank=( + [u16tf(rk) for rk in decoded.get("rank", [])] + if decoded.get("rank") is not None + else None + ), block_at_registration=decoded["block_at_registration"], - alpha_stake=[_tbwu(ast, _netuid) for ast in decoded["alpha_stake"]] - if decoded.get("alpha_stake") is not None - else None, - tao_stake=[ - _tbwu(ts) * settings.ROOT_TAO_STAKE_WEIGHT - for ts in decoded["tao_stake"] - ] - if decoded.get("tao_stake") is not None - else None, - total_stake=[_tbwu(ts, _netuid) for ts in decoded["total_stake"]] - if decoded.get("total_stake") is not None - else None, + alpha_stake=( + [_tbwu(ast, _netuid) for ast in decoded["alpha_stake"]] + if decoded.get("alpha_stake") is not None + else None + ), + tao_stake=( + [ + _tbwu(ts) * settings.ROOT_TAO_STAKE_WEIGHT + for ts in decoded["tao_stake"] + ] + if decoded.get("tao_stake") is not None + else None + ), + total_stake=( + [_tbwu(ts, _netuid) for ts in decoded["total_stake"]] + if decoded.get("total_stake") is not None + else None + ), # Dividend break down - tao_dividends_per_hotkey=[ - (decode_account_id(alpha[0]), _tbwu(alpha[1])) - for alpha in decoded["tao_dividends_per_hotkey"] - ] - if decoded.get("tao_dividends_per_hotkey") is not None - else None, - alpha_dividends_per_hotkey=[ - (decode_account_id(adphk[0]), _tbwu(adphk[1], _netuid)) - for adphk in decoded["alpha_dividends_per_hotkey"] - ] - if decoded.get("alpha_dividends_per_hotkey") is not None - else None, + tao_dividends_per_hotkey=( + [ + (decode_account_id(alpha[0]), _tbwu(alpha[1])) + for alpha in decoded["tao_dividends_per_hotkey"] + ] + if decoded.get("tao_dividends_per_hotkey") is not None + else None + ), + alpha_dividends_per_hotkey=( + [ + (decode_account_id(adphk[0]), _tbwu(adphk[1], _netuid)) + for adphk in decoded["alpha_dividends_per_hotkey"] + ] + if decoded.get("alpha_dividends_per_hotkey") is not None + else None + ), ) From caed73368b1560865c4c6f67b783ec4e6a519b88 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 24 Apr 2025 12:27:28 -0700 Subject: [PATCH 041/251] field_indices name --- bittensor/core/async_subtensor.py | 16 ++++++++-------- bittensor/core/subtensor.py | 16 ++++++++-------- tests/e2e_tests/test_metagraph.py | 4 ++-- tests/unit_tests/test_async_subtensor.py | 8 ++++---- tests/unit_tests/test_subtensor.py | 8 ++++---- 5 files changed, 26 insertions(+), 26 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 7a7db978cc..8426bffcd4 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -1471,7 +1471,7 @@ async def get_minimum_required_stake(self): async def get_metagraph_info( self, netuid: int, - field_indexes: Optional[list["SelectiveMetagraphIndex"]] = None, + field_indices: Optional[list["SelectiveMetagraphIndex"]] = None, block: Optional[int] = None, block_hash: Optional[str] = None, reuse_block: bool = False, @@ -1481,7 +1481,7 @@ async def get_metagraph_info( Arguments: netuid (int): The NetUID of the subnet to query. - field_indexes (Optional[list[SelectiveMetagraphIndex]]): An optional list of SelectiveMetagraphIndex values + field_indices (Optional[list[SelectiveMetagraphIndex]]): An optional list of SelectiveMetagraphIndex values specifying which fields to retrieve. If not provided, all available fields will be returned. block: the block number at which to retrieve the hyperparameter. Do not specify if using block_hash or reuse_block @@ -1498,19 +1498,19 @@ async def get_metagraph_info( partial_meta_info = subtensor.get_metagraph_info( netuid=2, - field_indexes=[SelectiveMetagraphIndex.Name, SelectiveMetagraphIndex.OwnerHotkeys] + field_indices=[SelectiveMetagraphIndex.Name, SelectiveMetagraphIndex.OwnerHotkeys] ) """ indexes = SelectiveMetagraphIndex.all_indices() - if field_indexes: - if isinstance(field_indexes, list) and all( - isinstance(f, SelectiveMetagraphIndex) for f in field_indexes + if field_indices: + if isinstance(field_indices, list) and all( + isinstance(f, SelectiveMetagraphIndex) for f in field_indices ): - indexes = [f.value for f in field_indexes] + indexes = [f.value for f in field_indices] else: raise ValueError( - "`field_indexes` must be a list of SelectiveMetagraphIndex items." + "`field_indices` must be a list of SelectiveMetagraphIndex items." ) block_hash = await self.determine_block_hash(block, block_hash, reuse_block) diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 71a35d7ca9..9fd500c16e 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -1133,7 +1133,7 @@ def get_minimum_required_stake(self) -> Balance: def get_metagraph_info( self, netuid: int, - field_indexes: Optional[list["SelectiveMetagraphIndex"]] = None, + field_indices: Optional[list["SelectiveMetagraphIndex"]] = None, block: Optional[int] = None, ) -> Optional[MetagraphInfo]: """ @@ -1141,7 +1141,7 @@ def get_metagraph_info( Arguments: netuid (int): The NetUID of the subnet to query. - field_indexes (Optional[list[SelectiveMetagraphIndex]]): An optional list of SelectiveMetagraphIndex values + field_indices (Optional[list[SelectiveMetagraphIndex]]): An optional list of SelectiveMetagraphIndex values specifying which fields to retrieve. If not provided, all available fields will be returned. block (Optional[int]):The block number at which to query the data. If not specified, the current block or one determined via reuse_block or block_hash will be used. @@ -1155,19 +1155,19 @@ def get_metagraph_info( partial_meta_info = subtensor.get_metagraph_info( netuid=2, - field_indexes=[SelectiveMetagraphIndex.Name, SelectiveMetagraphIndex.OwnerHotkeys] + field_indices=[SelectiveMetagraphIndex.Name, SelectiveMetagraphIndex.OwnerHotkeys] ) """ indexes = SelectiveMetagraphIndex.all_indices() - if field_indexes: - if isinstance(field_indexes, list) and all( - isinstance(f, SelectiveMetagraphIndex) for f in field_indexes + if field_indices: + if isinstance(field_indices, list) and all( + isinstance(f, SelectiveMetagraphIndex) for f in field_indices ): - indexes = [f.value for f in field_indexes] + indexes = [f.value for f in field_indices] else: raise ValueError( - "`field_indexes` must be a list of SelectiveMetagraphIndex items." + "`field_indices` must be a list of SelectiveMetagraphIndex items." ) block_hash = self.determine_block_hash(block) diff --git a/tests/e2e_tests/test_metagraph.py b/tests/e2e_tests/test_metagraph.py index 2f8cab751a..104c2ffceb 100644 --- a/tests/e2e_tests/test_metagraph.py +++ b/tests/e2e_tests/test_metagraph.py @@ -456,7 +456,7 @@ def test_metagraph_info_with_indexes(subtensor, alice_wallet, bob_wallet): ] metagraph_info = subtensor.get_metagraph_info( - netuid=alice_subnet_netuid, field_indexes=field_indices + netuid=alice_subnet_netuid, field_indices=field_indices ) assert metagraph_info == MetagraphInfo( @@ -563,7 +563,7 @@ def test_metagraph_info_with_indexes(subtensor, alice_wallet, bob_wallet): ] metagraph_info = subtensor.get_metagraph_info( - netuid=alice_subnet_netuid, field_indexes=fields + netuid=alice_subnet_netuid, field_indices=fields ) assert metagraph_info == MetagraphInfo( diff --git a/tests/unit_tests/test_async_subtensor.py b/tests/unit_tests/test_async_subtensor.py index 6d16858761..07d3c4e98c 100644 --- a/tests/unit_tests/test_async_subtensor.py +++ b/tests/unit_tests/test_async_subtensor.py @@ -3112,7 +3112,7 @@ async def test_get_metagraph_info_specific_fields(subtensor, mocker): ) # Call - result = await subtensor.get_metagraph_info(netuid=netuid, field_indexes=fields) + result = await subtensor.get_metagraph_info(netuid=netuid, field_indices=fields) # Asserts assert result == "parsed_metagraph" @@ -3137,12 +3137,12 @@ async def test_get_metagraph_info_specific_fields(subtensor, mocker): ) @pytest.mark.asyncio async def test_get_metagraph_info_invalid_field_indices(subtensor, wrong_fields): - """Test get_metagraph_info raises ValueError on invalid field_indexes.""" + """Test get_metagraph_info raises ValueError on invalid field_indices.""" with pytest.raises( ValueError, - match="`field_indexes` must be a list of SelectiveMetagraphIndex items.", + match="`field_indices` must be a list of SelectiveMetagraphIndex items.", ): - await subtensor.get_metagraph_info(netuid=1, field_indexes=wrong_fields) + await subtensor.get_metagraph_info(netuid=1, field_indices=wrong_fields) @pytest.mark.asyncio diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index 349d345da0..0d5f418170 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -3440,7 +3440,7 @@ def test_get_metagraph_info_specific_fields(subtensor, mocker): ) # Call - result = subtensor.get_metagraph_info(netuid=netuid, field_indexes=fields) + result = subtensor.get_metagraph_info(netuid=netuid, field_indices=fields) # Asserts assert result == "parsed_metagraph" @@ -3464,12 +3464,12 @@ def test_get_metagraph_info_specific_fields(subtensor, mocker): ], ) def test_get_metagraph_info_invalid_field_indices(subtensor, wrong_fields): - """Test get_metagraph_info raises ValueError on invalid field_indexes.""" + """Test get_metagraph_info raises ValueError on invalid field_indices.""" with pytest.raises( ValueError, - match="`field_indexes` must be a list of SelectiveMetagraphIndex items.", + match="`field_indices` must be a list of SelectiveMetagraphIndex items.", ): - subtensor.get_metagraph_info(netuid=1, field_indexes=wrong_fields) + subtensor.get_metagraph_info(netuid=1, field_indices=wrong_fields) def test_get_metagraph_info_subnet_not_exist(subtensor, mocker): From e0134a31fd4244a7694583163ee10cfa3d2ee78a Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 24 Apr 2025 12:56:52 -0700 Subject: [PATCH 042/251] add one more attempt --- .github/workflows/e2e-subtensor-tests.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/e2e-subtensor-tests.yaml b/.github/workflows/e2e-subtensor-tests.yaml index 08e8c2606f..81576ffeef 100644 --- a/.github/workflows/e2e-subtensor-tests.yaml +++ b/.github/workflows/e2e-subtensor-tests.yaml @@ -107,7 +107,7 @@ jobs: - name: Run tests with retry run: | set +e - for i in 1 2; do + for i in 1 2 3; do echo "🔁 Attempt $i: Running tests" uv run pytest ${{ matrix.test-file }} -s status=$? @@ -116,8 +116,8 @@ jobs: break else echo "❌ Tests failed on attempt $i" - if [ $i -eq 2 ]; then - echo "Tests failed after 2 attempts" + if [ $i -eq 3 ]; then + echo "Tests failed after 3 attempts" exit 1 fi echo "Retrying..." From 49637640349b621f0784ca9149d710e61df1040f Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 24 Apr 2025 14:33:31 -0700 Subject: [PATCH 043/251] single test + logging --- .github/workflows/e2e-subtensor-tests.yaml | 3 ++- tests/e2e_tests/test_staking.py | 15 ++++++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/.github/workflows/e2e-subtensor-tests.yaml b/.github/workflows/e2e-subtensor-tests.yaml index 81576ffeef..2f62fab24e 100644 --- a/.github/workflows/e2e-subtensor-tests.yaml +++ b/.github/workflows/e2e-subtensor-tests.yaml @@ -38,9 +38,10 @@ jobs: - name: Find test files id: get-tests run: | - test_files=$(find tests/e2e_tests -name "test*.py" | jq -R -s -c 'split("\n") | map(select(. != ""))') + # test_files=$(find tests/e2e_tests -name "test*.py" | jq -R -s -c 'split("\n") | map(select(. != ""))') # keep it here for future debug # test_files=$(find tests/e2e_tests -type f -name "test*.py" | grep -E 'test_(incentive|commit_weights|set_weights)\.py$' | jq -R -s -c 'split("\n") | map(select(. != ""))') + test_files=$(find tests/e2e_tests -type f -name "test*.py" | grep -E 'test_staking.py$' | jq -R -s -c 'split("\n") | map(select(. != ""))') echo "test-files=$test_files" >> "$GITHUB_OUTPUT" shell: bash diff --git a/tests/e2e_tests/test_staking.py b/tests/e2e_tests/test_staking.py index 55afa58beb..ae687ccbc8 100644 --- a/tests/e2e_tests/test_staking.py +++ b/tests/e2e_tests/test_staking.py @@ -354,7 +354,7 @@ def test_safe_staking_scenarios(subtensor, alice_wallet, bob_wallet): wait_for_inclusion=True, wait_for_finalization=True, safe_staking=True, - rate_tolerance=0.005, # 0.5% + rate_tolerance=0.001, # 0.5% allow_partial_stake=False, ) assert success is False @@ -375,7 +375,7 @@ def test_safe_staking_scenarios(subtensor, alice_wallet, bob_wallet): wait_for_inclusion=True, wait_for_finalization=True, safe_staking=True, - rate_tolerance=0.005, # 0.5% + rate_tolerance=0.001, # 0.5% allow_partial_stake=True, ) assert success is True @@ -424,13 +424,17 @@ def test_safe_staking_scenarios(subtensor, alice_wallet, bob_wallet): rate_tolerance=0.005, # 0.5% allow_partial_stake=False, ) - assert success is False + assert success is False, "Unstake should fail." current_stake = subtensor.get_stake( alice_wallet.coldkey.ss58_address, bob_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid, ) + + logging.console.info(f"[orange]Current stake: {current_stake}[orange]") + logging.console.info(f"[orange]Full stake: {full_stake}[orange]") + assert current_stake == full_stake, ( "Stake should not change after failed unstake attempt" ) @@ -444,7 +448,7 @@ def test_safe_staking_scenarios(subtensor, alice_wallet, bob_wallet): wait_for_inclusion=True, wait_for_finalization=True, safe_staking=True, - rate_tolerance=0.005, # 0.5% + rate_tolerance=0.001, # 0.5% allow_partial_stake=True, ) assert success is True @@ -454,6 +458,7 @@ def test_safe_staking_scenarios(subtensor, alice_wallet, bob_wallet): bob_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid, ) + logging.console.info(f"[orange]Partial unstake: {partial_unstake}[orange]") assert partial_unstake > Balance(0), "Some stake should remain" # 3. Higher threshold - should succeed fully @@ -468,7 +473,7 @@ def test_safe_staking_scenarios(subtensor, alice_wallet, bob_wallet): rate_tolerance=0.3, # 30% allow_partial_stake=False, ) - assert success is True + assert success is True, "Unstake should succeed" def test_safe_swap_stake_scenarios(subtensor, alice_wallet, bob_wallet): From 120e64458b659f611b3142b3f0449580c529d0ed Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 24 Apr 2025 14:46:06 -0700 Subject: [PATCH 044/251] just one test --- tests/e2e_tests/test_staking.py | 1204 +++++++++++++++---------------- 1 file changed, 602 insertions(+), 602 deletions(-) diff --git a/tests/e2e_tests/test_staking.py b/tests/e2e_tests/test_staking.py index ae687ccbc8..f0673c7532 100644 --- a/tests/e2e_tests/test_staking.py +++ b/tests/e2e_tests/test_staking.py @@ -8,298 +8,298 @@ logging.enable_info() -def test_single_operation(subtensor, alice_wallet, bob_wallet): - """ - Tests: - - Staking using `add_stake` - - Unstaking using `unstake` - - Checks StakeInfo - """ - alice_subnet_netuid = subtensor.get_total_subnets() # 2 - - # Register root as Alice - the subnet owner and validator - assert subtensor.register_subnet(alice_wallet, True, True) - - # Verify subnet created successfully - assert subtensor.subnet_exists(alice_subnet_netuid), ( - "Subnet wasn't created successfully" - ) - - assert wait_to_start_call(subtensor, alice_wallet, alice_subnet_netuid) - - subtensor.burned_register( - alice_wallet, - netuid=alice_subnet_netuid, - wait_for_inclusion=True, - wait_for_finalization=True, - ) - subtensor.burned_register( - bob_wallet, - netuid=alice_subnet_netuid, - wait_for_inclusion=True, - wait_for_finalization=True, - ) - - stake = subtensor.get_stake( - alice_wallet.coldkey.ss58_address, - bob_wallet.hotkey.ss58_address, - netuid=alice_subnet_netuid, - ) - - assert stake == Balance(0).set_unit(alice_subnet_netuid) - - success = subtensor.add_stake( - alice_wallet, - bob_wallet.hotkey.ss58_address, - netuid=alice_subnet_netuid, - amount=Balance.from_tao(1), - wait_for_inclusion=True, - wait_for_finalization=True, - ) - - assert success is True - - stake_alice = subtensor.get_stake( - alice_wallet.coldkey.ss58_address, - alice_wallet.hotkey.ss58_address, - netuid=alice_subnet_netuid, - ) - - stake_bob = subtensor.get_stake( - alice_wallet.coldkey.ss58_address, - bob_wallet.hotkey.ss58_address, - netuid=alice_subnet_netuid, - ) - - assert stake_bob > Balance(0).set_unit(alice_subnet_netuid) - - stakes = subtensor.get_stake_for_coldkey(alice_wallet.coldkey.ss58_address) - - assert stakes == [ - StakeInfo( - hotkey_ss58=alice_wallet.hotkey.ss58_address, - coldkey_ss58=alice_wallet.coldkey.ss58_address, - netuid=alice_subnet_netuid, - stake=get_dynamic_balance(stakes[0].stake.rao, alice_subnet_netuid), - locked=Balance(0).set_unit(alice_subnet_netuid), - emission=get_dynamic_balance(stakes[0].emission.rao, alice_subnet_netuid), - drain=0, - is_registered=True, - ), - StakeInfo( - hotkey_ss58=bob_wallet.hotkey.ss58_address, - coldkey_ss58=alice_wallet.coldkey.ss58_address, - netuid=alice_subnet_netuid, - stake=get_dynamic_balance(stakes[1].stake.rao, alice_subnet_netuid), - locked=Balance(0).set_unit(alice_subnet_netuid), - emission=get_dynamic_balance(stakes[1].emission.rao, alice_subnet_netuid), - drain=0, - is_registered=True, - ), - ] - - stakes = subtensor.get_stake_info_for_coldkey(alice_wallet.coldkey.ss58_address) - - assert stakes == [ - StakeInfo( - hotkey_ss58=alice_wallet.hotkey.ss58_address, - coldkey_ss58=alice_wallet.coldkey.ss58_address, - netuid=alice_subnet_netuid, - stake=get_dynamic_balance(stakes[0].stake.rao, alice_subnet_netuid), - locked=Balance(0).set_unit(alice_subnet_netuid), - emission=get_dynamic_balance(stakes[0].emission.rao, alice_subnet_netuid), - drain=0, - is_registered=True, - ), - StakeInfo( - hotkey_ss58=bob_wallet.hotkey.ss58_address, - coldkey_ss58=alice_wallet.coldkey.ss58_address, - netuid=alice_subnet_netuid, - stake=get_dynamic_balance(stakes[1].stake.rao, alice_subnet_netuid), - locked=Balance(0).set_unit(alice_subnet_netuid), - emission=get_dynamic_balance(stakes[1].emission.rao, alice_subnet_netuid), - 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.set_unit(1), - locked=Balance.from_tao(0, netuid=1), - emission=Balance.from_tao(0, netuid=1), - drain=0, - is_registered=False, - ), - 2: StakeInfo( - hotkey_ss58=bob_wallet.hotkey.ss58_address, - coldkey_ss58=alice_wallet.coldkey.ss58_address, - netuid=alice_subnet_netuid, - stake=get_dynamic_balance(stakes[2].stake.rao, alice_subnet_netuid), - locked=Balance.from_tao(0, netuid=alice_subnet_netuid), - emission=get_dynamic_balance(stakes[2].emission.rao, alice_subnet_netuid), - drain=0, - is_registered=True, - ), - } - - success = subtensor.unstake( - alice_wallet, - bob_wallet.hotkey.ss58_address, - netuid=alice_subnet_netuid, - amount=stake_bob, - 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=alice_subnet_netuid, - ) - - assert stake == Balance(0).set_unit(alice_subnet_netuid) - - -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, - ) - - # make sure we passed start_call limit for both subnets - for netuid in netuids: - assert wait_to_start_call(subtensor, alice_wallet, netuid) - - 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: get_dynamic_balance( - balances[alice_wallet.coldkey.ss58_address].rao, 2 - ), - bob_wallet.coldkey.ss58_address: Balance.from_tao(999_998).set_unit(3), - } - - 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: ApproxBalance(alice_balance.rao), - 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: get_dynamic_balance( - balances[alice_wallet.coldkey.ss58_address].rao, 2 - ), - bob_wallet.coldkey.ss58_address: Balance.from_tao(999_998), - } - assert balances[alice_wallet.coldkey.ss58_address] > alice_balance +# def test_single_operation(subtensor, alice_wallet, bob_wallet): +# """ +# Tests: +# - Staking using `add_stake` +# - Unstaking using `unstake` +# - Checks StakeInfo +# """ +# alice_subnet_netuid = subtensor.get_total_subnets() # 2 +# +# # Register root as Alice - the subnet owner and validator +# assert subtensor.register_subnet(alice_wallet, True, True) +# +# # Verify subnet created successfully +# assert subtensor.subnet_exists(alice_subnet_netuid), ( +# "Subnet wasn't created successfully" +# ) +# +# assert wait_to_start_call(subtensor, alice_wallet, alice_subnet_netuid) +# +# subtensor.burned_register( +# alice_wallet, +# netuid=alice_subnet_netuid, +# wait_for_inclusion=True, +# wait_for_finalization=True, +# ) +# subtensor.burned_register( +# bob_wallet, +# netuid=alice_subnet_netuid, +# wait_for_inclusion=True, +# wait_for_finalization=True, +# ) +# +# stake = subtensor.get_stake( +# alice_wallet.coldkey.ss58_address, +# bob_wallet.hotkey.ss58_address, +# netuid=alice_subnet_netuid, +# ) +# +# assert stake == Balance(0).set_unit(alice_subnet_netuid) +# +# success = subtensor.add_stake( +# alice_wallet, +# bob_wallet.hotkey.ss58_address, +# netuid=alice_subnet_netuid, +# amount=Balance.from_tao(1), +# wait_for_inclusion=True, +# wait_for_finalization=True, +# ) +# +# assert success is True +# +# stake_alice = subtensor.get_stake( +# alice_wallet.coldkey.ss58_address, +# alice_wallet.hotkey.ss58_address, +# netuid=alice_subnet_netuid, +# ) +# +# stake_bob = subtensor.get_stake( +# alice_wallet.coldkey.ss58_address, +# bob_wallet.hotkey.ss58_address, +# netuid=alice_subnet_netuid, +# ) +# +# assert stake_bob > Balance(0).set_unit(alice_subnet_netuid) +# +# stakes = subtensor.get_stake_for_coldkey(alice_wallet.coldkey.ss58_address) +# +# assert stakes == [ +# StakeInfo( +# hotkey_ss58=alice_wallet.hotkey.ss58_address, +# coldkey_ss58=alice_wallet.coldkey.ss58_address, +# netuid=alice_subnet_netuid, +# stake=get_dynamic_balance(stakes[0].stake.rao, alice_subnet_netuid), +# locked=Balance(0).set_unit(alice_subnet_netuid), +# emission=get_dynamic_balance(stakes[0].emission.rao, alice_subnet_netuid), +# drain=0, +# is_registered=True, +# ), +# StakeInfo( +# hotkey_ss58=bob_wallet.hotkey.ss58_address, +# coldkey_ss58=alice_wallet.coldkey.ss58_address, +# netuid=alice_subnet_netuid, +# stake=get_dynamic_balance(stakes[1].stake.rao, alice_subnet_netuid), +# locked=Balance(0).set_unit(alice_subnet_netuid), +# emission=get_dynamic_balance(stakes[1].emission.rao, alice_subnet_netuid), +# drain=0, +# is_registered=True, +# ), +# ] +# +# stakes = subtensor.get_stake_info_for_coldkey(alice_wallet.coldkey.ss58_address) +# +# assert stakes == [ +# StakeInfo( +# hotkey_ss58=alice_wallet.hotkey.ss58_address, +# coldkey_ss58=alice_wallet.coldkey.ss58_address, +# netuid=alice_subnet_netuid, +# stake=get_dynamic_balance(stakes[0].stake.rao, alice_subnet_netuid), +# locked=Balance(0).set_unit(alice_subnet_netuid), +# emission=get_dynamic_balance(stakes[0].emission.rao, alice_subnet_netuid), +# drain=0, +# is_registered=True, +# ), +# StakeInfo( +# hotkey_ss58=bob_wallet.hotkey.ss58_address, +# coldkey_ss58=alice_wallet.coldkey.ss58_address, +# netuid=alice_subnet_netuid, +# stake=get_dynamic_balance(stakes[1].stake.rao, alice_subnet_netuid), +# locked=Balance(0).set_unit(alice_subnet_netuid), +# emission=get_dynamic_balance(stakes[1].emission.rao, alice_subnet_netuid), +# 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.set_unit(1), +# locked=Balance.from_tao(0, netuid=1), +# emission=Balance.from_tao(0, netuid=1), +# drain=0, +# is_registered=False, +# ), +# 2: StakeInfo( +# hotkey_ss58=bob_wallet.hotkey.ss58_address, +# coldkey_ss58=alice_wallet.coldkey.ss58_address, +# netuid=alice_subnet_netuid, +# stake=get_dynamic_balance(stakes[2].stake.rao, alice_subnet_netuid), +# locked=Balance.from_tao(0, netuid=alice_subnet_netuid), +# emission=get_dynamic_balance(stakes[2].emission.rao, alice_subnet_netuid), +# drain=0, +# is_registered=True, +# ), +# } +# +# success = subtensor.unstake( +# alice_wallet, +# bob_wallet.hotkey.ss58_address, +# netuid=alice_subnet_netuid, +# amount=stake_bob, +# 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=alice_subnet_netuid, +# ) +# +# assert stake == Balance(0).set_unit(alice_subnet_netuid) +# +# +# 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, +# ) +# +# # make sure we passed start_call limit for both subnets +# for netuid in netuids: +# assert wait_to_start_call(subtensor, alice_wallet, netuid) +# +# 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: get_dynamic_balance( +# balances[alice_wallet.coldkey.ss58_address].rao, 2 +# ), +# bob_wallet.coldkey.ss58_address: Balance.from_tao(999_998).set_unit(3), +# } +# +# 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: ApproxBalance(alice_balance.rao), +# 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: get_dynamic_balance( +# balances[alice_wallet.coldkey.ss58_address].rao, 2 +# ), +# bob_wallet.coldkey.ss58_address: Balance.from_tao(999_998), +# } +# assert balances[alice_wallet.coldkey.ss58_address] > alice_balance def test_safe_staking_scenarios(subtensor, alice_wallet, bob_wallet): @@ -476,313 +476,313 @@ def test_safe_staking_scenarios(subtensor, alice_wallet, bob_wallet): assert success is True, "Unstake should succeed" -def test_safe_swap_stake_scenarios(subtensor, alice_wallet, bob_wallet): - """ - Tests safe swap stake scenarios with different parameters. - - Tests: - 1. Fails with strict threshold (0.5%) - 2. Succeeds with lenient threshold (10%) - """ - # Create new subnet (netuid 2) and register Alice - origin_netuid = 2 - assert subtensor.register_subnet(bob_wallet, True, True) - assert subtensor.subnet_exists(origin_netuid), "Subnet wasn't created successfully" - dest_netuid = 3 - assert subtensor.register_subnet(bob_wallet, True, True) - assert subtensor.subnet_exists(dest_netuid), "Subnet wasn't created successfully" - - # make sure we passed start_call limit for both subnets - assert wait_to_start_call(subtensor, bob_wallet, origin_netuid) - assert wait_to_start_call(subtensor, bob_wallet, dest_netuid) - - # Register Alice on both subnets - subtensor.burned_register( - alice_wallet, - netuid=origin_netuid, - wait_for_inclusion=True, - wait_for_finalization=True, - ) - subtensor.burned_register( - alice_wallet, - netuid=dest_netuid, - wait_for_inclusion=True, - wait_for_finalization=True, - ) - - # Add initial stake to swap from - initial_stake_amount = Balance.from_tao(10_000) - success = subtensor.add_stake( - alice_wallet, - alice_wallet.hotkey.ss58_address, - netuid=origin_netuid, - amount=initial_stake_amount, - wait_for_inclusion=True, - wait_for_finalization=True, - ) - assert success is True - - origin_stake = subtensor.get_stake( - alice_wallet.coldkey.ss58_address, - alice_wallet.hotkey.ss58_address, - netuid=origin_netuid, - ) - assert origin_stake > Balance(0), "Origin stake should be non-zero" - - stake_swap_amount = Balance.from_tao(10_000) - # 1. Try swap with strict threshold and big amount- should fail - success = subtensor.swap_stake( - wallet=alice_wallet, - hotkey_ss58=alice_wallet.hotkey.ss58_address, - origin_netuid=origin_netuid, - destination_netuid=dest_netuid, - amount=stake_swap_amount, - wait_for_inclusion=True, - wait_for_finalization=True, - safe_staking=True, - rate_tolerance=0.005, # 0.5% - allow_partial_stake=False, - ) - assert success is False - - # Verify no stake was moved - dest_stake = subtensor.get_stake( - alice_wallet.coldkey.ss58_address, - alice_wallet.hotkey.ss58_address, - netuid=dest_netuid, - ) - assert dest_stake == Balance(0), ( - "Destination stake should remain 0 after failed swap" - ) - - # 2. Try swap with higher threshold and less amount - should succeed - stake_swap_amount = Balance.from_tao(100) - success = subtensor.swap_stake( - wallet=alice_wallet, - hotkey_ss58=alice_wallet.hotkey.ss58_address, - origin_netuid=origin_netuid, - destination_netuid=dest_netuid, - amount=stake_swap_amount, - wait_for_inclusion=True, - wait_for_finalization=True, - safe_staking=True, - rate_tolerance=0.3, # 30% - allow_partial_stake=True, - ) - assert success is True - - # Verify stake was moved - origin_stake = subtensor.get_stake( - alice_wallet.coldkey.ss58_address, - alice_wallet.hotkey.ss58_address, - netuid=origin_netuid, - ) - dest_stake = subtensor.get_stake( - alice_wallet.coldkey.ss58_address, - alice_wallet.hotkey.ss58_address, - netuid=dest_netuid, - ) - assert dest_stake > Balance(0), ( - "Destination stake should be non-zero after successful swap" - ) - - -def test_move_stake(subtensor, alice_wallet, bob_wallet): - """ - Tests: - - Adding stake - - Moving stake from one hotkey-subnet pair to another - """ - - alice_subnet_netuid = subtensor.get_total_subnets() # 2 - assert subtensor.register_subnet(alice_wallet, True, True) - assert subtensor.subnet_exists(alice_subnet_netuid), ( - "Subnet wasn't created successfully" - ) - - assert wait_to_start_call(subtensor, alice_wallet, alice_subnet_netuid) - - subtensor.burned_register( - alice_wallet, - netuid=alice_subnet_netuid, - wait_for_inclusion=True, - wait_for_finalization=True, - ) - - assert subtensor.add_stake( - alice_wallet, - alice_wallet.hotkey.ss58_address, - netuid=alice_subnet_netuid, - amount=Balance.from_tao(1_000), - wait_for_inclusion=True, - wait_for_finalization=True, - ) - - stakes = subtensor.get_stake_for_coldkey(alice_wallet.coldkey.ss58_address) - - assert stakes == [ - StakeInfo( - hotkey_ss58=alice_wallet.hotkey.ss58_address, - coldkey_ss58=alice_wallet.coldkey.ss58_address, - netuid=alice_subnet_netuid, - stake=get_dynamic_balance(stakes[0].stake.rao, alice_subnet_netuid), - locked=Balance(0), - emission=get_dynamic_balance(stakes[0].emission.rao, alice_subnet_netuid), - drain=0, - is_registered=True, - ), - ] - - bob_subnet_netuid = subtensor.get_total_subnets() # 3 - subtensor.register_subnet(bob_wallet, True, True) - assert subtensor.subnet_exists(bob_subnet_netuid), ( - "Subnet wasn't created successfully" - ) - - assert wait_to_start_call(subtensor, bob_wallet, bob_subnet_netuid) - - assert subtensor.move_stake( - alice_wallet, - origin_hotkey=alice_wallet.hotkey.ss58_address, - origin_netuid=alice_subnet_netuid, - destination_hotkey=bob_wallet.hotkey.ss58_address, - destination_netuid=bob_subnet_netuid, - amount=stakes[0].stake, - wait_for_finalization=True, - wait_for_inclusion=True, - ) - - stakes = subtensor.get_stake_for_coldkey(alice_wallet.coldkey.ss58_address) - - assert stakes == [ - StakeInfo( - hotkey_ss58=alice_wallet.hotkey.ss58_address, - coldkey_ss58=alice_wallet.coldkey.ss58_address, - netuid=alice_subnet_netuid, - stake=get_dynamic_balance(stakes[0].stake.rao, bob_subnet_netuid), - locked=Balance(0).set_unit(bob_subnet_netuid), - emission=get_dynamic_balance(stakes[0].emission.rao, bob_subnet_netuid), - drain=0, - is_registered=True, - ), - StakeInfo( - hotkey_ss58=bob_wallet.hotkey.ss58_address, - coldkey_ss58=alice_wallet.coldkey.ss58_address, - netuid=bob_subnet_netuid, - stake=get_dynamic_balance(stakes[1].stake.rao, bob_subnet_netuid), - locked=Balance(0).set_unit(bob_subnet_netuid), - emission=get_dynamic_balance(stakes[1].emission.rao, bob_subnet_netuid), - drain=0, - is_registered=True, - ), - ] - - -def test_transfer_stake(subtensor, alice_wallet, bob_wallet, dave_wallet): - """ - Tests: - - Adding stake - - Transferring stake from one coldkey-subnet pair to another - """ - alice_subnet_netuid = subtensor.get_total_subnets() # 2 - - assert subtensor.register_subnet(alice_wallet, True, True) - assert subtensor.subnet_exists(alice_subnet_netuid), ( - "Subnet wasn't created successfully" - ) - - assert wait_to_start_call(subtensor, alice_wallet, alice_subnet_netuid) - - subtensor.burned_register( - alice_wallet, - netuid=alice_subnet_netuid, - wait_for_inclusion=True, - wait_for_finalization=True, - ) - - assert subtensor.add_stake( - alice_wallet, - alice_wallet.hotkey.ss58_address, - netuid=alice_subnet_netuid, - amount=Balance.from_tao(1_000), - wait_for_inclusion=True, - wait_for_finalization=True, - ) - - alice_stakes = subtensor.get_stake_for_coldkey(alice_wallet.coldkey.ss58_address) - - assert alice_stakes == [ - StakeInfo( - hotkey_ss58=alice_wallet.hotkey.ss58_address, - coldkey_ss58=alice_wallet.coldkey.ss58_address, - netuid=alice_subnet_netuid, - stake=get_dynamic_balance(alice_stakes[0].stake.rao, alice_subnet_netuid), - locked=Balance(0), - emission=get_dynamic_balance( - alice_stakes[0].emission.rao, alice_subnet_netuid - ), - drain=0, - is_registered=True, - ), - ] - - bob_stakes = subtensor.get_stake_for_coldkey(bob_wallet.coldkey.ss58_address) - - assert bob_stakes == [] - - dave_subnet_netuid = subtensor.get_total_subnets() # 3 - subtensor.register_subnet(dave_wallet, True, True) - - assert wait_to_start_call(subtensor, dave_wallet, dave_subnet_netuid) - - subtensor.burned_register( - bob_wallet, - netuid=dave_subnet_netuid, - wait_for_inclusion=True, - wait_for_finalization=True, - ) - - assert subtensor.transfer_stake( - alice_wallet, - destination_coldkey_ss58=bob_wallet.coldkey.ss58_address, - hotkey_ss58=alice_wallet.hotkey.ss58_address, - origin_netuid=alice_subnet_netuid, - destination_netuid=dave_subnet_netuid, - amount=alice_stakes[0].stake, - wait_for_inclusion=True, - wait_for_finalization=True, - ) - - alice_stakes = subtensor.get_stake_for_coldkey(alice_wallet.coldkey.ss58_address) - - assert alice_stakes == [ - StakeInfo( - hotkey_ss58=alice_wallet.hotkey.ss58_address, - coldkey_ss58=alice_wallet.coldkey.ss58_address, - netuid=alice_subnet_netuid, - stake=get_dynamic_balance(alice_stakes[0].stake.rao, alice_subnet_netuid), - locked=Balance(0).set_unit(alice_subnet_netuid), - emission=get_dynamic_balance( - alice_stakes[0].emission.rao, alice_subnet_netuid - ), - drain=0, - is_registered=True, - ), - ] - - bob_stakes = subtensor.get_stake_for_coldkey(bob_wallet.coldkey.ss58_address) - - assert bob_stakes == [ - StakeInfo( - hotkey_ss58=alice_wallet.hotkey.ss58_address, - coldkey_ss58=bob_wallet.coldkey.ss58_address, - netuid=dave_subnet_netuid, - stake=get_dynamic_balance(bob_stakes[0].stake.rao, dave_subnet_netuid), - locked=Balance(0), - emission=get_dynamic_balance( - bob_stakes[0].emission.rao, dave_subnet_netuid - ), - drain=0, - is_registered=False, - ), - ] +# def test_safe_swap_stake_scenarios(subtensor, alice_wallet, bob_wallet): +# """ +# Tests safe swap stake scenarios with different parameters. +# +# Tests: +# 1. Fails with strict threshold (0.5%) +# 2. Succeeds with lenient threshold (10%) +# """ +# # Create new subnet (netuid 2) and register Alice +# origin_netuid = 2 +# assert subtensor.register_subnet(bob_wallet, True, True) +# assert subtensor.subnet_exists(origin_netuid), "Subnet wasn't created successfully" +# dest_netuid = 3 +# assert subtensor.register_subnet(bob_wallet, True, True) +# assert subtensor.subnet_exists(dest_netuid), "Subnet wasn't created successfully" +# +# # make sure we passed start_call limit for both subnets +# assert wait_to_start_call(subtensor, bob_wallet, origin_netuid) +# assert wait_to_start_call(subtensor, bob_wallet, dest_netuid) +# +# # Register Alice on both subnets +# subtensor.burned_register( +# alice_wallet, +# netuid=origin_netuid, +# wait_for_inclusion=True, +# wait_for_finalization=True, +# ) +# subtensor.burned_register( +# alice_wallet, +# netuid=dest_netuid, +# wait_for_inclusion=True, +# wait_for_finalization=True, +# ) +# +# # Add initial stake to swap from +# initial_stake_amount = Balance.from_tao(10_000) +# success = subtensor.add_stake( +# alice_wallet, +# alice_wallet.hotkey.ss58_address, +# netuid=origin_netuid, +# amount=initial_stake_amount, +# wait_for_inclusion=True, +# wait_for_finalization=True, +# ) +# assert success is True +# +# origin_stake = subtensor.get_stake( +# alice_wallet.coldkey.ss58_address, +# alice_wallet.hotkey.ss58_address, +# netuid=origin_netuid, +# ) +# assert origin_stake > Balance(0), "Origin stake should be non-zero" +# +# stake_swap_amount = Balance.from_tao(10_000) +# # 1. Try swap with strict threshold and big amount- should fail +# success = subtensor.swap_stake( +# wallet=alice_wallet, +# hotkey_ss58=alice_wallet.hotkey.ss58_address, +# origin_netuid=origin_netuid, +# destination_netuid=dest_netuid, +# amount=stake_swap_amount, +# wait_for_inclusion=True, +# wait_for_finalization=True, +# safe_staking=True, +# rate_tolerance=0.005, # 0.5% +# allow_partial_stake=False, +# ) +# assert success is False +# +# # Verify no stake was moved +# dest_stake = subtensor.get_stake( +# alice_wallet.coldkey.ss58_address, +# alice_wallet.hotkey.ss58_address, +# netuid=dest_netuid, +# ) +# assert dest_stake == Balance(0), ( +# "Destination stake should remain 0 after failed swap" +# ) +# +# # 2. Try swap with higher threshold and less amount - should succeed +# stake_swap_amount = Balance.from_tao(100) +# success = subtensor.swap_stake( +# wallet=alice_wallet, +# hotkey_ss58=alice_wallet.hotkey.ss58_address, +# origin_netuid=origin_netuid, +# destination_netuid=dest_netuid, +# amount=stake_swap_amount, +# wait_for_inclusion=True, +# wait_for_finalization=True, +# safe_staking=True, +# rate_tolerance=0.3, # 30% +# allow_partial_stake=True, +# ) +# assert success is True +# +# # Verify stake was moved +# origin_stake = subtensor.get_stake( +# alice_wallet.coldkey.ss58_address, +# alice_wallet.hotkey.ss58_address, +# netuid=origin_netuid, +# ) +# dest_stake = subtensor.get_stake( +# alice_wallet.coldkey.ss58_address, +# alice_wallet.hotkey.ss58_address, +# netuid=dest_netuid, +# ) +# assert dest_stake > Balance(0), ( +# "Destination stake should be non-zero after successful swap" +# ) +# +# +# def test_move_stake(subtensor, alice_wallet, bob_wallet): +# """ +# Tests: +# - Adding stake +# - Moving stake from one hotkey-subnet pair to another +# """ +# +# alice_subnet_netuid = subtensor.get_total_subnets() # 2 +# assert subtensor.register_subnet(alice_wallet, True, True) +# assert subtensor.subnet_exists(alice_subnet_netuid), ( +# "Subnet wasn't created successfully" +# ) +# +# assert wait_to_start_call(subtensor, alice_wallet, alice_subnet_netuid) +# +# subtensor.burned_register( +# alice_wallet, +# netuid=alice_subnet_netuid, +# wait_for_inclusion=True, +# wait_for_finalization=True, +# ) +# +# assert subtensor.add_stake( +# alice_wallet, +# alice_wallet.hotkey.ss58_address, +# netuid=alice_subnet_netuid, +# amount=Balance.from_tao(1_000), +# wait_for_inclusion=True, +# wait_for_finalization=True, +# ) +# +# stakes = subtensor.get_stake_for_coldkey(alice_wallet.coldkey.ss58_address) +# +# assert stakes == [ +# StakeInfo( +# hotkey_ss58=alice_wallet.hotkey.ss58_address, +# coldkey_ss58=alice_wallet.coldkey.ss58_address, +# netuid=alice_subnet_netuid, +# stake=get_dynamic_balance(stakes[0].stake.rao, alice_subnet_netuid), +# locked=Balance(0), +# emission=get_dynamic_balance(stakes[0].emission.rao, alice_subnet_netuid), +# drain=0, +# is_registered=True, +# ), +# ] +# +# bob_subnet_netuid = subtensor.get_total_subnets() # 3 +# subtensor.register_subnet(bob_wallet, True, True) +# assert subtensor.subnet_exists(bob_subnet_netuid), ( +# "Subnet wasn't created successfully" +# ) +# +# assert wait_to_start_call(subtensor, bob_wallet, bob_subnet_netuid) +# +# assert subtensor.move_stake( +# alice_wallet, +# origin_hotkey=alice_wallet.hotkey.ss58_address, +# origin_netuid=alice_subnet_netuid, +# destination_hotkey=bob_wallet.hotkey.ss58_address, +# destination_netuid=bob_subnet_netuid, +# amount=stakes[0].stake, +# wait_for_finalization=True, +# wait_for_inclusion=True, +# ) +# +# stakes = subtensor.get_stake_for_coldkey(alice_wallet.coldkey.ss58_address) +# +# assert stakes == [ +# StakeInfo( +# hotkey_ss58=alice_wallet.hotkey.ss58_address, +# coldkey_ss58=alice_wallet.coldkey.ss58_address, +# netuid=alice_subnet_netuid, +# stake=get_dynamic_balance(stakes[0].stake.rao, bob_subnet_netuid), +# locked=Balance(0).set_unit(bob_subnet_netuid), +# emission=get_dynamic_balance(stakes[0].emission.rao, bob_subnet_netuid), +# drain=0, +# is_registered=True, +# ), +# StakeInfo( +# hotkey_ss58=bob_wallet.hotkey.ss58_address, +# coldkey_ss58=alice_wallet.coldkey.ss58_address, +# netuid=bob_subnet_netuid, +# stake=get_dynamic_balance(stakes[1].stake.rao, bob_subnet_netuid), +# locked=Balance(0).set_unit(bob_subnet_netuid), +# emission=get_dynamic_balance(stakes[1].emission.rao, bob_subnet_netuid), +# drain=0, +# is_registered=True, +# ), +# ] +# +# +# def test_transfer_stake(subtensor, alice_wallet, bob_wallet, dave_wallet): +# """ +# Tests: +# - Adding stake +# - Transferring stake from one coldkey-subnet pair to another +# """ +# alice_subnet_netuid = subtensor.get_total_subnets() # 2 +# +# assert subtensor.register_subnet(alice_wallet, True, True) +# assert subtensor.subnet_exists(alice_subnet_netuid), ( +# "Subnet wasn't created successfully" +# ) +# +# assert wait_to_start_call(subtensor, alice_wallet, alice_subnet_netuid) +# +# subtensor.burned_register( +# alice_wallet, +# netuid=alice_subnet_netuid, +# wait_for_inclusion=True, +# wait_for_finalization=True, +# ) +# +# assert subtensor.add_stake( +# alice_wallet, +# alice_wallet.hotkey.ss58_address, +# netuid=alice_subnet_netuid, +# amount=Balance.from_tao(1_000), +# wait_for_inclusion=True, +# wait_for_finalization=True, +# ) +# +# alice_stakes = subtensor.get_stake_for_coldkey(alice_wallet.coldkey.ss58_address) +# +# assert alice_stakes == [ +# StakeInfo( +# hotkey_ss58=alice_wallet.hotkey.ss58_address, +# coldkey_ss58=alice_wallet.coldkey.ss58_address, +# netuid=alice_subnet_netuid, +# stake=get_dynamic_balance(alice_stakes[0].stake.rao, alice_subnet_netuid), +# locked=Balance(0), +# emission=get_dynamic_balance( +# alice_stakes[0].emission.rao, alice_subnet_netuid +# ), +# drain=0, +# is_registered=True, +# ), +# ] +# +# bob_stakes = subtensor.get_stake_for_coldkey(bob_wallet.coldkey.ss58_address) +# +# assert bob_stakes == [] +# +# dave_subnet_netuid = subtensor.get_total_subnets() # 3 +# subtensor.register_subnet(dave_wallet, True, True) +# +# assert wait_to_start_call(subtensor, dave_wallet, dave_subnet_netuid) +# +# subtensor.burned_register( +# bob_wallet, +# netuid=dave_subnet_netuid, +# wait_for_inclusion=True, +# wait_for_finalization=True, +# ) +# +# assert subtensor.transfer_stake( +# alice_wallet, +# destination_coldkey_ss58=bob_wallet.coldkey.ss58_address, +# hotkey_ss58=alice_wallet.hotkey.ss58_address, +# origin_netuid=alice_subnet_netuid, +# destination_netuid=dave_subnet_netuid, +# amount=alice_stakes[0].stake, +# wait_for_inclusion=True, +# wait_for_finalization=True, +# ) +# +# alice_stakes = subtensor.get_stake_for_coldkey(alice_wallet.coldkey.ss58_address) +# +# assert alice_stakes == [ +# StakeInfo( +# hotkey_ss58=alice_wallet.hotkey.ss58_address, +# coldkey_ss58=alice_wallet.coldkey.ss58_address, +# netuid=alice_subnet_netuid, +# stake=get_dynamic_balance(alice_stakes[0].stake.rao, alice_subnet_netuid), +# locked=Balance(0).set_unit(alice_subnet_netuid), +# emission=get_dynamic_balance( +# alice_stakes[0].emission.rao, alice_subnet_netuid +# ), +# drain=0, +# is_registered=True, +# ), +# ] +# +# bob_stakes = subtensor.get_stake_for_coldkey(bob_wallet.coldkey.ss58_address) +# +# assert bob_stakes == [ +# StakeInfo( +# hotkey_ss58=alice_wallet.hotkey.ss58_address, +# coldkey_ss58=bob_wallet.coldkey.ss58_address, +# netuid=dave_subnet_netuid, +# stake=get_dynamic_balance(bob_stakes[0].stake.rao, dave_subnet_netuid), +# locked=Balance(0), +# emission=get_dynamic_balance( +# bob_stakes[0].emission.rao, dave_subnet_netuid +# ), +# drain=0, +# is_registered=False, +# ), +# ] From 22378f36fb6d9f380bb438d898c6b509aceaa3c6 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 24 Apr 2025 14:57:23 -0700 Subject: [PATCH 045/251] left the comments for the future debug --- .github/workflows/e2e-subtensor-tests.yaml | 3 +- tests/e2e_tests/test_staking.py | 1204 ++++++++++---------- 2 files changed, 603 insertions(+), 604 deletions(-) diff --git a/.github/workflows/e2e-subtensor-tests.yaml b/.github/workflows/e2e-subtensor-tests.yaml index 2f62fab24e..81576ffeef 100644 --- a/.github/workflows/e2e-subtensor-tests.yaml +++ b/.github/workflows/e2e-subtensor-tests.yaml @@ -38,10 +38,9 @@ jobs: - name: Find test files id: get-tests run: | - # test_files=$(find tests/e2e_tests -name "test*.py" | jq -R -s -c 'split("\n") | map(select(. != ""))') + test_files=$(find tests/e2e_tests -name "test*.py" | jq -R -s -c 'split("\n") | map(select(. != ""))') # keep it here for future debug # test_files=$(find tests/e2e_tests -type f -name "test*.py" | grep -E 'test_(incentive|commit_weights|set_weights)\.py$' | jq -R -s -c 'split("\n") | map(select(. != ""))') - test_files=$(find tests/e2e_tests -type f -name "test*.py" | grep -E 'test_staking.py$' | jq -R -s -c 'split("\n") | map(select(. != ""))') echo "test-files=$test_files" >> "$GITHUB_OUTPUT" shell: bash diff --git a/tests/e2e_tests/test_staking.py b/tests/e2e_tests/test_staking.py index f0673c7532..ae687ccbc8 100644 --- a/tests/e2e_tests/test_staking.py +++ b/tests/e2e_tests/test_staking.py @@ -8,298 +8,298 @@ logging.enable_info() -# def test_single_operation(subtensor, alice_wallet, bob_wallet): -# """ -# Tests: -# - Staking using `add_stake` -# - Unstaking using `unstake` -# - Checks StakeInfo -# """ -# alice_subnet_netuid = subtensor.get_total_subnets() # 2 -# -# # Register root as Alice - the subnet owner and validator -# assert subtensor.register_subnet(alice_wallet, True, True) -# -# # Verify subnet created successfully -# assert subtensor.subnet_exists(alice_subnet_netuid), ( -# "Subnet wasn't created successfully" -# ) -# -# assert wait_to_start_call(subtensor, alice_wallet, alice_subnet_netuid) -# -# subtensor.burned_register( -# alice_wallet, -# netuid=alice_subnet_netuid, -# wait_for_inclusion=True, -# wait_for_finalization=True, -# ) -# subtensor.burned_register( -# bob_wallet, -# netuid=alice_subnet_netuid, -# wait_for_inclusion=True, -# wait_for_finalization=True, -# ) -# -# stake = subtensor.get_stake( -# alice_wallet.coldkey.ss58_address, -# bob_wallet.hotkey.ss58_address, -# netuid=alice_subnet_netuid, -# ) -# -# assert stake == Balance(0).set_unit(alice_subnet_netuid) -# -# success = subtensor.add_stake( -# alice_wallet, -# bob_wallet.hotkey.ss58_address, -# netuid=alice_subnet_netuid, -# amount=Balance.from_tao(1), -# wait_for_inclusion=True, -# wait_for_finalization=True, -# ) -# -# assert success is True -# -# stake_alice = subtensor.get_stake( -# alice_wallet.coldkey.ss58_address, -# alice_wallet.hotkey.ss58_address, -# netuid=alice_subnet_netuid, -# ) -# -# stake_bob = subtensor.get_stake( -# alice_wallet.coldkey.ss58_address, -# bob_wallet.hotkey.ss58_address, -# netuid=alice_subnet_netuid, -# ) -# -# assert stake_bob > Balance(0).set_unit(alice_subnet_netuid) -# -# stakes = subtensor.get_stake_for_coldkey(alice_wallet.coldkey.ss58_address) -# -# assert stakes == [ -# StakeInfo( -# hotkey_ss58=alice_wallet.hotkey.ss58_address, -# coldkey_ss58=alice_wallet.coldkey.ss58_address, -# netuid=alice_subnet_netuid, -# stake=get_dynamic_balance(stakes[0].stake.rao, alice_subnet_netuid), -# locked=Balance(0).set_unit(alice_subnet_netuid), -# emission=get_dynamic_balance(stakes[0].emission.rao, alice_subnet_netuid), -# drain=0, -# is_registered=True, -# ), -# StakeInfo( -# hotkey_ss58=bob_wallet.hotkey.ss58_address, -# coldkey_ss58=alice_wallet.coldkey.ss58_address, -# netuid=alice_subnet_netuid, -# stake=get_dynamic_balance(stakes[1].stake.rao, alice_subnet_netuid), -# locked=Balance(0).set_unit(alice_subnet_netuid), -# emission=get_dynamic_balance(stakes[1].emission.rao, alice_subnet_netuid), -# drain=0, -# is_registered=True, -# ), -# ] -# -# stakes = subtensor.get_stake_info_for_coldkey(alice_wallet.coldkey.ss58_address) -# -# assert stakes == [ -# StakeInfo( -# hotkey_ss58=alice_wallet.hotkey.ss58_address, -# coldkey_ss58=alice_wallet.coldkey.ss58_address, -# netuid=alice_subnet_netuid, -# stake=get_dynamic_balance(stakes[0].stake.rao, alice_subnet_netuid), -# locked=Balance(0).set_unit(alice_subnet_netuid), -# emission=get_dynamic_balance(stakes[0].emission.rao, alice_subnet_netuid), -# drain=0, -# is_registered=True, -# ), -# StakeInfo( -# hotkey_ss58=bob_wallet.hotkey.ss58_address, -# coldkey_ss58=alice_wallet.coldkey.ss58_address, -# netuid=alice_subnet_netuid, -# stake=get_dynamic_balance(stakes[1].stake.rao, alice_subnet_netuid), -# locked=Balance(0).set_unit(alice_subnet_netuid), -# emission=get_dynamic_balance(stakes[1].emission.rao, alice_subnet_netuid), -# 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.set_unit(1), -# locked=Balance.from_tao(0, netuid=1), -# emission=Balance.from_tao(0, netuid=1), -# drain=0, -# is_registered=False, -# ), -# 2: StakeInfo( -# hotkey_ss58=bob_wallet.hotkey.ss58_address, -# coldkey_ss58=alice_wallet.coldkey.ss58_address, -# netuid=alice_subnet_netuid, -# stake=get_dynamic_balance(stakes[2].stake.rao, alice_subnet_netuid), -# locked=Balance.from_tao(0, netuid=alice_subnet_netuid), -# emission=get_dynamic_balance(stakes[2].emission.rao, alice_subnet_netuid), -# drain=0, -# is_registered=True, -# ), -# } -# -# success = subtensor.unstake( -# alice_wallet, -# bob_wallet.hotkey.ss58_address, -# netuid=alice_subnet_netuid, -# amount=stake_bob, -# 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=alice_subnet_netuid, -# ) -# -# assert stake == Balance(0).set_unit(alice_subnet_netuid) -# -# -# 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, -# ) -# -# # make sure we passed start_call limit for both subnets -# for netuid in netuids: -# assert wait_to_start_call(subtensor, alice_wallet, netuid) -# -# 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: get_dynamic_balance( -# balances[alice_wallet.coldkey.ss58_address].rao, 2 -# ), -# bob_wallet.coldkey.ss58_address: Balance.from_tao(999_998).set_unit(3), -# } -# -# 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: ApproxBalance(alice_balance.rao), -# 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: get_dynamic_balance( -# balances[alice_wallet.coldkey.ss58_address].rao, 2 -# ), -# bob_wallet.coldkey.ss58_address: Balance.from_tao(999_998), -# } -# assert balances[alice_wallet.coldkey.ss58_address] > alice_balance +def test_single_operation(subtensor, alice_wallet, bob_wallet): + """ + Tests: + - Staking using `add_stake` + - Unstaking using `unstake` + - Checks StakeInfo + """ + alice_subnet_netuid = subtensor.get_total_subnets() # 2 + + # Register root as Alice - the subnet owner and validator + assert subtensor.register_subnet(alice_wallet, True, True) + + # Verify subnet created successfully + assert subtensor.subnet_exists(alice_subnet_netuid), ( + "Subnet wasn't created successfully" + ) + + assert wait_to_start_call(subtensor, alice_wallet, alice_subnet_netuid) + + subtensor.burned_register( + alice_wallet, + netuid=alice_subnet_netuid, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + subtensor.burned_register( + bob_wallet, + netuid=alice_subnet_netuid, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + + stake = subtensor.get_stake( + alice_wallet.coldkey.ss58_address, + bob_wallet.hotkey.ss58_address, + netuid=alice_subnet_netuid, + ) + + assert stake == Balance(0).set_unit(alice_subnet_netuid) + + success = subtensor.add_stake( + alice_wallet, + bob_wallet.hotkey.ss58_address, + netuid=alice_subnet_netuid, + amount=Balance.from_tao(1), + wait_for_inclusion=True, + wait_for_finalization=True, + ) + + assert success is True + + stake_alice = subtensor.get_stake( + alice_wallet.coldkey.ss58_address, + alice_wallet.hotkey.ss58_address, + netuid=alice_subnet_netuid, + ) + + stake_bob = subtensor.get_stake( + alice_wallet.coldkey.ss58_address, + bob_wallet.hotkey.ss58_address, + netuid=alice_subnet_netuid, + ) + + assert stake_bob > Balance(0).set_unit(alice_subnet_netuid) + + stakes = subtensor.get_stake_for_coldkey(alice_wallet.coldkey.ss58_address) + + assert stakes == [ + StakeInfo( + hotkey_ss58=alice_wallet.hotkey.ss58_address, + coldkey_ss58=alice_wallet.coldkey.ss58_address, + netuid=alice_subnet_netuid, + stake=get_dynamic_balance(stakes[0].stake.rao, alice_subnet_netuid), + locked=Balance(0).set_unit(alice_subnet_netuid), + emission=get_dynamic_balance(stakes[0].emission.rao, alice_subnet_netuid), + drain=0, + is_registered=True, + ), + StakeInfo( + hotkey_ss58=bob_wallet.hotkey.ss58_address, + coldkey_ss58=alice_wallet.coldkey.ss58_address, + netuid=alice_subnet_netuid, + stake=get_dynamic_balance(stakes[1].stake.rao, alice_subnet_netuid), + locked=Balance(0).set_unit(alice_subnet_netuid), + emission=get_dynamic_balance(stakes[1].emission.rao, alice_subnet_netuid), + drain=0, + is_registered=True, + ), + ] + + stakes = subtensor.get_stake_info_for_coldkey(alice_wallet.coldkey.ss58_address) + + assert stakes == [ + StakeInfo( + hotkey_ss58=alice_wallet.hotkey.ss58_address, + coldkey_ss58=alice_wallet.coldkey.ss58_address, + netuid=alice_subnet_netuid, + stake=get_dynamic_balance(stakes[0].stake.rao, alice_subnet_netuid), + locked=Balance(0).set_unit(alice_subnet_netuid), + emission=get_dynamic_balance(stakes[0].emission.rao, alice_subnet_netuid), + drain=0, + is_registered=True, + ), + StakeInfo( + hotkey_ss58=bob_wallet.hotkey.ss58_address, + coldkey_ss58=alice_wallet.coldkey.ss58_address, + netuid=alice_subnet_netuid, + stake=get_dynamic_balance(stakes[1].stake.rao, alice_subnet_netuid), + locked=Balance(0).set_unit(alice_subnet_netuid), + emission=get_dynamic_balance(stakes[1].emission.rao, alice_subnet_netuid), + 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.set_unit(1), + locked=Balance.from_tao(0, netuid=1), + emission=Balance.from_tao(0, netuid=1), + drain=0, + is_registered=False, + ), + 2: StakeInfo( + hotkey_ss58=bob_wallet.hotkey.ss58_address, + coldkey_ss58=alice_wallet.coldkey.ss58_address, + netuid=alice_subnet_netuid, + stake=get_dynamic_balance(stakes[2].stake.rao, alice_subnet_netuid), + locked=Balance.from_tao(0, netuid=alice_subnet_netuid), + emission=get_dynamic_balance(stakes[2].emission.rao, alice_subnet_netuid), + drain=0, + is_registered=True, + ), + } + + success = subtensor.unstake( + alice_wallet, + bob_wallet.hotkey.ss58_address, + netuid=alice_subnet_netuid, + amount=stake_bob, + 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=alice_subnet_netuid, + ) + + assert stake == Balance(0).set_unit(alice_subnet_netuid) + + +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, + ) + + # make sure we passed start_call limit for both subnets + for netuid in netuids: + assert wait_to_start_call(subtensor, alice_wallet, netuid) + + 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: get_dynamic_balance( + balances[alice_wallet.coldkey.ss58_address].rao, 2 + ), + bob_wallet.coldkey.ss58_address: Balance.from_tao(999_998).set_unit(3), + } + + 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: ApproxBalance(alice_balance.rao), + 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: get_dynamic_balance( + balances[alice_wallet.coldkey.ss58_address].rao, 2 + ), + bob_wallet.coldkey.ss58_address: Balance.from_tao(999_998), + } + assert balances[alice_wallet.coldkey.ss58_address] > alice_balance def test_safe_staking_scenarios(subtensor, alice_wallet, bob_wallet): @@ -476,313 +476,313 @@ def test_safe_staking_scenarios(subtensor, alice_wallet, bob_wallet): assert success is True, "Unstake should succeed" -# def test_safe_swap_stake_scenarios(subtensor, alice_wallet, bob_wallet): -# """ -# Tests safe swap stake scenarios with different parameters. -# -# Tests: -# 1. Fails with strict threshold (0.5%) -# 2. Succeeds with lenient threshold (10%) -# """ -# # Create new subnet (netuid 2) and register Alice -# origin_netuid = 2 -# assert subtensor.register_subnet(bob_wallet, True, True) -# assert subtensor.subnet_exists(origin_netuid), "Subnet wasn't created successfully" -# dest_netuid = 3 -# assert subtensor.register_subnet(bob_wallet, True, True) -# assert subtensor.subnet_exists(dest_netuid), "Subnet wasn't created successfully" -# -# # make sure we passed start_call limit for both subnets -# assert wait_to_start_call(subtensor, bob_wallet, origin_netuid) -# assert wait_to_start_call(subtensor, bob_wallet, dest_netuid) -# -# # Register Alice on both subnets -# subtensor.burned_register( -# alice_wallet, -# netuid=origin_netuid, -# wait_for_inclusion=True, -# wait_for_finalization=True, -# ) -# subtensor.burned_register( -# alice_wallet, -# netuid=dest_netuid, -# wait_for_inclusion=True, -# wait_for_finalization=True, -# ) -# -# # Add initial stake to swap from -# initial_stake_amount = Balance.from_tao(10_000) -# success = subtensor.add_stake( -# alice_wallet, -# alice_wallet.hotkey.ss58_address, -# netuid=origin_netuid, -# amount=initial_stake_amount, -# wait_for_inclusion=True, -# wait_for_finalization=True, -# ) -# assert success is True -# -# origin_stake = subtensor.get_stake( -# alice_wallet.coldkey.ss58_address, -# alice_wallet.hotkey.ss58_address, -# netuid=origin_netuid, -# ) -# assert origin_stake > Balance(0), "Origin stake should be non-zero" -# -# stake_swap_amount = Balance.from_tao(10_000) -# # 1. Try swap with strict threshold and big amount- should fail -# success = subtensor.swap_stake( -# wallet=alice_wallet, -# hotkey_ss58=alice_wallet.hotkey.ss58_address, -# origin_netuid=origin_netuid, -# destination_netuid=dest_netuid, -# amount=stake_swap_amount, -# wait_for_inclusion=True, -# wait_for_finalization=True, -# safe_staking=True, -# rate_tolerance=0.005, # 0.5% -# allow_partial_stake=False, -# ) -# assert success is False -# -# # Verify no stake was moved -# dest_stake = subtensor.get_stake( -# alice_wallet.coldkey.ss58_address, -# alice_wallet.hotkey.ss58_address, -# netuid=dest_netuid, -# ) -# assert dest_stake == Balance(0), ( -# "Destination stake should remain 0 after failed swap" -# ) -# -# # 2. Try swap with higher threshold and less amount - should succeed -# stake_swap_amount = Balance.from_tao(100) -# success = subtensor.swap_stake( -# wallet=alice_wallet, -# hotkey_ss58=alice_wallet.hotkey.ss58_address, -# origin_netuid=origin_netuid, -# destination_netuid=dest_netuid, -# amount=stake_swap_amount, -# wait_for_inclusion=True, -# wait_for_finalization=True, -# safe_staking=True, -# rate_tolerance=0.3, # 30% -# allow_partial_stake=True, -# ) -# assert success is True -# -# # Verify stake was moved -# origin_stake = subtensor.get_stake( -# alice_wallet.coldkey.ss58_address, -# alice_wallet.hotkey.ss58_address, -# netuid=origin_netuid, -# ) -# dest_stake = subtensor.get_stake( -# alice_wallet.coldkey.ss58_address, -# alice_wallet.hotkey.ss58_address, -# netuid=dest_netuid, -# ) -# assert dest_stake > Balance(0), ( -# "Destination stake should be non-zero after successful swap" -# ) -# -# -# def test_move_stake(subtensor, alice_wallet, bob_wallet): -# """ -# Tests: -# - Adding stake -# - Moving stake from one hotkey-subnet pair to another -# """ -# -# alice_subnet_netuid = subtensor.get_total_subnets() # 2 -# assert subtensor.register_subnet(alice_wallet, True, True) -# assert subtensor.subnet_exists(alice_subnet_netuid), ( -# "Subnet wasn't created successfully" -# ) -# -# assert wait_to_start_call(subtensor, alice_wallet, alice_subnet_netuid) -# -# subtensor.burned_register( -# alice_wallet, -# netuid=alice_subnet_netuid, -# wait_for_inclusion=True, -# wait_for_finalization=True, -# ) -# -# assert subtensor.add_stake( -# alice_wallet, -# alice_wallet.hotkey.ss58_address, -# netuid=alice_subnet_netuid, -# amount=Balance.from_tao(1_000), -# wait_for_inclusion=True, -# wait_for_finalization=True, -# ) -# -# stakes = subtensor.get_stake_for_coldkey(alice_wallet.coldkey.ss58_address) -# -# assert stakes == [ -# StakeInfo( -# hotkey_ss58=alice_wallet.hotkey.ss58_address, -# coldkey_ss58=alice_wallet.coldkey.ss58_address, -# netuid=alice_subnet_netuid, -# stake=get_dynamic_balance(stakes[0].stake.rao, alice_subnet_netuid), -# locked=Balance(0), -# emission=get_dynamic_balance(stakes[0].emission.rao, alice_subnet_netuid), -# drain=0, -# is_registered=True, -# ), -# ] -# -# bob_subnet_netuid = subtensor.get_total_subnets() # 3 -# subtensor.register_subnet(bob_wallet, True, True) -# assert subtensor.subnet_exists(bob_subnet_netuid), ( -# "Subnet wasn't created successfully" -# ) -# -# assert wait_to_start_call(subtensor, bob_wallet, bob_subnet_netuid) -# -# assert subtensor.move_stake( -# alice_wallet, -# origin_hotkey=alice_wallet.hotkey.ss58_address, -# origin_netuid=alice_subnet_netuid, -# destination_hotkey=bob_wallet.hotkey.ss58_address, -# destination_netuid=bob_subnet_netuid, -# amount=stakes[0].stake, -# wait_for_finalization=True, -# wait_for_inclusion=True, -# ) -# -# stakes = subtensor.get_stake_for_coldkey(alice_wallet.coldkey.ss58_address) -# -# assert stakes == [ -# StakeInfo( -# hotkey_ss58=alice_wallet.hotkey.ss58_address, -# coldkey_ss58=alice_wallet.coldkey.ss58_address, -# netuid=alice_subnet_netuid, -# stake=get_dynamic_balance(stakes[0].stake.rao, bob_subnet_netuid), -# locked=Balance(0).set_unit(bob_subnet_netuid), -# emission=get_dynamic_balance(stakes[0].emission.rao, bob_subnet_netuid), -# drain=0, -# is_registered=True, -# ), -# StakeInfo( -# hotkey_ss58=bob_wallet.hotkey.ss58_address, -# coldkey_ss58=alice_wallet.coldkey.ss58_address, -# netuid=bob_subnet_netuid, -# stake=get_dynamic_balance(stakes[1].stake.rao, bob_subnet_netuid), -# locked=Balance(0).set_unit(bob_subnet_netuid), -# emission=get_dynamic_balance(stakes[1].emission.rao, bob_subnet_netuid), -# drain=0, -# is_registered=True, -# ), -# ] -# -# -# def test_transfer_stake(subtensor, alice_wallet, bob_wallet, dave_wallet): -# """ -# Tests: -# - Adding stake -# - Transferring stake from one coldkey-subnet pair to another -# """ -# alice_subnet_netuid = subtensor.get_total_subnets() # 2 -# -# assert subtensor.register_subnet(alice_wallet, True, True) -# assert subtensor.subnet_exists(alice_subnet_netuid), ( -# "Subnet wasn't created successfully" -# ) -# -# assert wait_to_start_call(subtensor, alice_wallet, alice_subnet_netuid) -# -# subtensor.burned_register( -# alice_wallet, -# netuid=alice_subnet_netuid, -# wait_for_inclusion=True, -# wait_for_finalization=True, -# ) -# -# assert subtensor.add_stake( -# alice_wallet, -# alice_wallet.hotkey.ss58_address, -# netuid=alice_subnet_netuid, -# amount=Balance.from_tao(1_000), -# wait_for_inclusion=True, -# wait_for_finalization=True, -# ) -# -# alice_stakes = subtensor.get_stake_for_coldkey(alice_wallet.coldkey.ss58_address) -# -# assert alice_stakes == [ -# StakeInfo( -# hotkey_ss58=alice_wallet.hotkey.ss58_address, -# coldkey_ss58=alice_wallet.coldkey.ss58_address, -# netuid=alice_subnet_netuid, -# stake=get_dynamic_balance(alice_stakes[0].stake.rao, alice_subnet_netuid), -# locked=Balance(0), -# emission=get_dynamic_balance( -# alice_stakes[0].emission.rao, alice_subnet_netuid -# ), -# drain=0, -# is_registered=True, -# ), -# ] -# -# bob_stakes = subtensor.get_stake_for_coldkey(bob_wallet.coldkey.ss58_address) -# -# assert bob_stakes == [] -# -# dave_subnet_netuid = subtensor.get_total_subnets() # 3 -# subtensor.register_subnet(dave_wallet, True, True) -# -# assert wait_to_start_call(subtensor, dave_wallet, dave_subnet_netuid) -# -# subtensor.burned_register( -# bob_wallet, -# netuid=dave_subnet_netuid, -# wait_for_inclusion=True, -# wait_for_finalization=True, -# ) -# -# assert subtensor.transfer_stake( -# alice_wallet, -# destination_coldkey_ss58=bob_wallet.coldkey.ss58_address, -# hotkey_ss58=alice_wallet.hotkey.ss58_address, -# origin_netuid=alice_subnet_netuid, -# destination_netuid=dave_subnet_netuid, -# amount=alice_stakes[0].stake, -# wait_for_inclusion=True, -# wait_for_finalization=True, -# ) -# -# alice_stakes = subtensor.get_stake_for_coldkey(alice_wallet.coldkey.ss58_address) -# -# assert alice_stakes == [ -# StakeInfo( -# hotkey_ss58=alice_wallet.hotkey.ss58_address, -# coldkey_ss58=alice_wallet.coldkey.ss58_address, -# netuid=alice_subnet_netuid, -# stake=get_dynamic_balance(alice_stakes[0].stake.rao, alice_subnet_netuid), -# locked=Balance(0).set_unit(alice_subnet_netuid), -# emission=get_dynamic_balance( -# alice_stakes[0].emission.rao, alice_subnet_netuid -# ), -# drain=0, -# is_registered=True, -# ), -# ] -# -# bob_stakes = subtensor.get_stake_for_coldkey(bob_wallet.coldkey.ss58_address) -# -# assert bob_stakes == [ -# StakeInfo( -# hotkey_ss58=alice_wallet.hotkey.ss58_address, -# coldkey_ss58=bob_wallet.coldkey.ss58_address, -# netuid=dave_subnet_netuid, -# stake=get_dynamic_balance(bob_stakes[0].stake.rao, dave_subnet_netuid), -# locked=Balance(0), -# emission=get_dynamic_balance( -# bob_stakes[0].emission.rao, dave_subnet_netuid -# ), -# drain=0, -# is_registered=False, -# ), -# ] +def test_safe_swap_stake_scenarios(subtensor, alice_wallet, bob_wallet): + """ + Tests safe swap stake scenarios with different parameters. + + Tests: + 1. Fails with strict threshold (0.5%) + 2. Succeeds with lenient threshold (10%) + """ + # Create new subnet (netuid 2) and register Alice + origin_netuid = 2 + assert subtensor.register_subnet(bob_wallet, True, True) + assert subtensor.subnet_exists(origin_netuid), "Subnet wasn't created successfully" + dest_netuid = 3 + assert subtensor.register_subnet(bob_wallet, True, True) + assert subtensor.subnet_exists(dest_netuid), "Subnet wasn't created successfully" + + # make sure we passed start_call limit for both subnets + assert wait_to_start_call(subtensor, bob_wallet, origin_netuid) + assert wait_to_start_call(subtensor, bob_wallet, dest_netuid) + + # Register Alice on both subnets + subtensor.burned_register( + alice_wallet, + netuid=origin_netuid, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + subtensor.burned_register( + alice_wallet, + netuid=dest_netuid, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + + # Add initial stake to swap from + initial_stake_amount = Balance.from_tao(10_000) + success = subtensor.add_stake( + alice_wallet, + alice_wallet.hotkey.ss58_address, + netuid=origin_netuid, + amount=initial_stake_amount, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + assert success is True + + origin_stake = subtensor.get_stake( + alice_wallet.coldkey.ss58_address, + alice_wallet.hotkey.ss58_address, + netuid=origin_netuid, + ) + assert origin_stake > Balance(0), "Origin stake should be non-zero" + + stake_swap_amount = Balance.from_tao(10_000) + # 1. Try swap with strict threshold and big amount- should fail + success = subtensor.swap_stake( + wallet=alice_wallet, + hotkey_ss58=alice_wallet.hotkey.ss58_address, + origin_netuid=origin_netuid, + destination_netuid=dest_netuid, + amount=stake_swap_amount, + wait_for_inclusion=True, + wait_for_finalization=True, + safe_staking=True, + rate_tolerance=0.005, # 0.5% + allow_partial_stake=False, + ) + assert success is False + + # Verify no stake was moved + dest_stake = subtensor.get_stake( + alice_wallet.coldkey.ss58_address, + alice_wallet.hotkey.ss58_address, + netuid=dest_netuid, + ) + assert dest_stake == Balance(0), ( + "Destination stake should remain 0 after failed swap" + ) + + # 2. Try swap with higher threshold and less amount - should succeed + stake_swap_amount = Balance.from_tao(100) + success = subtensor.swap_stake( + wallet=alice_wallet, + hotkey_ss58=alice_wallet.hotkey.ss58_address, + origin_netuid=origin_netuid, + destination_netuid=dest_netuid, + amount=stake_swap_amount, + wait_for_inclusion=True, + wait_for_finalization=True, + safe_staking=True, + rate_tolerance=0.3, # 30% + allow_partial_stake=True, + ) + assert success is True + + # Verify stake was moved + origin_stake = subtensor.get_stake( + alice_wallet.coldkey.ss58_address, + alice_wallet.hotkey.ss58_address, + netuid=origin_netuid, + ) + dest_stake = subtensor.get_stake( + alice_wallet.coldkey.ss58_address, + alice_wallet.hotkey.ss58_address, + netuid=dest_netuid, + ) + assert dest_stake > Balance(0), ( + "Destination stake should be non-zero after successful swap" + ) + + +def test_move_stake(subtensor, alice_wallet, bob_wallet): + """ + Tests: + - Adding stake + - Moving stake from one hotkey-subnet pair to another + """ + + alice_subnet_netuid = subtensor.get_total_subnets() # 2 + assert subtensor.register_subnet(alice_wallet, True, True) + assert subtensor.subnet_exists(alice_subnet_netuid), ( + "Subnet wasn't created successfully" + ) + + assert wait_to_start_call(subtensor, alice_wallet, alice_subnet_netuid) + + subtensor.burned_register( + alice_wallet, + netuid=alice_subnet_netuid, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + + assert subtensor.add_stake( + alice_wallet, + alice_wallet.hotkey.ss58_address, + netuid=alice_subnet_netuid, + amount=Balance.from_tao(1_000), + wait_for_inclusion=True, + wait_for_finalization=True, + ) + + stakes = subtensor.get_stake_for_coldkey(alice_wallet.coldkey.ss58_address) + + assert stakes == [ + StakeInfo( + hotkey_ss58=alice_wallet.hotkey.ss58_address, + coldkey_ss58=alice_wallet.coldkey.ss58_address, + netuid=alice_subnet_netuid, + stake=get_dynamic_balance(stakes[0].stake.rao, alice_subnet_netuid), + locked=Balance(0), + emission=get_dynamic_balance(stakes[0].emission.rao, alice_subnet_netuid), + drain=0, + is_registered=True, + ), + ] + + bob_subnet_netuid = subtensor.get_total_subnets() # 3 + subtensor.register_subnet(bob_wallet, True, True) + assert subtensor.subnet_exists(bob_subnet_netuid), ( + "Subnet wasn't created successfully" + ) + + assert wait_to_start_call(subtensor, bob_wallet, bob_subnet_netuid) + + assert subtensor.move_stake( + alice_wallet, + origin_hotkey=alice_wallet.hotkey.ss58_address, + origin_netuid=alice_subnet_netuid, + destination_hotkey=bob_wallet.hotkey.ss58_address, + destination_netuid=bob_subnet_netuid, + amount=stakes[0].stake, + wait_for_finalization=True, + wait_for_inclusion=True, + ) + + stakes = subtensor.get_stake_for_coldkey(alice_wallet.coldkey.ss58_address) + + assert stakes == [ + StakeInfo( + hotkey_ss58=alice_wallet.hotkey.ss58_address, + coldkey_ss58=alice_wallet.coldkey.ss58_address, + netuid=alice_subnet_netuid, + stake=get_dynamic_balance(stakes[0].stake.rao, bob_subnet_netuid), + locked=Balance(0).set_unit(bob_subnet_netuid), + emission=get_dynamic_balance(stakes[0].emission.rao, bob_subnet_netuid), + drain=0, + is_registered=True, + ), + StakeInfo( + hotkey_ss58=bob_wallet.hotkey.ss58_address, + coldkey_ss58=alice_wallet.coldkey.ss58_address, + netuid=bob_subnet_netuid, + stake=get_dynamic_balance(stakes[1].stake.rao, bob_subnet_netuid), + locked=Balance(0).set_unit(bob_subnet_netuid), + emission=get_dynamic_balance(stakes[1].emission.rao, bob_subnet_netuid), + drain=0, + is_registered=True, + ), + ] + + +def test_transfer_stake(subtensor, alice_wallet, bob_wallet, dave_wallet): + """ + Tests: + - Adding stake + - Transferring stake from one coldkey-subnet pair to another + """ + alice_subnet_netuid = subtensor.get_total_subnets() # 2 + + assert subtensor.register_subnet(alice_wallet, True, True) + assert subtensor.subnet_exists(alice_subnet_netuid), ( + "Subnet wasn't created successfully" + ) + + assert wait_to_start_call(subtensor, alice_wallet, alice_subnet_netuid) + + subtensor.burned_register( + alice_wallet, + netuid=alice_subnet_netuid, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + + assert subtensor.add_stake( + alice_wallet, + alice_wallet.hotkey.ss58_address, + netuid=alice_subnet_netuid, + amount=Balance.from_tao(1_000), + wait_for_inclusion=True, + wait_for_finalization=True, + ) + + alice_stakes = subtensor.get_stake_for_coldkey(alice_wallet.coldkey.ss58_address) + + assert alice_stakes == [ + StakeInfo( + hotkey_ss58=alice_wallet.hotkey.ss58_address, + coldkey_ss58=alice_wallet.coldkey.ss58_address, + netuid=alice_subnet_netuid, + stake=get_dynamic_balance(alice_stakes[0].stake.rao, alice_subnet_netuid), + locked=Balance(0), + emission=get_dynamic_balance( + alice_stakes[0].emission.rao, alice_subnet_netuid + ), + drain=0, + is_registered=True, + ), + ] + + bob_stakes = subtensor.get_stake_for_coldkey(bob_wallet.coldkey.ss58_address) + + assert bob_stakes == [] + + dave_subnet_netuid = subtensor.get_total_subnets() # 3 + subtensor.register_subnet(dave_wallet, True, True) + + assert wait_to_start_call(subtensor, dave_wallet, dave_subnet_netuid) + + subtensor.burned_register( + bob_wallet, + netuid=dave_subnet_netuid, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + + assert subtensor.transfer_stake( + alice_wallet, + destination_coldkey_ss58=bob_wallet.coldkey.ss58_address, + hotkey_ss58=alice_wallet.hotkey.ss58_address, + origin_netuid=alice_subnet_netuid, + destination_netuid=dave_subnet_netuid, + amount=alice_stakes[0].stake, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + + alice_stakes = subtensor.get_stake_for_coldkey(alice_wallet.coldkey.ss58_address) + + assert alice_stakes == [ + StakeInfo( + hotkey_ss58=alice_wallet.hotkey.ss58_address, + coldkey_ss58=alice_wallet.coldkey.ss58_address, + netuid=alice_subnet_netuid, + stake=get_dynamic_balance(alice_stakes[0].stake.rao, alice_subnet_netuid), + locked=Balance(0).set_unit(alice_subnet_netuid), + emission=get_dynamic_balance( + alice_stakes[0].emission.rao, alice_subnet_netuid + ), + drain=0, + is_registered=True, + ), + ] + + bob_stakes = subtensor.get_stake_for_coldkey(bob_wallet.coldkey.ss58_address) + + assert bob_stakes == [ + StakeInfo( + hotkey_ss58=alice_wallet.hotkey.ss58_address, + coldkey_ss58=bob_wallet.coldkey.ss58_address, + netuid=dave_subnet_netuid, + stake=get_dynamic_balance(bob_stakes[0].stake.rao, dave_subnet_netuid), + locked=Balance(0), + emission=get_dynamic_balance( + bob_stakes[0].emission.rao, dave_subnet_netuid + ), + drain=0, + is_registered=False, + ), + ] From 51187feb8daace52b263615853fdf9646cb1f2e3 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 24 Apr 2025 15:02:45 -0700 Subject: [PATCH 046/251] bring back previous rate_tolerance --- tests/e2e_tests/test_staking.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/e2e_tests/test_staking.py b/tests/e2e_tests/test_staking.py index ae687ccbc8..dddd9d1af1 100644 --- a/tests/e2e_tests/test_staking.py +++ b/tests/e2e_tests/test_staking.py @@ -354,7 +354,7 @@ def test_safe_staking_scenarios(subtensor, alice_wallet, bob_wallet): wait_for_inclusion=True, wait_for_finalization=True, safe_staking=True, - rate_tolerance=0.001, # 0.5% + rate_tolerance=0.005, # 0.5% allow_partial_stake=False, ) assert success is False @@ -375,7 +375,7 @@ def test_safe_staking_scenarios(subtensor, alice_wallet, bob_wallet): wait_for_inclusion=True, wait_for_finalization=True, safe_staking=True, - rate_tolerance=0.001, # 0.5% + rate_tolerance=0.005, # 0.5% allow_partial_stake=True, ) assert success is True @@ -448,7 +448,7 @@ def test_safe_staking_scenarios(subtensor, alice_wallet, bob_wallet): wait_for_inclusion=True, wait_for_finalization=True, safe_staking=True, - rate_tolerance=0.001, # 0.5% + rate_tolerance=0.005, # 0.5% allow_partial_stake=True, ) assert success is True From 56080c188e9eb9119a48de6b9ecf90682a2c5595 Mon Sep 17 00:00:00 2001 From: PixelPilot <161360836+PixelPil0t1@users.noreply.github.com> Date: Sat, 26 Apr 2025 12:28:34 +0200 Subject: [PATCH 047/251] Update docker_release.yml --- .github/workflows/docker_release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker_release.yml b/.github/workflows/docker_release.yml index dbb6c3bab8..9e8aabed5a 100644 --- a/.github/workflows/docker_release.yml +++ b/.github/workflows/docker_release.yml @@ -18,7 +18,7 @@ jobs: steps: - name: Check out code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install cosign uses: sigstore/cosign-installer@v3 @@ -48,4 +48,4 @@ jobs: DIGEST: ${{ steps.build.outputs.digest }} TAGS: ${{ steps.build.outputs.tags }} run: | - echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST} \ No newline at end of file + echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST} From 55db811c454174158f130ef45dd2f66e5f5bec5b Mon Sep 17 00:00:00 2001 From: PixelPilot <161360836+PixelPil0t1@users.noreply.github.com> Date: Sat, 26 Apr 2025 12:29:10 +0200 Subject: [PATCH 048/251] Update release.yml --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3d282dbb2d..1118590448 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,7 +13,7 @@ jobs: name: Build Python distribution runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v4 @@ -69,4 +69,4 @@ jobs: uses: pypa/gh-action-pypi-publish@release/v1 with: verbose: true - print-hash: true \ No newline at end of file + print-hash: true From 4666407af1928a22eb20b2557eef35b894bb7982 Mon Sep 17 00:00:00 2001 From: Donny <130464015+defitricks@users.noreply.github.com> Date: Sun, 27 Apr 2025 20:41:38 +0300 Subject: [PATCH 049/251] fix: $BASH_ENV loading issue --- scripts/check_requirements_changes.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check_requirements_changes.sh b/scripts/check_requirements_changes.sh index 5b41f463c1..4a736f525d 100755 --- a/scripts/check_requirements_changes.sh +++ b/scripts/check_requirements_changes.sh @@ -1,7 +1,7 @@ #!/bin/bash # Check if requirements files have changed in the last commit -if git diff --name-only HEAD~1 | grep -E 'pyproject.toml'; then +if git diff --name-only HEAD | grep -E 'pyproject.toml'; then echo "Requirements files may have changed. Running compatibility checks..." echo 'export REQUIREMENTS_CHANGED="true"' >> $BASH_ENV else From 456acd476fb9388e1a5b403f30c5ab2097f227e2 Mon Sep 17 00:00:00 2001 From: Roman Date: Mon, 28 Apr 2025 12:23:04 -0700 Subject: [PATCH 050/251] let users use `int` indexes, not only enum --- bittensor/core/async_subtensor.py | 13 ++++++++----- bittensor/core/subtensor.py | 19 +++++++++++-------- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 6fa365a2b7..5d9c01beab 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -1480,9 +1480,9 @@ async def get_metagraph_info( Retrieves full or partial metagraph information for the specified subnet (netuid). Arguments: - netuid (int): The NetUID of the subnet to query. - field_indices (Optional[list[SelectiveMetagraphIndex]]): An optional list of SelectiveMetagraphIndex values - specifying which fields to retrieve. If not provided, all available fields will be returned. + netuid: The NetUID of the subnet to query. + field_indices: An optional list of SelectiveMetagraphIndex values specifying which fields to retrieve. If + not provided, all available fields will be returned. block: the block number at which to retrieve the hyperparameter. Do not specify if using block_hash or reuse_block block_hash: The hash of blockchain block number for the query. Do not specify if using @@ -1505,9 +1505,12 @@ async def get_metagraph_info( if field_indices: if isinstance(field_indices, list) and all( - isinstance(f, SelectiveMetagraphIndex) for f in field_indices + isinstance(f, (SelectiveMetagraphIndex, int)) for f in field_indices ): - indexes = [f.value for f in field_indices] + indexes = [ + f.value if isinstance(f, SelectiveMetagraphIndex) else f + for f in field_indices + ] else: raise ValueError( "`field_indices` must be a list of SelectiveMetagraphIndex items." diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 62b9fb8c83..d28b7f34b2 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -1133,18 +1133,18 @@ def get_minimum_required_stake(self) -> Balance: def get_metagraph_info( self, netuid: int, - field_indices: Optional[list["SelectiveMetagraphIndex"]] = None, + field_indices: Optional[list[Union["SelectiveMetagraphIndex", int]]] = None, block: Optional[int] = None, ) -> Optional[MetagraphInfo]: """ Retrieves full or partial metagraph information for the specified subnet (netuid). Arguments: - netuid (int): The NetUID of the subnet to query. - field_indices (Optional[list[SelectiveMetagraphIndex]]): An optional list of SelectiveMetagraphIndex values - specifying which fields to retrieve. If not provided, all available fields will be returned. - block (Optional[int]):The block number at which to query the data. If not specified, the current block or - one determined via reuse_block or block_hash will be used. + netuid: The NetUID of the subnet to query. + field_indices: An optional list of SelectiveMetagraphIndex values specifying which fields to retrieve. If + not provided, all available fields will be returned. + block: The block number at which to query the data. If not specified, the current block or one determined + via reuse_block or block_hash will be used. Returns: Optional[MetagraphInfo]: A MetagraphInfo object containing the requested subnet data, or None if the subnet @@ -1162,9 +1162,12 @@ def get_metagraph_info( if field_indices: if isinstance(field_indices, list) and all( - isinstance(f, SelectiveMetagraphIndex) for f in field_indices + isinstance(f, (SelectiveMetagraphIndex, int)) for f in field_indices ): - indexes = [f.value for f in field_indices] + indexes = [ + f.value if isinstance(f, SelectiveMetagraphIndex) else f + for f in field_indices + ] else: raise ValueError( "`field_indices` must be a list of SelectiveMetagraphIndex items." From 27f9c4e0ab1ecc16f4e6ffa1ccb78231460692a8 Mon Sep 17 00:00:00 2001 From: Roman Date: Mon, 28 Apr 2025 12:23:29 -0700 Subject: [PATCH 051/251] add SelectiveMetagraphIndexes into easy_imports --- bittensor/utils/easy_imports.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bittensor/utils/easy_imports.py b/bittensor/utils/easy_imports.py index cc81efe3d2..ea0e72bce9 100644 --- a/bittensor/utils/easy_imports.py +++ b/bittensor/utils/easy_imports.py @@ -47,6 +47,7 @@ ProposalCallData, ProposalVoteData, ScheduledColdkeySwapInfo, + SelectiveMetagraphIndex, StakeInfo, SubnetHyperparameters, SubnetIdentity, From 0a2c41a7d0056be54fa2a3390ae6ce7e7b9484fb Mon Sep 17 00:00:00 2001 From: Roman Date: Mon, 28 Apr 2025 12:23:37 -0700 Subject: [PATCH 052/251] improve tests --- tests/unit_tests/test_async_subtensor.py | 14 ++++++++++---- tests/unit_tests/test_subtensor.py | 14 ++++++++++---- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/tests/unit_tests/test_async_subtensor.py b/tests/unit_tests/test_async_subtensor.py index 07d3c4e98c..0c4e4587e8 100644 --- a/tests/unit_tests/test_async_subtensor.py +++ b/tests/unit_tests/test_async_subtensor.py @@ -3100,7 +3100,7 @@ async def test_get_metagraph_info_specific_fields(subtensor, mocker): # Preps netuid = 1 mock_value = {"mock": "data"} - fields = [SelectiveMetagraphIndex.Name, SelectiveMetagraphIndex.OwnerHotkey] + fields = [SelectiveMetagraphIndex.Name, 5] mock_runtime_call = mocker.patch.object( subtensor.substrate, @@ -3119,7 +3119,13 @@ async def test_get_metagraph_info_specific_fields(subtensor, mocker): mock_runtime_call.assert_awaited_once_with( "SubnetInfoRuntimeApi", "get_selective_metagraph", - params=[netuid, [0] + [f.value for f in fields]], + params=[ + netuid, + [0] + + [ + f.value if isinstance(f, SelectiveMetagraphIndex) else f for f in fields + ], + ], block_hash=await subtensor.determine_block_hash(None), ) mock_from_dict.assert_called_once_with(mock_value) @@ -3131,8 +3137,8 @@ async def test_get_metagraph_info_specific_fields(subtensor, mocker): [ "invalid", ], - [SelectiveMetagraphIndex.Active, 1], - [1, 2, 3], + [SelectiveMetagraphIndex.Active, 1, "f"], + [1, 2, 3, "f"], ], ) @pytest.mark.asyncio diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index 0d5f418170..015884a831 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -3428,7 +3428,7 @@ def test_get_metagraph_info_specific_fields(subtensor, mocker): # Preps netuid = 1 mock_value = {"mock": "data"} - fields = [SelectiveMetagraphIndex.Name, SelectiveMetagraphIndex.OwnerHotkey] + fields = [SelectiveMetagraphIndex.Name, 5] mock_runtime_call = mocker.patch.object( subtensor.substrate, @@ -3447,7 +3447,13 @@ def test_get_metagraph_info_specific_fields(subtensor, mocker): mock_runtime_call.assert_called_once_with( "SubnetInfoRuntimeApi", "get_selective_metagraph", - params=[netuid, [0] + [f.value for f in fields]], + params=[ + netuid, + [0] + + [ + f.value if isinstance(f, SelectiveMetagraphIndex) else f for f in fields + ], + ], block_hash=subtensor.determine_block_hash(None), ) mock_from_dict.assert_called_once_with(mock_value) @@ -3459,8 +3465,8 @@ def test_get_metagraph_info_specific_fields(subtensor, mocker): [ "invalid", ], - [SelectiveMetagraphIndex.Active, 1], - [1, 2, 3], + [SelectiveMetagraphIndex.Active, 1, "f"], + [1, 2, 3, "f"], ], ) def test_get_metagraph_info_invalid_field_indices(subtensor, wrong_fields): From b0cf11571060f5bf3be45777ee62700818f0d263 Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 29 Apr 2025 16:04:14 -0700 Subject: [PATCH 053/251] add new methods to subtensor --- bittensor/core/subtensor.py | 60 +++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 29fc857e2e..1268c481b6 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -388,6 +388,23 @@ def all_subnets(self, block: Optional[int] = None) -> Optional[list["DynamicInfo ) return DynamicInfo.list_from_dicts(query.decode()) + def blocks_since_last_step( + self, netuid: int, block: Optional[int] = None + ) -> Optional[int]: + """Returns number of blocks since the last epoch of the subnet. + + Arguments: + netuid (int): The unique identifier of the subnetwork. + block: the block number for this query. + + Returns: + block number of the last step in the subnet. + """ + query = self.query_subtensor( + name="BlocksSinceLastStep", block=block, params=[netuid] + ) + return query.value if query is not None and hasattr(query, "value") else query + def blocks_since_last_update(self, netuid: int, uid: int) -> Optional[int]: """ Returns the number of blocks since the last update for a specific UID in the subnetwork. @@ -2575,6 +2592,46 @@ def get_timestamp(self, block: Optional[int] = None) -> datetime: unix = cast(ScaleObj, self.query_module("Timestamp", "Now", block=block)).value return datetime.fromtimestamp(unix / 1000, tz=timezone.utc) + def get_subnet_owner_hotkey( + self, netuid: int, block: Optional[int] = None + ) -> Optional[str]: + """ + Retrieves the hotkey of the subnet owner for a given network UID. + + This function queries the subtensor network to fetch the hotkey of the owner of a subnet specified by its + netuid. If no data is found or the query fails, the function returns None. + + Arguments: + netuid: The network UID of the subnet to fetch the owner's hotkey for. + block: The specific block number to query the data from. + + Returns: + The hotkey of the subnet owner if available; None otherwise. + """ + return self.query_subtensor( + name="SubnetOwnerHotkey", params=[netuid], block=block + ) + + def get_subnet_validator_permits( + self, netuid: int, block: Optional[int] = None + ) -> Optional[list[bool]]: + """ + Retrieves the list of validator permits for a given subnet as boolean values. + + Arguments: + netuid: The unique identifier of the subnetwork. + block: The blockchain block number for the query. + + Returns: + A list of boolean values representing validator permits, or None if not available. + """ + query = self.query_subtensor( + name="ValidatorPermit", + params=[netuid], + block=block, + ) + return query.value if query is not None and hasattr(query, "value") else query + # Extrinsics helper ================================================================================================ def sign_and_send_extrinsic( @@ -2598,6 +2655,9 @@ def sign_and_send_extrinsic( wait_for_inclusion (bool): whether to wait until the extrinsic call is included on the chain wait_for_finalization (bool): whether to wait until the extrinsic call is finalized on the chain sign_with: the wallet's keypair to use for the signing. Options are "coldkey", "hotkey", "coldkeypub" + use_nonce: unique identifier for the transaction related with hot/coldkey. + period: the period of the transaction as ERA part for transaction. Means how many blocks the transaction will be valid for. + nonce_key: the type on nonce to use. Options are "hotkey" or "coldkey". raise_error: raises relevant exception rather than returning `False` if unsuccessful. Returns: From 8403fd9dc1218f9fe840957bdbd1ba3420dc843e Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 29 Apr 2025 16:04:27 -0700 Subject: [PATCH 054/251] add new methods to async subtensor --- bittensor/core/async_subtensor.py | 60 +++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 1dee258313..422fb0a6e4 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -580,6 +580,23 @@ async def all_subnets( subnets = DynamicInfo.list_from_dicts(query.decode()) return subnets + async def blocks_since_last_step( + self, netuid: int, block: Optional[int] = None + ) -> Optional[int]: + """Returns number of blocks since the last epoch of the subnet. + + Arguments: + netuid (int): The unique identifier of the subnetwork. + block: the block number for this query. + + Returns: + block number of the last step in the subnet. + """ + query = await self.query_subtensor( + name="BlocksSinceLastStep", block=block, params=[netuid] + ) + return query.value if query is not None and hasattr(query, "value") else query + async def blocks_since_last_update(self, netuid: int, uid: int) -> Optional[int]: """ Returns the number of blocks since the last update for a specific UID in the subnetwork. @@ -3149,6 +3166,46 @@ async def get_timestamp( ).value return datetime.fromtimestamp(unix / 1000, tz=timezone.utc) + async def get_subnet_owner_hotkey( + self, netuid: int, block: Optional[int] = None + ) -> Optional[str]: + """ + Retrieves the hotkey of the subnet owner for a given network UID. + + This function queries the subtensor network to fetch the hotkey of the owner of a subnet specified by its + netuid. If no data is found or the query fails, the function returns None. + + Arguments: + netuid: The network UID of the subnet to fetch the owner's hotkey for. + block: The specific block number to query the data from. + + Returns: + The hotkey of the subnet owner if available; None otherwise. + """ + return await self.query_subtensor( + name="SubnetOwnerHotkey", params=[netuid], block=block + ) + + async def get_subnet_validator_permits( + self, netuid: int, block: Optional[int] = None + ) -> Optional[list[bool]]: + """ + Retrieves the list of validator permits for a given subnet as boolean values. + + Arguments: + netuid: The unique identifier of the subnetwork. + block: The blockchain block number for the query. + + Returns: + A list of boolean values representing validator permits, or None if not available. + """ + query = await self.query_subtensor( + name="ValidatorPermit", + params=[netuid], + block=block, + ) + return query.value if query is not None and hasattr(query, "value") else query + # Extrinsics helper ================================================================================================ async def sign_and_send_extrinsic( @@ -3172,6 +3229,9 @@ async def sign_and_send_extrinsic( wait_for_inclusion (bool): whether to wait until the extrinsic call is included on the chain wait_for_finalization (bool): whether to wait until the extrinsic call is finalized on the chain sign_with: the wallet's keypair to use for the signing. Options are "coldkey", "hotkey", "coldkeypub" + use_nonce: unique identifier for the transaction related with hot/coldkey. + period: the period of the transaction as ERA part for transaction. Means how many blocks the transaction will be valid for. + nonce_key: the type on nonce to use. Options are "hotkey" or "coldkey". raise_error: raises relevant exception rather than returning `False` if unsuccessful. Returns: From 97c522e8eeb1d1354f01483ec1936e39523962c6 Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 29 Apr 2025 16:04:36 -0700 Subject: [PATCH 055/251] add tests for subtensor --- tests/unit_tests/test_subtensor.py | 129 +++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index 015884a831..ca6aad000d 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -3494,3 +3494,132 @@ def test_get_metagraph_info_subnet_not_exist(subtensor, mocker): assert result is None mocked_logger.assert_called_once_with(f"Subnet {netuid} does not exist.") + + +def test_blocks_since_last_step_with_value(subtensor, mocker): + """Test blocks_since_last_step returns correct value.""" + # preps + netuid = 1 + block = 123 + mocked_query_subtensor = mocker.MagicMock() + subtensor.query_subtensor = mocked_query_subtensor + + # call + result = subtensor.blocks_since_last_step(netuid=netuid, block=block) + + # asserts + mocked_query_subtensor.assert_called_once_with( + name="BlocksSinceLastStep", + block=block, + params=[netuid], + ) + + assert result == mocked_query_subtensor.return_value.value + + +def test_blocks_since_last_step_is_none(subtensor, mocker): + """Test blocks_since_last_step returns None correctly.""" + # preps + netuid = 1 + block = 123 + mocked_query_subtensor = mocker.MagicMock(return_value=None) + subtensor.query_subtensor = mocked_query_subtensor + + # call + result = subtensor.blocks_since_last_step(netuid=netuid, block=block) + + # asserts + mocked_query_subtensor.assert_called_once_with( + name="BlocksSinceLastStep", + block=block, + params=[netuid], + ) + + assert result is None + + +def test_get_subnet_owner_hotkey_has_return(subtensor, mocker): + """Test get_subnet_owner_hotkey returns correct value.""" + # preps + netuid = 14 + block = 123 + expected_owner_hotkey = "owner_hotkey" + mocked_query_subtensor = mocker.MagicMock(return_value=expected_owner_hotkey) + subtensor.query_subtensor = mocked_query_subtensor + + # call + result = subtensor.get_subnet_owner_hotkey(netuid=netuid, block=block) + + # asserts + mocked_query_subtensor.assert_called_once_with( + name="SubnetOwnerHotkey", + block=block, + params=[netuid], + ) + + assert result == expected_owner_hotkey + + +def test_get_subnet_owner_hotkey_is_none(subtensor, mocker): + """Test get_subnet_owner_hotkey returns None correctly.""" + # preps + netuid = 14 + block = 123 + mocked_query_subtensor = mocker.MagicMock(return_value=None) + subtensor.query_subtensor = mocked_query_subtensor + + # call + result = subtensor.get_subnet_owner_hotkey(netuid=netuid, block=block) + + # asserts + mocked_query_subtensor.assert_called_once_with( + name="SubnetOwnerHotkey", + block=block, + params=[netuid], + ) + + assert result is None + + +def test_get_subnet_validator_permits_has_values(subtensor, mocker): + """Test get_subnet_validator_permits returns correct value.""" + # preps + netuid = 14 + block = 123 + expected_validator_permits = [False, True, False] + mocked_query_subtensor = mocker.MagicMock(return_value=expected_validator_permits) + subtensor.query_subtensor = mocked_query_subtensor + + # call + result = subtensor.get_subnet_validator_permits(netuid=netuid, block=block) + + # asserts + mocked_query_subtensor.assert_called_once_with( + name="ValidatorPermit", + block=block, + params=[netuid], + ) + + assert result == expected_validator_permits + + +def test_get_subnet_validator_permits_is_none(subtensor, mocker): + """Test get_subnet_validator_permits returns correct value.""" + # preps + netuid = 14 + block = 123 + + mocked_query_subtensor = mocker.MagicMock(return_value=None) + subtensor.query_subtensor = mocked_query_subtensor + + # call + result = subtensor.get_subnet_validator_permits(netuid=netuid, block=block) + + # asserts + mocked_query_subtensor.assert_called_once_with( + name="ValidatorPermit", + block=block, + params=[netuid], + ) + + assert result is None From 7288c8f2321225a3b4329d6427a601208d44e37c Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 29 Apr 2025 16:04:41 -0700 Subject: [PATCH 056/251] add tests for async subtensor --- tests/unit_tests/test_async_subtensor.py | 135 +++++++++++++++++++++++ 1 file changed, 135 insertions(+) diff --git a/tests/unit_tests/test_async_subtensor.py b/tests/unit_tests/test_async_subtensor.py index 0c4e4587e8..bb6bb5b49f 100644 --- a/tests/unit_tests/test_async_subtensor.py +++ b/tests/unit_tests/test_async_subtensor.py @@ -3168,3 +3168,138 @@ async def test_get_metagraph_info_subnet_not_exist(subtensor, mocker): assert result is None mocked_logger.assert_called_once_with(f"Subnet {netuid} does not exist.") + + +@pytest.mark.asyncio +async def test_blocks_since_last_step_with_value(subtensor, mocker): + """Test blocks_since_last_step returns correct value.""" + # preps + netuid = 1 + block = 123 + mocked_query_subtensor = mocker.AsyncMock() + subtensor.query_subtensor = mocked_query_subtensor + + # call + result = await subtensor.blocks_since_last_step(netuid=netuid, block=block) + + # asserts + mocked_query_subtensor.assert_awaited_once_with( + name="BlocksSinceLastStep", + block=block, + params=[netuid], + ) + + assert result == mocked_query_subtensor.return_value.value + + +@pytest.mark.asyncio +async def test_blocks_since_last_step_is_none(subtensor, mocker): + """Test blocks_since_last_step returns None correctly.""" + # preps + netuid = 1 + block = 123 + mocked_query_subtensor = mocker.AsyncMock(return_value=None) + subtensor.query_subtensor = mocked_query_subtensor + + # call + result = await subtensor.blocks_since_last_step(netuid=netuid, block=block) + + # asserts + mocked_query_subtensor.assert_awaited_once_with( + name="BlocksSinceLastStep", + block=block, + params=[netuid], + ) + + assert result is None + + +@pytest.mark.asyncio +async def test_get_subnet_owner_hotkey_has_return(subtensor, mocker): + """Test get_subnet_owner_hotkey returns correct value.""" + # preps + netuid = 14 + block = 123 + expected_owner_hotkey = "owner_hotkey" + mocked_query_subtensor = mocker.AsyncMock(return_value=expected_owner_hotkey) + subtensor.query_subtensor = mocked_query_subtensor + + # call + result = await subtensor.get_subnet_owner_hotkey(netuid=netuid, block=block) + + # asserts + mocked_query_subtensor.assert_awaited_once_with( + name="SubnetOwnerHotkey", + block=block, + params=[netuid], + ) + + assert result == expected_owner_hotkey + + +@pytest.mark.asyncio +async def test_get_subnet_owner_hotkey_is_none(subtensor, mocker): + """Test get_subnet_owner_hotkey returns None correctly.""" + # preps + netuid = 14 + block = 123 + mocked_query_subtensor = mocker.AsyncMock(return_value=None) + subtensor.query_subtensor = mocked_query_subtensor + + # call + result = await subtensor.get_subnet_owner_hotkey(netuid=netuid, block=block) + + # asserts + mocked_query_subtensor.assert_awaited_once_with( + name="SubnetOwnerHotkey", + block=block, + params=[netuid], + ) + + assert result is None + + +@pytest.mark.asyncio +async def test_get_subnet_validator_permits_has_values(subtensor, mocker): + """Test get_subnet_validator_permits returns correct value.""" + # preps + netuid = 14 + block = 123 + expected_validator_permits = [False, True, False] + mocked_query_subtensor = mocker.AsyncMock(return_value=expected_validator_permits) + subtensor.query_subtensor = mocked_query_subtensor + + # call + result = await subtensor.get_subnet_validator_permits(netuid=netuid, block=block) + + # asserts + mocked_query_subtensor.assert_awaited_once_with( + name="ValidatorPermit", + block=block, + params=[netuid], + ) + + assert result == expected_validator_permits + + +@pytest.mark.asyncio +async def test_get_subnet_validator_permits_is_none(subtensor, mocker): + """Test get_subnet_validator_permits returns correct value.""" + # preps + netuid = 14 + block = 123 + + mocked_query_subtensor = mocker.AsyncMock(return_value=None) + subtensor.query_subtensor = mocked_query_subtensor + + # call + result = await subtensor.get_subnet_validator_permits(netuid=netuid, block=block) + + # asserts + mocked_query_subtensor.assert_awaited_once_with( + name="ValidatorPermit", + block=block, + params=[netuid], + ) + + assert result is None From 59d52ff1283e0ee282853ce7236c29fc76f95b52 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 30 Apr 2025 17:13:42 -0700 Subject: [PATCH 057/251] cleanup `bittensor/core/extrinsics/utils.py` --- bittensor/core/extrinsics/utils.py | 97 ++---------------------------- 1 file changed, 5 insertions(+), 92 deletions(-) diff --git a/bittensor/core/extrinsics/utils.py b/bittensor/core/extrinsics/utils.py index 7cc2e3c4e0..1000d58f28 100644 --- a/bittensor/core/extrinsics/utils.py +++ b/bittensor/core/extrinsics/utils.py @@ -2,98 +2,11 @@ from typing import TYPE_CHECKING -from async_substrate_interface.errors import SubstrateRequestException - -from bittensor.utils import format_error_message from bittensor.utils.balance import Balance -from bittensor.utils.btlogging import logging if TYPE_CHECKING: from bittensor_wallet import Wallet - from bittensor.core.async_subtensor import AsyncSubtensor - from async_substrate_interface import ( - AsyncExtrinsicReceipt, - ExtrinsicReceipt, - ) - from bittensor.core.subtensor import Subtensor from bittensor.core.chain_data import StakeInfo - from scalecodec.types import GenericExtrinsic - - -def submit_extrinsic( - subtensor: "Subtensor", - extrinsic: "GenericExtrinsic", - wait_for_inclusion: bool, - wait_for_finalization: bool, -) -> "ExtrinsicReceipt": - """ - Submits an extrinsic to the substrate blockchain and handles potential exceptions. - - This function attempts to submit an extrinsic to the substrate blockchain with specified options - for waiting for inclusion in a block and/or finalization. If an exception occurs during submission, - it logs the error and re-raises the exception. - - Args: - subtensor: The Subtensor instance used to interact with the blockchain. - extrinsic (scalecodec.types.GenericExtrinsic): The extrinsic to be submitted to the blockchain. - wait_for_inclusion (bool): Whether to wait for the extrinsic to be included in a block. - wait_for_finalization (bool): Whether to wait for the extrinsic to be finalized on the blockchain. - - Returns: - response: The response from the substrate after submitting the extrinsic. - - Raises: - SubstrateRequestException: If the submission of the extrinsic fails, the error is logged and re-raised. - """ - try: - return subtensor.substrate.submit_extrinsic( - extrinsic, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) - except SubstrateRequestException as e: - logging.error(format_error_message(e.args[0])) - # Re-raise the exception for retrying of the extrinsic call. If we remove the retry logic, - # the raise will need to be removed. - raise - - -async def async_submit_extrinsic( - subtensor: "AsyncSubtensor", - extrinsic: "GenericExtrinsic", - wait_for_inclusion: bool, - wait_for_finalization: bool, -) -> "AsyncExtrinsicReceipt": - """ - Submits an extrinsic to the substrate blockchain and handles potential exceptions. - - This function attempts to submit an extrinsic to the substrate blockchain with specified options - for waiting for inclusion in a block and/or finalization. If an exception occurs during submission, - it logs the error and re-raises the exception. - - Args: - subtensor: The AsyncSubtensor instance used to interact with the blockchain. - extrinsic: The extrinsic to be submitted to the blockchain. - wait_for_inclusion: Whether to wait for the extrinsic to be included in a block. - wait_for_finalization: Whether to wait for the extrinsic to be finalized on the blockchain. - - Returns: - response: The response from the substrate after submitting the extrinsic. - - Raises: - SubstrateRequestException: If the submission of the extrinsic fails, the error is logged and re-raised. - """ - try: - return await subtensor.substrate.submit_extrinsic( - extrinsic, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) - except SubstrateRequestException as e: - logging.error(format_error_message(e.args[0])) - # Re-raise the exception for retrying of the extrinsic call. If we remove the retry logic, - # the raise will need to be removed. - raise def get_old_stakes( @@ -101,7 +14,7 @@ def get_old_stakes( hotkey_ss58s: list[str], netuids: list[int], all_stakes: list["StakeInfo"], -) -> list[Balance]: +) -> list["Balance"]: """ Retrieve the previous staking balances for a wallet's hotkeys across given netuids. @@ -110,10 +23,10 @@ def get_old_stakes( is found for a particular hotkey and netuid combination, a default balance of zero is returned. Args: - wallet (Wallet): The wallet containing the coldkey to compare with stake data. - hotkey_ss58s (list[str]): List of hotkey SS58 addresses for which stakes are retrieved. - netuids (list[int]): List of network unique identifiers (netuids) corresponding to the hotkeys. - all_stakes (list[StakeInfo]): A collection of all staking information to search through. + wallet: The wallet containing the coldkey to compare with stake data. + hotkey_ss58s: List of hotkey SS58 addresses for which stakes are retrieved. + netuids: List of network unique identifiers (netuids) corresponding to the hotkeys. + all_stakes: A collection of all staking information to search through. Returns: list[Balance]: A list of Balances, each representing the stake for a given hotkey and netuid. From 7206524dcd631e58b339143af8a97e472ffa6446 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 30 Apr 2025 17:14:17 -0700 Subject: [PATCH 058/251] improve `bittensor.utils.format_error_message` --- bittensor/utils/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bittensor/utils/__init__.py b/bittensor/utils/__init__.py index 3d2c82803b..cbcaa3dd0f 100644 --- a/bittensor/utils/__init__.py +++ b/bittensor/utils/__init__.py @@ -248,7 +248,7 @@ def format_error_message(error_message: Union[dict, Exception]) -> str: err_type = error_message.get("type", err_type) err_name = error_message.get("name", err_name) err_docs = error_message.get("docs", [err_description]) - err_description = err_docs[0] if err_docs else err_description + err_description = " ".join(err_docs) if err_docs else err_description elif error_message.get("code") and error_message.get("message"): err_type = error_message.get("code", err_name) From 9861b117e8ae85d05a4b3deac902b26a5d947ed6 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 30 Apr 2025 17:15:13 -0700 Subject: [PATCH 059/251] remove unnecessary tests and add new ones --- tests/unit_tests/extrinsics/test_utils.py | 55 ++++++++++++----------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/tests/unit_tests/extrinsics/test_utils.py b/tests/unit_tests/extrinsics/test_utils.py index 060f45146b..d77c3cc5e7 100644 --- a/tests/unit_tests/extrinsics/test_utils.py +++ b/tests/unit_tests/extrinsics/test_utils.py @@ -1,26 +1,31 @@ -from unittest.mock import MagicMock - -import pytest -from scalecodec.types import GenericExtrinsic - +from bittensor.core.chain_data import StakeInfo from bittensor.core.extrinsics import utils -from bittensor.core.subtensor import Subtensor - - -@pytest.fixture -def mock_subtensor(mock_substrate): - mock_subtensor = MagicMock(autospec=Subtensor) - mock_subtensor.substrate = mock_substrate - yield mock_subtensor - - -@pytest.fixture -def starting_block(): - yield {"header": {"number": 1, "hash": "0x0100"}} - - -def test_submit_extrinsic_success(mock_subtensor): - mock_subtensor.substrate.submit_extrinsic.return_value = True - mock_extrinsic = MagicMock(autospec=GenericExtrinsic) - result = utils.submit_extrinsic(mock_subtensor, mock_extrinsic, True, True) - assert result is True +from bittensor.utils.balance import Balance + + +def test_old_stake(subtensor, mocker): + wallet = mocker.Mock( + hotkey=mocker.Mock(ss58_address="HK1"), + coldkeypub=mocker.Mock(ss58_address="CK1"), + ) + + expected_stake = Balance.from_tao(100) + + hotkey_ss58s = ["HK1", "HK2"] + netuids = [3, 4] + all_stakes = [ + StakeInfo( + hotkey_ss58="HK1", + coldkey_ss58="CK1", + netuid=3, + stake=expected_stake, + locked=Balance.from_tao(10), + emission=Balance.from_tao(1), + drain=0, + is_registered=True, + ), + ] + + result = utils.get_old_stakes(wallet, hotkey_ss58s, netuids, all_stakes) + + assert result == [expected_stake, Balance.from_tao(0)] From 7fbfe8f11754622a83a89e1761e76006046b2c25 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 30 Apr 2025 17:33:09 -0700 Subject: [PATCH 060/251] fix tests --- tests/unit_tests/extrinsics/test__init__.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/tests/unit_tests/extrinsics/test__init__.py b/tests/unit_tests/extrinsics/test__init__.py index 1c087a781b..9eb994a2a3 100644 --- a/tests/unit_tests/extrinsics/test__init__.py +++ b/tests/unit_tests/extrinsics/test__init__.py @@ -4,12 +4,16 @@ def test_format_error_message_with_right_error_message(): - """Verify that error message from extrinsic response parses correctly.""" + """Verify that an error message from extrinsic response parses correctly.""" # Prep fake_error_message = { "type": "SomeType", "name": "SomeErrorName", - "docs": ["Some error description."], + "docs": [ + "Some error description.", + "I'm second part.", + "Hah, I'm the last one.", + ], } # Call @@ -17,13 +21,14 @@ def test_format_error_message_with_right_error_message(): # Assertions - assert "SomeType" in result - assert "SomeErrorName" in result - assert "Some error description." in result + assert ( + result + == "Subtensor returned `SomeErrorName(SomeType)` error. This means: `Some error description. I'm second part. Hah, I'm the last one.`." + ) def test_format_error_message_with_empty_error_message(): - """Verify that empty error message from extrinsic response parses correctly.""" + """Verify that an empty error message from extrinsic response parses correctly.""" # Prep fake_error_message = {} From 6dbf118e720caf251439ed44e41da1434db6ab8a Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 30 Apr 2025 17:35:23 -0700 Subject: [PATCH 061/251] refactoring in subtensors --- bittensor/core/async_subtensor.py | 5 ++--- bittensor/core/subtensor.py | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 422fb0a6e4..3f63ad35cb 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -58,12 +58,12 @@ publish_metadata, get_metadata, ) -from bittensor.core.extrinsics.asyncex.start_call import start_call_extrinsic from bittensor.core.extrinsics.asyncex.serving import serve_axon_extrinsic from bittensor.core.extrinsics.asyncex.staking import ( add_stake_extrinsic, add_stake_multiple_extrinsic, ) +from bittensor.core.extrinsics.asyncex.start_call import start_call_extrinsic from bittensor.core.extrinsics.asyncex.take import ( decrease_take_extrinsic, increase_take_extrinsic, @@ -135,7 +135,6 @@ def __init__( self.chain_endpoint, self.network = AsyncSubtensor.setup_config( network, self._config ) - self._mock = _mock self.log_verbose = log_verbose self._check_and_log_network_settings() @@ -229,7 +228,7 @@ async def encode_params( call_definition: dict[str, list["ParamWithTypes"]], params: Union[list[Any], dict[str, Any]], ) -> str: - """Returns a hex encoded string of the params using their types.""" + """Returns a hex-encoded string of the params using their types.""" param_data = scalecodec.ScaleBytes(b"") for i, param in enumerate(call_definition["params"]): diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 1268c481b6..c868a98a3b 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -61,12 +61,12 @@ get_metadata, serve_axon_extrinsic, ) -from bittensor.core.extrinsics.start_call import start_call_extrinsic from bittensor.core.extrinsics.set_weights import set_weights_extrinsic from bittensor.core.extrinsics.staking import ( add_stake_extrinsic, add_stake_multiple_extrinsic, ) +from bittensor.core.extrinsics.start_call import start_call_extrinsic from bittensor.core.extrinsics.take import ( decrease_take_extrinsic, increase_take_extrinsic, @@ -110,7 +110,7 @@ class Subtensor(SubtensorMixin): - """Thin layer for interacting with Substrate Interface. Mostly a collection of frequently-used calls.""" + """Thin layer for interacting with Substrate Interface. Mostly a collection of frequently used calls.""" def __init__( self, @@ -135,7 +135,6 @@ def __init__( config = self.config() self._config = copy.deepcopy(config) self.chain_endpoint, self.network = self.setup_config(network, self._config) - self._mock = _mock self.log_verbose = log_verbose self._check_and_log_network_settings() From 43ffc7a1e19873f21c986765654a233d8e7f2d95 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 30 Apr 2025 18:10:15 -0700 Subject: [PATCH 062/251] improve `async/subtensor.sign_and_send_extrinsic` --- bittensor/core/async_subtensor.py | 9 ++++++--- bittensor/core/subtensor.py | 12 +++++++----- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 422fb0a6e4..d8e7d56748 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -3216,7 +3216,7 @@ async def sign_and_send_extrinsic( wait_for_finalization: bool = False, sign_with: str = "coldkey", use_nonce: bool = False, - period: Optional[int] = None, + period: Optional[int] = 64, # 96 seconds for non-fast-blocks and 2 seconds for fast-blocks nonce_key: str = "hotkey", raise_error: bool = False, ) -> tuple[bool, str]: @@ -3230,9 +3230,12 @@ async def sign_and_send_extrinsic( wait_for_finalization (bool): whether to wait until the extrinsic call is finalized on the chain sign_with: the wallet's keypair to use for the signing. Options are "coldkey", "hotkey", "coldkeypub" use_nonce: unique identifier for the transaction related with hot/coldkey. - period: the period of the transaction as ERA part for transaction. Means how many blocks the transaction will be valid for. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. nonce_key: the type on nonce to use. Options are "hotkey" or "coldkey". - raise_error: raises relevant exception rather than returning `False` if unsuccessful. + nonce_key: the type on nonce to use. Options are "hotkey" or "coldkey". + raise_error: raises a relevant exception rather than returning `False` if unsuccessful. Returns: (success, error message) diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 1268c481b6..517f751ca0 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -2642,7 +2642,7 @@ def sign_and_send_extrinsic( wait_for_finalization: bool = False, sign_with: str = "coldkey", use_nonce: bool = False, - period: Optional[int] = None, + period: Optional[int] = 64, # 96 seconds for non-fast-blocks and 2 seconds for fast-blocks nonce_key: str = "hotkey", raise_error: bool = False, ) -> tuple[bool, str]: @@ -2654,11 +2654,13 @@ def sign_and_send_extrinsic( wallet (bittensor_wallet.Wallet): the wallet whose coldkey will be used to sign the extrinsic wait_for_inclusion (bool): whether to wait until the extrinsic call is included on the chain wait_for_finalization (bool): whether to wait until the extrinsic call is finalized on the chain - sign_with: the wallet's keypair to use for the signing. Options are "coldkey", "hotkey", "coldkeypub" - use_nonce: unique identifier for the transaction related with hot/coldkey. - period: the period of the transaction as ERA part for transaction. Means how many blocks the transaction will be valid for. + sign_with (str): the wallet's keypair to use for the signing. Options are "coldkey", "hotkey", "coldkeypub" + use_nonce (bool): unique identifier for the transaction related with hot/coldkey. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. nonce_key: the type on nonce to use. Options are "hotkey" or "coldkey". - raise_error: raises relevant exception rather than returning `False` if unsuccessful. + raise_error: raises a relevant exception rather than returning `False` if unsuccessful. Returns: (success, error message) From 4d2450bb80d044992fbb4193db5a65c907d67be6 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 30 Apr 2025 19:33:37 -0700 Subject: [PATCH 063/251] add extrinsics helper function `convert_and_normalize_weights_and_uids` --- bittensor/core/extrinsics/utils.py | 38 ++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/bittensor/core/extrinsics/utils.py b/bittensor/core/extrinsics/utils.py index 7cc2e3c4e0..4a58260936 100644 --- a/bittensor/core/extrinsics/utils.py +++ b/bittensor/core/extrinsics/utils.py @@ -1,12 +1,15 @@ """Module with helper functions for extrinsics.""" -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Union +import numpy as np from async_substrate_interface.errors import SubstrateRequestException +from numpy.typing import NDArray from bittensor.utils import format_error_message from bittensor.utils.balance import Balance from bittensor.utils.btlogging import logging +from bittensor.utils.weight_utils import convert_weights_and_uids_for_emit if TYPE_CHECKING: from bittensor_wallet import Wallet @@ -18,6 +21,7 @@ from bittensor.core.subtensor import Subtensor from bittensor.core.chain_data import StakeInfo from scalecodec.types import GenericExtrinsic + from bittensor.utils.registration import torch def submit_extrinsic( @@ -105,9 +109,9 @@ def get_old_stakes( """ Retrieve the previous staking balances for a wallet's hotkeys across given netuids. - This function searches through the provided staking data to find the stake amounts - for the specified hotkeys and netuids associated with the wallet's coldkey. If no match - is found for a particular hotkey and netuid combination, a default balance of zero is returned. + This function searches through the provided staking data to find the stake amounts for the specified hotkeys and + netuids associated with the wallet's coldkey. If no match is found for a particular hotkey and netuid combination, + a default balance of zero is returned. Args: wallet (Wallet): The wallet containing the coldkey to compare with stake data. @@ -129,3 +133,29 @@ def get_old_stakes( ) for hotkey_ss58, netuid in zip(hotkey_ss58s, netuids) ] + + +def convert_and_normalize_weights_and_uids( + uids: Union[NDArray[np.int64], "torch.LongTensor", list], + weights: Union[NDArray[np.float32], "torch.FloatTensor", list], +) -> tuple[list[int], list[int]]: + """Converts weights and uids to numpy arrays if they are not already. + + Arguments: + uids (Union[NDArray[np.int64], torch.LongTensor, list]): The ``uint64`` uids of destination neurons. + weights (Union[NDArray[np.float32], torch.FloatTensor, list]): The weights to set. These must be ``float`` s + and correspond to the passed ``uid`` s. + + Returns: + weight_uids, weight_vals: Bytes converted weights and uids + """ + if isinstance(uids, list): + uids = np.array(uids, dtype=np.int64) + if isinstance(weights, list): + weights = np.array(weights, dtype=np.float32) + + # Reformat and normalize. + weight_uids, weight_vals = convert_weights_and_uids_for_emit( + uids, weights + ) + return weight_uids, weight_vals From 463dd2aa62afbe37b3c1b2455cbc3f37ca99038c Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 30 Apr 2025 19:34:43 -0700 Subject: [PATCH 064/251] use `convert_and_normalize_weights_and_uids` in set_weights.py --- bittensor/core/extrinsics/set_weights.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/bittensor/core/extrinsics/set_weights.py b/bittensor/core/extrinsics/set_weights.py index 908fb2b2a9..7e5714afa3 100644 --- a/bittensor/core/extrinsics/set_weights.py +++ b/bittensor/core/extrinsics/set_weights.py @@ -5,8 +5,9 @@ import numpy as np from numpy.typing import NDArray +from bittensor.core.extrinsics.utils import convert_and_normalize_weights_and_uids from bittensor.core.settings import version_as_int -from bittensor.utils import format_error_message, weight_utils +from bittensor.utils import format_error_message from bittensor.utils.btlogging import logging if TYPE_CHECKING: @@ -60,7 +61,7 @@ def _do_set_weights( }, ) next_nonce = subtensor.substrate.get_account_next_index(wallet.hotkey.ss58_address) - # Period dictates how long the extrinsic will stay as part of waiting pool + # Period dictates how long the extrinsic will stay as part of the waiting pool extrinsic = subtensor.substrate.create_signed_extrinsic( call=call, keypair=wallet.hotkey, @@ -93,7 +94,7 @@ def set_weights_extrinsic( wait_for_finalization: bool = False, period: int = 5, ) -> tuple[bool, str]: - """Sets the given weights and values on chain for wallet hotkey account. + """Sets the given weights and values on a chain for a wallet hotkey account. Args: subtensor (bittensor.core.async_subtensor.AsyncSubtensor): Bittensor subtensor object. @@ -113,16 +114,14 @@ def set_weights_extrinsic( success (bool): Flag is ``True`` if extrinsic was finalized or included in the block. If we did not wait for finalization / inclusion, the response is ``True``. """ - # First convert types. + # Convert types. if isinstance(uids, list): uids = np.array(uids, dtype=np.int64) if isinstance(weights, list): weights = np.array(weights, dtype=np.float32) # Reformat and normalize. - weight_uids, weight_vals = weight_utils.convert_weights_and_uids_for_emit( - uids, weights - ) + weight_uids, weight_vals = convert_and_normalize_weights_and_uids(uids, weights) logging.info( f":satellite: [magenta]Setting weights on [/magenta][blue]{subtensor.network}[/blue] [magenta]...[/magenta]" From 58f3f151b8969526fd4dfe71592c4f1d4ccc816f Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 30 Apr 2025 19:35:22 -0700 Subject: [PATCH 065/251] use `convert_and_normalize_weights_and_uids` in weights.py --- bittensor/core/extrinsics/asyncex/weights.py | 29 ++++++-------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/bittensor/core/extrinsics/asyncex/weights.py b/bittensor/core/extrinsics/asyncex/weights.py index 6e07b90adb..62f9ea31f1 100644 --- a/bittensor/core/extrinsics/asyncex/weights.py +++ b/bittensor/core/extrinsics/asyncex/weights.py @@ -5,9 +5,8 @@ import numpy as np from numpy.typing import NDArray -import bittensor.utils.weight_utils as weight_utils +from bittensor.core.extrinsics.utils import convert_and_normalize_weights_and_uids from bittensor.core.settings import version_as_int -from bittensor.utils import format_error_message from bittensor.utils.btlogging import logging if TYPE_CHECKING: @@ -119,7 +118,7 @@ async def _do_reveal_weights( version_key: int, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, -) -> tuple[bool, Optional[dict]]: +) -> tuple[bool, str]: """ Internal method to send a transaction to the Bittensor blockchain, revealing the weights for a specific subnet. This method constructs and submits the transaction, handling retries and blockchain communication. @@ -137,7 +136,7 @@ async def _do_reveal_weights( wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. Returns: - tuple[bool, Optional[str]]: A tuple containing a success flag and an optional error message. + tuple[bool, str]: A tuple containing a success flag and an optional error message. This method ensures that the weight revelation is securely recorded on the Bittensor blockchain, providing transparency and accountability for the neuron's weight distribution. @@ -181,8 +180,7 @@ async def reveal_weights_extrinsic( This function is a wrapper around the `_do_reveal_weights` method. Args: - subtensor (bittensor.core.async_subtensor.AsyncSubtensor): The subtensor instance used for blockchain - interaction. + subtensor (bittensor.core.async_subtensor.AsyncSubtensor): The subtensor instance used for blockchain interaction. wallet (bittensor_wallet.Wallet): The wallet associated with the neuron revealing the weights. netuid (int): The unique identifier of the subnet. uids (list[int]): List of neuron UIDs for which weights are being revealed. @@ -217,7 +215,6 @@ async def reveal_weights_extrinsic( logging.info(success_message) return True, success_message - error_message = format_error_message(error_message) logging.error(f"Failed to reveal weights: {error_message}") return False, error_message @@ -232,7 +229,7 @@ async def _do_set_weights( wait_for_inclusion: bool = False, wait_for_finalization: bool = False, period: int = 5, -) -> tuple[bool, Optional[str]]: # (success, error_message) +) -> tuple[bool, str]: # (success, error_message) """ Internal method to send a transaction to the Bittensor blockchain, setting weights for specified neurons. This method constructs and submits the transaction, handling @@ -250,7 +247,7 @@ async def _do_set_weights( period (int, optional): The period in seconds to wait for extrinsic inclusion or finalization. Defaults to 5. Returns: - Tuple[bool, Optional[str]]: A tuple containing a success flag and an optional error message. + Tuple[bool, str]: A tuple containing a success flag and an optional error message. This method is vital for the dynamic weighting mechanism in Bittensor, where neurons adjust their trust in other neurons based on observed performance and contributions. @@ -289,7 +286,7 @@ async def set_weights_extrinsic( wait_for_finalization: bool = False, period: int = 5, ) -> tuple[bool, str]: - """Sets the given weights and values on chain for wallet hotkey account. + """Sets the given weights and values on a chain for a wallet hotkey account. Args: subtensor (bittensor.core.async_subtensor.AsyncSubtensor): Bittensor subtensor object. @@ -309,16 +306,8 @@ async def set_weights_extrinsic( success (bool): Flag is ``True`` if extrinsic was finalized or included in the block. If we did not wait for finalization / inclusion, the response is ``True``. """ - # First convert types. - if isinstance(uids, list): - uids = np.array(uids, dtype=np.int64) - if isinstance(weights, list): - weights = np.array(weights, dtype=np.float32) - - # Reformat and normalize. - weight_uids, weight_vals = weight_utils.convert_weights_and_uids_for_emit( - uids, weights - ) + + weight_uids, weight_vals = convert_and_normalize_weights_and_uids(uids, weights) logging.info( f":satellite: [magenta]Setting weights on [/magenta][blue]{subtensor.network}[/blue] [magenta]...[/magenta]" From e1543dba4f6c31c9ab44f77fc48a907602de4a84 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 30 Apr 2025 19:57:19 -0700 Subject: [PATCH 066/251] update commit_reveal extrinsics --- .../core/extrinsics/asyncex/commit_reveal.py | 44 +++++++++++-------- bittensor/core/extrinsics/commit_reveal.py | 41 ++++++++++------- 2 files changed, 49 insertions(+), 36 deletions(-) diff --git a/bittensor/core/extrinsics/asyncex/commit_reveal.py b/bittensor/core/extrinsics/asyncex/commit_reveal.py index 70c12fe0fb..7994bbbd76 100644 --- a/bittensor/core/extrinsics/asyncex/commit_reveal.py +++ b/bittensor/core/extrinsics/asyncex/commit_reveal.py @@ -6,9 +6,9 @@ from bittensor_drand import get_encrypted_commit from numpy.typing import NDArray +from bittensor.core.extrinsics.utils import convert_and_normalize_weights_and_uids from bittensor.core.settings import version_as_int from bittensor.utils.btlogging import logging -from bittensor.utils.weight_utils import convert_weights_and_uids_for_emit if TYPE_CHECKING: from bittensor_wallet import Wallet @@ -24,23 +24,26 @@ async def _do_commit_reveal_v3( reveal_round: int, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, -) -> tuple[bool, Optional[str]]: + period: Optional[int] = None, +) -> tuple[bool, str]: """ - Executes the commit-reveal phase 3 for a given netuid and commit, and optionally waits for extrinsic inclusion or - finalization. + Executes commit-reveal phase 3 for a given netuid and commit, and optionally waits for extrinsic inclusion or finalization. Arguments: subtensor: An instance of the AsyncSubtensor class. wallet: Wallet An instance of the Wallet class containing the user's keypair. netuid: int The network unique identifier. - commit bytes The commit data in bytes format. + commit: bytes The commit data in bytes format. reveal_round: int The round number for the reveal phase. wait_for_inclusion: bool, optional Flag indicating whether to wait for the extrinsic to be included in a block. wait_for_finalization: bool, optional Flag indicating whether to wait for the extrinsic to be finalized. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: - A tuple where the first element is a boolean indicating success or failure, and the second element is an - optional string containing error message if any. + A tuple where the first element is a boolean indicating success or failure, and the second element is a + string containing an error message if any. """ logging.info( f"Committing weights hash [blue]{commit.hex()}[/blue] for subnet #[blue]{netuid}[/blue] with " @@ -57,7 +60,12 @@ async def _do_commit_reveal_v3( }, ) return await subtensor.sign_and_send_extrinsic( - call, wallet, wait_for_inclusion, wait_for_finalization, sign_with="hotkey" + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + sign_with="hotkey", + period=period ) @@ -70,10 +78,11 @@ async def commit_reveal_v3_extrinsic( version_key: int = version_as_int, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, - block_time: float = 12.0, + block_time: Union[int, float] = 12.0, + period: Optional[int] = None, ) -> tuple[bool, str]: """ - Commits and reveals weights for given subtensor and wallet with provided uids and weights. + Commits and reveals weights for a given subtensor and wallet with provided uids and weights. Arguments: subtensor: The AsyncSubtensor instance. @@ -84,21 +93,17 @@ async def commit_reveal_v3_extrinsic( version_key: The version key to use for committing and revealing. Default is version_as_int. wait_for_inclusion: Whether to wait for the inclusion of the transaction. Default is False. wait_for_finalization: Whether to wait for the finalization of the transaction. Default is False. - block_time (float): The amount of seconds for block duration. Default is 12.0 seconds. + block_time (float): The number of seconds for block duration. Default is 12.0 seconds. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: tuple[bool, str]: A tuple where the first element is a boolean indicating success or failure, and the second element is a message associated with the result """ try: - # Convert uids and weights - if isinstance(uids, list): - uids = np.array(uids, dtype=np.int64) - if isinstance(weights, list): - weights = np.array(weights, dtype=np.float32) - - # Reformat and normalize. - uids, weights = convert_weights_and_uids_for_emit(uids, weights) + uids, weights = convert_and_normalize_weights_and_uids(uids, weights) current_block = await subtensor.substrate.get_block(None) subnet_hyperparameters = await subtensor.get_subnet_hyperparameters( @@ -127,6 +132,7 @@ async def commit_reveal_v3_extrinsic( reveal_round=reveal_round, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) if success is not True: diff --git a/bittensor/core/extrinsics/commit_reveal.py b/bittensor/core/extrinsics/commit_reveal.py index be66a71ea3..73e62aaf8f 100644 --- a/bittensor/core/extrinsics/commit_reveal.py +++ b/bittensor/core/extrinsics/commit_reveal.py @@ -2,13 +2,13 @@ from typing import Union, TYPE_CHECKING, Optional -from bittensor_drand import get_encrypted_commit import numpy as np +from bittensor_drand import get_encrypted_commit from numpy.typing import NDArray +from bittensor.core.extrinsics.utils import convert_and_normalize_weights_and_uids from bittensor.core.settings import version_as_int from bittensor.utils.btlogging import logging -from bittensor.utils.weight_utils import convert_weights_and_uids_for_emit if TYPE_CHECKING: from bittensor_wallet import Wallet @@ -24,23 +24,27 @@ def _do_commit_reveal_v3( reveal_round: int, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> tuple[bool, Optional[str]]: """ - Executes the commit-reveal phase 3 for a given netuid and commit, and optionally waits for extrinsic inclusion or + Executes commit-reveal phase 3 for a given netuid and commit, and optionally waits for extrinsic inclusion or finalization. Arguments: subtensor: An instance of the Subtensor class. wallet: Wallet An instance of the Wallet class containing the user's keypair. netuid: int The network unique identifier. - commit bytes The commit data in bytes format. + commit: bytes The commit data in bytes format. reveal_round: int The round number for the reveal phase. wait_for_inclusion: bool, optional Flag indicating whether to wait for the extrinsic to be included in a block. wait_for_finalization: bool, optional Flag indicating whether to wait for the extrinsic to be finalized. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: A tuple where the first element is a boolean indicating success or failure, and the second element is an - optional string containing error message if any. + optional string containing an error message if any. """ logging.info( f"Committing weights hash [blue]{commit.hex()}[/blue] for subnet #[blue]{netuid}[/blue] with " @@ -57,7 +61,12 @@ def _do_commit_reveal_v3( }, ) return subtensor.sign_and_send_extrinsic( - call, wallet, wait_for_inclusion, wait_for_finalization, sign_with="hotkey" + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + sign_with="hotkey", + period=period ) @@ -70,10 +79,11 @@ def commit_reveal_v3_extrinsic( version_key: int = version_as_int, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, - block_time: float = 12.0, + block_time: Union[int, float] = 12.0, + period: Optional[int] = None, ) -> tuple[bool, str]: """ - Commits and reveals weights for given subtensor and wallet with provided uids and weights. + Commits and reveals weights for a given subtensor and wallet with provided uids and weights. Arguments: subtensor: The Subtensor instance. @@ -84,21 +94,17 @@ def commit_reveal_v3_extrinsic( version_key: The version key to use for committing and revealing. Default is version_as_int. wait_for_inclusion: Whether to wait for the inclusion of the transaction. Default is False. wait_for_finalization: Whether to wait for the finalization of the transaction. Default is False. - block_time (float): The amount of seconds for block duration. Default is 12.0 seconds. + block_time (float): The number of seconds for block duration. Default is 12.0 seconds. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If the + transaction is not included in a block within that number of blocks, it will expire and be rejected. You can + think of it as an expiration date for the transaction. Returns: tuple[bool, str]: A tuple where the first element is a boolean indicating success or failure, and the second element is a message associated with the result """ try: - # Convert uids and weights - if isinstance(uids, list): - uids = np.array(uids, dtype=np.int64) - if isinstance(weights, list): - weights = np.array(weights, dtype=np.float32) - - # Reformat and normalize. - uids, weights = convert_weights_and_uids_for_emit(uids, weights) + uids, weights = convert_and_normalize_weights_and_uids(uids, weights) current_block = subtensor.get_current_block() subnet_hyperparameters = subtensor.get_subnet_hyperparameters( @@ -127,6 +133,7 @@ def commit_reveal_v3_extrinsic( reveal_round=reveal_round, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) if success is not True: From dbc23e3f1e943ab6669eb095880ec5d1a0da9436 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 30 Apr 2025 19:57:59 -0700 Subject: [PATCH 067/251] update `bittensor.core.subtensor.Subtensor.set_weights` call --- bittensor/core/subtensor.py | 46 ++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 517f751ca0..b5c83fbfba 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -3216,7 +3216,7 @@ def set_weights( wait_for_finalization: bool = False, max_retries: int = 5, block_time: float = 12.0, - period: int = 5, + period: int = 8, ) -> tuple[bool, str]: """ Sets the inter-neuronal weights for the specified neuron. This process involves specifying the influence or @@ -3224,27 +3224,26 @@ def set_weights( decentralized learning architecture. Arguments: - wallet (bittensor_wallet.Wallet): The wallet associated with the neuron setting the weights. - netuid (int): The unique identifier of the subnet. - uids (Union[NDArray[np.int64], torch.LongTensor, list]): The list of neuron UIDs that the weights are being - set for. - weights (Union[NDArray[np.float32], torch.FloatTensor, list]): The corresponding weights to be set for each - UID. - version_key (int): Version key for compatibility with the network. Default is int representation of - Bittensor version. - wait_for_inclusion (bool): Waits for the transaction to be included in a block. Default is ``False``. - wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. Default is - ``False``. - max_retries (int): The number of maximum attempts to set weights. Default is ``5``. - block_time (float): The amount of seconds for block duration. Default is 12.0 seconds. - period (int, optional): The period in seconds to wait for extrinsic inclusion or finalization. Defaults to 5. + wallet: The wallet associated with the neuron setting the weights. + netuid: The unique identifier of the subnet. + uids: The list of neuron UIDs that the weights are being set for. + weights: The corresponding weights to be set for each UID. + version_key: Version key for compatibility with the network. Default is int representation of a Bittensor version. + wait_for_inclusion: Waits for the transaction to be included in a block. Default is ``False``. + wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Default is ``False``. + max_retries: The number of maximum attempts to set weights. Default is ``5``. + block_time: The number of seconds for block duration. Default is 12.0 seconds. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: - tuple[bool, str]: ``True`` if the setting of weights is successful, False otherwise. And `msg`, a string - value describing the success or potential error. + tuple: + ``True`` if the setting of weights is successful, False otherwise. + `msg` is a string value describing the success or potential error. This function is crucial in shaping the network's collective intelligence, where each neuron's learning and - contribution are influenced by the weights it sets towards others【81†source】. + contribution are influenced by the weights it sets towards others. """ def _blocks_weight_limit() -> bool: @@ -3268,7 +3267,8 @@ def _blocks_weight_limit() -> bool: while retries < max_retries and success is False and _blocks_weight_limit(): logging.info( - f"Committing weights for subnet #{netuid}. Attempt {retries + 1} of {max_retries}." + f"Committing weights for subnet [blue]{netuid}[/blue]. " + f"Attempt [blue]{retries + 1}[blue] of [green]{max_retries}[/green]." ) success, message = commit_reveal_v3_extrinsic( subtensor=self, @@ -3280,17 +3280,18 @@ def _blocks_weight_limit() -> bool: wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, block_time=block_time, + period=period, ) retries += 1 return success, message else: - # go with classic `set weights extrinsic` + # go with classic `set_weights_extrinsic` while retries < max_retries and success is False and _blocks_weight_limit(): try: logging.info( - f"Setting weights for subnet #[blue]{netuid}[/blue]. " - f"Attempt [blue]{retries + 1} of {max_retries}[/blue]." + f"Setting weights for subnet [blue]{netuid}[/blue]. " + f"Attempt [blue]{retries + 1}[/blue] of [green]{max_retries}[/green]." ) success, message = set_weights_extrinsic( subtensor=self, @@ -3305,7 +3306,6 @@ def _blocks_weight_limit() -> bool: ) except Exception as e: logging.error(f"Error setting weights: {e}") - finally: retries += 1 return success, message From c88822f0b53db9a2aab43dd4f1521338a351fa5e Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 30 Apr 2025 19:58:59 -0700 Subject: [PATCH 068/251] update test related with `bittensor.core.subtensor.Subtensor.set_weights` call --- tests/unit_tests/test_subtensor.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index ca6aad000d..2774d7ca4e 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -1203,7 +1203,7 @@ def test_set_weights(subtensor, mocker, fake_wallet): version_key=settings.version_as_int, wait_for_inclusion=fake_wait_for_inclusion, wait_for_finalization=fake_wait_for_finalization, - period=5, + period=8, ) assert result == expected_result @@ -3144,6 +3144,7 @@ def test_set_weights_with_commit_reveal_enabled(subtensor, fake_wallet, mocker): wait_for_inclusion=fake_wait_for_inclusion, wait_for_finalization=fake_wait_for_finalization, block_time=12.0, + period=8, ) assert result == mocked_commit_reveal_v3_extrinsic.return_value From 1b98ea03fedab8ab5960f003b430df4e99663185 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 30 Apr 2025 21:31:39 -0700 Subject: [PATCH 069/251] increase set_weights' period until 16 to avoid flaky behavior --- bittensor/core/subtensor.py | 6 +++--- tests/e2e_tests/test_commit_reveal_v3.py | 2 +- tests/e2e_tests/test_commit_weights.py | 16 ++++++++-------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index b5c83fbfba..1090c4162b 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -3139,7 +3139,7 @@ def root_set_weights( wait_for_finalization: bool = False, ) -> bool: """ - Set weights for root network. + Set weights for the root network. Arguments: wallet (bittensor_wallet.Wallet): bittensor wallet instance. @@ -3216,7 +3216,7 @@ def set_weights( wait_for_finalization: bool = False, max_retries: int = 5, block_time: float = 12.0, - period: int = 8, + period: int = 16, ) -> tuple[bool, str]: """ Sets the inter-neuronal weights for the specified neuron. This process involves specifying the influence or @@ -3235,7 +3235,7 @@ def set_weights( block_time: The number of seconds for block duration. Default is 12.0 seconds. period (int): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. - You can think of it as an expiration date for the transaction. + You can think of it as an expiration date for the transaction. Default is 16. Returns: tuple: diff --git a/tests/e2e_tests/test_commit_reveal_v3.py b/tests/e2e_tests/test_commit_reveal_v3.py index 106af50a96..4eff2ce052 100644 --- a/tests/e2e_tests/test_commit_reveal_v3.py +++ b/tests/e2e_tests/test_commit_reveal_v3.py @@ -22,7 +22,7 @@ async def test_commit_and_reveal_weights_cr3(local_chain, subtensor, alice_walle Steps: 1. Register a subnet through Alice 2. Register Alice's neuron and add stake - 3. Enable commit-reveal mechanism on the subnet + 3. Enable a commit-reveal mechanism on subnet 4. Lower weights rate limit 5. Change the tempo for subnet 1 5. Commit weights and ensure they are committed. diff --git a/tests/e2e_tests/test_commit_weights.py b/tests/e2e_tests/test_commit_weights.py index 5015208a1b..6d77895e76 100644 --- a/tests/e2e_tests/test_commit_weights.py +++ b/tests/e2e_tests/test_commit_weights.py @@ -19,14 +19,14 @@ async def test_commit_and_reveal_weights_legacy(local_chain, subtensor, alice_wa Steps: 1. Register a subnet through Alice - 2. Enable commit-reveal mechanism on the subnet + 2. Enable a commit-reveal mechanism on subnet 3. Lower the commit_reveal interval and rate limit 4. Commit weights and verify 5. Wait interval & reveal weights and verify Raises: AssertionError: If any of the checks or verifications fail """ - netuid = 2 + netuid = subtensor.get_total_subnets() # 2 print("Testing test_commit_and_reveal_weights") @@ -153,12 +153,12 @@ async def test_commit_and_reveal_weights_legacy(local_chain, subtensor, alice_wa @pytest.mark.asyncio async def test_commit_weights_uses_next_nonce(local_chain, subtensor, alice_wallet): """ - Tests that committing weights doesn't re-use a nonce in the transaction pool. + Tests that committing weights doesn't re-use nonce in the transaction pool. Steps: 1. Register a subnet through Alice 2. Register Alice's neuron and add stake - 3. Enable commit-reveal mechanism on the subnet + 3. Enable a commit-reveal mechanism on subnet 4. Lower the commit_reveal interval and rate limit 5. Commit weights three times 6. Assert that all commits succeeded @@ -166,7 +166,7 @@ async def test_commit_weights_uses_next_nonce(local_chain, subtensor, alice_wall AssertionError: If any of the checks or verifications fail """ subnet_tempo = 50 - netuid = 2 + netuid = subtensor.get_total_subnets() # 2 # Wait for 2 tempos to pass as CR3 only reveals weights after 2 tempos subtensor.wait_for_block(subnet_tempo * 2 + 1) @@ -226,7 +226,7 @@ async def test_commit_weights_uses_next_nonce(local_chain, subtensor, alice_wall # wait while weights_rate_limit changes applied. subtensor.wait_for_block(subnet_tempo + 1) - # create different commited data to avoid coming into pool black list with the error + # Create different commited data to avoid coming into the pool's blacklist with the error # Failed to commit weights: Subtensor returned `Custom type(1012)` error. This means: `Transaction is temporarily # banned`.Failed to commit weights: Subtensor returned `Custom type(1012)` error. This means: `Transaction is # temporarily banned`.` @@ -245,7 +245,7 @@ def get_weights_and_salt(counter: int): f"{subtensor.substrate.get_account_next_index(alice_wallet.hotkey.ss58_address)}[/orange]" ) - # 3 time doing call if nonce wasn't updated, then raise error + # 3 time doing call if nonce wasn't updated, then raise the error @retry.retry(exceptions=Exception, tries=3, delay=1) @execute_and_wait_for_next_nonce(subtensor=subtensor, wallet=alice_wallet) def send_commit(salt_, weight_uids_, weight_vals_): @@ -260,7 +260,7 @@ def send_commit(salt_, weight_uids_, weight_vals_): ) assert success is True, message - # send some amount of commit weights + # Send some number of commit weights AMOUNT_OF_COMMIT_WEIGHTS = 3 for call in range(AMOUNT_OF_COMMIT_WEIGHTS): weight_uids, weight_vals, salt = get_weights_and_salt(call) From b0e5238365bc74ba629741567423edd2bb169332 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 30 Apr 2025 21:31:52 -0700 Subject: [PATCH 070/251] fix tests --- tests/unit_tests/test_subtensor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index 2774d7ca4e..f6bb50f619 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -1203,7 +1203,7 @@ def test_set_weights(subtensor, mocker, fake_wallet): version_key=settings.version_as_int, wait_for_inclusion=fake_wait_for_inclusion, wait_for_finalization=fake_wait_for_finalization, - period=8, + period=16, ) assert result == expected_result @@ -3144,7 +3144,7 @@ def test_set_weights_with_commit_reveal_enabled(subtensor, fake_wallet, mocker): wait_for_inclusion=fake_wait_for_inclusion, wait_for_finalization=fake_wait_for_finalization, block_time=12.0, - period=8, + period=16, ) assert result == mocked_commit_reveal_v3_extrinsic.return_value From e32638c0b016ef39f72181280a1b65d9f1ad4722 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 30 Apr 2025 21:40:02 -0700 Subject: [PATCH 071/251] fix commit reveal unit tests --- .../extrinsics/asyncex/test_commit_reveal.py | 10 ++++++---- tests/unit_tests/extrinsics/test_commit_reveal.py | 11 +++++++---- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/tests/unit_tests/extrinsics/asyncex/test_commit_reveal.py b/tests/unit_tests/extrinsics/asyncex/test_commit_reveal.py index 7802ccf9bb..2b0b1f596d 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_commit_reveal.py +++ b/tests/unit_tests/extrinsics/asyncex/test_commit_reveal.py @@ -163,7 +163,7 @@ async def test_commit_reveal_v3_extrinsic_success_with_torch( mocked_weights = mocker.Mock() mocked_convert_weights_and_uids_for_emit = mocker.patch.object( async_commit_reveal, - "convert_weights_and_uids_for_emit", + "convert_and_normalize_weights_and_uids", return_value=(mocked_uids, mocked_weights), ) mocked_get_subnet_reveal_period_epochs = mocker.patch.object( @@ -223,6 +223,7 @@ async def test_commit_reveal_v3_extrinsic_success_with_torch( reveal_round=fake_reveal_round, wait_for_inclusion=True, wait_for_finalization=True, + period=None ) @@ -238,7 +239,7 @@ async def test_commit_reveal_v3_extrinsic_success_with_numpy( mock_convert = mocker.patch.object( async_commit_reveal, - "convert_weights_and_uids_for_emit", + "convert_and_normalize_weights_and_uids", return_value=(fake_uids, fake_weights), ) mock_encode_drand = mocker.patch.object( @@ -288,7 +289,7 @@ async def test_commit_reveal_v3_extrinsic_response_false( # Mocks mocker.patch.object( async_commit_reveal, - "convert_weights_and_uids_for_emit", + "convert_and_normalize_weights_and_uids", return_value=(fake_uids, fake_weights), ) mocker.patch.object( @@ -328,6 +329,7 @@ async def test_commit_reveal_v3_extrinsic_response_false( reveal_round=fake_reveal_round, wait_for_inclusion=True, wait_for_finalization=True, + period=None ) @@ -341,7 +343,7 @@ async def test_commit_reveal_v3_extrinsic_exception(mocker, subtensor, fake_wall mocker.patch.object( async_commit_reveal, - "convert_weights_and_uids_for_emit", + "convert_and_normalize_weights_and_uids", side_effect=Exception("Test Error"), ) diff --git a/tests/unit_tests/extrinsics/test_commit_reveal.py b/tests/unit_tests/extrinsics/test_commit_reveal.py index 37b131e391..e254e88a0f 100644 --- a/tests/unit_tests/extrinsics/test_commit_reveal.py +++ b/tests/unit_tests/extrinsics/test_commit_reveal.py @@ -155,7 +155,7 @@ def test_commit_reveal_v3_extrinsic_success_with_torch( mocked_weights = mocker.Mock() mocked_convert_weights_and_uids_for_emit = mocker.patch.object( commit_reveal, - "convert_weights_and_uids_for_emit", + "convert_and_normalize_weights_and_uids", return_value=(mocked_uids, mocked_weights), ) mocker.patch.object(subtensor, "get_subnet_reveal_period_epochs") @@ -183,6 +183,7 @@ def test_commit_reveal_v3_extrinsic_success_with_torch( weights=fake_weights, wait_for_inclusion=True, wait_for_finalization=True, + period=None, ) # Asserts @@ -209,6 +210,7 @@ def test_commit_reveal_v3_extrinsic_success_with_torch( reveal_round=fake_reveal_round, wait_for_inclusion=True, wait_for_finalization=True, + period=None, ) @@ -223,7 +225,7 @@ def test_commit_reveal_v3_extrinsic_success_with_numpy( mock_convert = mocker.patch.object( commit_reveal, - "convert_weights_and_uids_for_emit", + "convert_and_normalize_weights_and_uids", return_value=(fake_uids, fake_weights), ) mock_encode_drand = mocker.patch.object( @@ -272,7 +274,7 @@ def test_commit_reveal_v3_extrinsic_response_false( # Mocks mocker.patch.object( commit_reveal, - "convert_weights_and_uids_for_emit", + "convert_and_normalize_weights_and_uids", return_value=(fake_uids, fake_weights), ) mocker.patch.object( @@ -312,6 +314,7 @@ def test_commit_reveal_v3_extrinsic_response_false( reveal_round=fake_reveal_round, wait_for_inclusion=True, wait_for_finalization=True, + period=None, ) @@ -324,7 +327,7 @@ def test_commit_reveal_v3_extrinsic_exception(mocker, subtensor, fake_wallet): mocker.patch.object( commit_reveal, - "convert_weights_and_uids_for_emit", + "convert_and_normalize_weights_and_uids", side_effect=Exception("Test Error"), ) From 71842ec093b62d4a8f214423942eb324256a2758 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 30 Apr 2025 21:41:54 -0700 Subject: [PATCH 072/251] set default period as None. Better to control this argument via each call --- bittensor/core/async_subtensor.py | 2 +- bittensor/core/subtensor.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index d8e7d56748..d7bee8895c 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -3216,7 +3216,7 @@ async def sign_and_send_extrinsic( wait_for_finalization: bool = False, sign_with: str = "coldkey", use_nonce: bool = False, - period: Optional[int] = 64, # 96 seconds for non-fast-blocks and 2 seconds for fast-blocks + period: Optional[int] = None, nonce_key: str = "hotkey", raise_error: bool = False, ) -> tuple[bool, str]: diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 1090c4162b..42e60c950a 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -2642,7 +2642,7 @@ def sign_and_send_extrinsic( wait_for_finalization: bool = False, sign_with: str = "coldkey", use_nonce: bool = False, - period: Optional[int] = 64, # 96 seconds for non-fast-blocks and 2 seconds for fast-blocks + period: Optional[int] = None, nonce_key: str = "hotkey", raise_error: bool = False, ) -> tuple[bool, str]: From 7225ddc6644dff6bef9b6c950677159771933d9e Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 30 Apr 2025 21:52:46 -0700 Subject: [PATCH 073/251] Add logic to control running localnet mode (non-/fast-blocks) to run e2e tests --- tests/e2e_tests/conftest.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/e2e_tests/conftest.py b/tests/e2e_tests/conftest.py index 0170cbd302..9eccd89f3f 100644 --- a/tests/e2e_tests/conftest.py +++ b/tests/e2e_tests/conftest.py @@ -57,7 +57,11 @@ def read_output(): def local_chain(request): """Determines whether to run the localnet.sh script in a subprocess or a Docker container.""" args = request.param if hasattr(request, "param") else None - params = "" if args is None else f"{args}" + + # passed env variable to control node mod (non-/fast-blocks) + non_fast_mode = os.getenv("NON_FAST_BLOCKS") == "1" + params = "" if args is None else f"{args}" + "True" if non_fast_mode is True else "False" + if shutil.which("docker") and not os.getenv("USE_DOCKER") == "0": yield from docker_runner(params) else: From 6aeadaea3e17069f75c435c1f6cd5374bd925979 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 30 Apr 2025 22:25:51 -0700 Subject: [PATCH 074/251] update `commit_weights` extrinsics calls --- bittensor/core/async_subtensor.py | 18 ++++++++++++------ bittensor/core/subtensor.py | 26 ++++++++++++++++++-------- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index d7bee8895c..6c58279425 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -3426,6 +3426,7 @@ async def commit_weights( wait_for_inclusion: bool = False, wait_for_finalization: bool = False, max_retries: int = 5, + period: Optional[int] = 16, ) -> tuple[bool, str]: """ Commits a hash of the neuron's weights to the Bittensor blockchain using the provided wallet. @@ -3438,15 +3439,19 @@ async def commit_weights( uids (np.ndarray): NumPy array of neuron UIDs for which weights are being committed. weights (np.ndarray): NumPy array of weight values corresponding to each UID. version_key (int): Version key for compatibility with the network. Default is ``int representation of - Bittensor version.``. + a Bittensor version.``. wait_for_inclusion (bool): Waits for the transaction to be included in a block. Default is ``False``. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. Default is ``False``. max_retries (int): The number of maximum attempts to commit weights. Default is ``5``. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: - tuple[bool, str]: ``True`` if the weight commitment is successful, False otherwise. And `msg`, a string - value describing the success or potential error. + tuple[bool, str]: + `True` if the weight commitment is successful, False otherwise. + `msg` is a string value describing the success or potential error. This function allows neurons to create a tamper-proof record of their weight distribution at a specific point in time, enhancing transparency and accountability within the Bittensor network. @@ -3456,8 +3461,9 @@ async def commit_weights( message = "No attempt made. Perhaps it is too soon to commit weights!" logging.info( - f"Committing weights with params: netuid={netuid}, uids={uids}, weights={weights}, " - f"version_key={version_key}" + f"Committing weights with params: " + f"netuid=[blue]{netuid}[/blue], uids=[blue]{uids}[/blue], weights=[blue]{weights}[/blue], " + f"version_key=[blue]{version_key}[/blue]" ) # Generate the hash of the weights @@ -3479,12 +3485,12 @@ async def commit_weights( commit_hash=commit_hash, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) if success: break except Exception as e: logging.error(f"Error committing weights: {e}") - finally: retries += 1 return success, message diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 42e60c950a..e45d032ae5 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -2509,8 +2509,11 @@ def wait_for_block(self, block: Optional[int] = None): bool: True if the target block was reached, False if timeout occurred. Example: - >>> subtensor.wait_for_block() # Waits for next block - >>> subtensor.wait_for_block(block=1234) # Waits for specific block + >>> import bittensor as bt + >>> subtensor = bt.subtensor(network="finney")) + + >>> subtensor.wait_for_block() # Waits for the next block + >>> subtensor.wait_for_block(block=1234) # Waits for a specific block """ def handler(block_data: dict): @@ -2519,6 +2522,7 @@ def handler(block_data: dict): ) if block_data["header"]["number"] >= target_block: return True + return False current_block = self.substrate.get_block() current_block_hash = current_block.get("header", {}).get("hash") @@ -2853,6 +2857,7 @@ def commit_weights( wait_for_inclusion: bool = False, wait_for_finalization: bool = False, max_retries: int = 5, + period: Optional[int] = 16, ) -> tuple[bool, str]: """ Commits a hash of the neuron's weights to the Bittensor blockchain using the provided wallet. @@ -2865,15 +2870,19 @@ def commit_weights( uids (np.ndarray): NumPy array of neuron UIDs for which weights are being committed. weights (np.ndarray): NumPy array of weight values corresponding to each UID. version_key (int): Version key for compatibility with the network. Default is ``int representation of - Bittensor version.``. + a Bittensor version.``. wait_for_inclusion (bool): Waits for the transaction to be included in a block. Default is ``False``. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. Default is ``False``. max_retries (int): The number of maximum attempts to commit weights. Default is ``5``. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: - tuple[bool, str]: ``True`` if the weight commitment is successful, False otherwise. And `msg`, a string - value describing the success or potential error. + tuple[bool, str]: + `True` if the weight commitment is successful, False otherwise. + `msg` is a string value describing the success or potential error. This function allows neurons to create a tamper-proof record of their weight distribution at a specific point in time, enhancing transparency and accountability within the Bittensor network. @@ -2883,8 +2892,9 @@ def commit_weights( message = "No attempt made. Perhaps it is too soon to commit weights!" logging.info( - f"Committing weights with params: netuid={netuid}, uids={uids}, weights={weights}, " - f"version_key={version_key}" + f"Committing weights with params: " + f"netuid=[blue]{netuid}[/blue], uids=[blue]{uids}[/blue], weights=[blue]{weights}[/blue], " + f"version_key=[blue]{version_key}[/blue]" ) # Generate the hash of the weights @@ -2906,12 +2916,12 @@ def commit_weights( commit_hash=commit_hash, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) if success: break except Exception as e: logging.error(f"Error committing weights: {e}") - finally: retries += 1 return success, message From fa9139bd068322ad7c7a86cc4520400248de5fdc Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 30 Apr 2025 22:26:11 -0700 Subject: [PATCH 075/251] update `commit_weights` extrinsics --- bittensor/core/extrinsics/asyncex/weights.py | 13 +++++++++++-- bittensor/core/extrinsics/commit_weights.py | 10 ++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/bittensor/core/extrinsics/asyncex/weights.py b/bittensor/core/extrinsics/asyncex/weights.py index 62f9ea31f1..0a6bf0b9ce 100644 --- a/bittensor/core/extrinsics/asyncex/weights.py +++ b/bittensor/core/extrinsics/asyncex/weights.py @@ -22,19 +22,22 @@ async def _do_commit_weights( commit_hash: str, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> tuple[bool, Optional[str]]: """ Internal method to send a transaction to the Bittensor blockchain, committing the hash of a neuron's weights. This method constructs and submits the transaction, handling retries and blockchain communication. Args: - subtensor (bittensor.core.async_subtensor.AsyncSubtensor): The subtensor instance used for blockchain - interaction. + subtensor (bittensor.core.async_subtensor.AsyncSubtensor): The subtensor instance used for blockchain interaction. wallet (bittensor_wallet.Wallet): The wallet associated with the neuron committing the weights. netuid (int): The unique identifier of the subnet. commit_hash (str): The hash of the neuron's weights to be committed. wait_for_inclusion (bool): Waits for the transaction to be included in a block. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: tuple[bool, Optional[str]]: A tuple containing a success flag and an optional error message. @@ -56,6 +59,7 @@ async def _do_commit_weights( wait_for_inclusion, wait_for_finalization, use_nonce=True, + period=period, nonce_key="hotkey", sign_with="hotkey", ) @@ -68,6 +72,7 @@ async def commit_weights_extrinsic( commit_hash: str, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> tuple[bool, str]: """ Commits a hash of the neuron's weights to the Bittensor blockchain using the provided wallet. @@ -81,6 +86,9 @@ async def commit_weights_extrinsic( commit_hash (str): The hash of the neuron's weights to be committed. wait_for_inclusion (bool): Waits for the transaction to be included in a block. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: tuple[bool, str]: ``True`` if the weight commitment is successful, False otherwise. And `msg`, a string @@ -97,6 +105,7 @@ async def commit_weights_extrinsic( commit_hash=commit_hash, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) if success: diff --git a/bittensor/core/extrinsics/commit_weights.py b/bittensor/core/extrinsics/commit_weights.py index 06e3295eb5..4b077eac30 100644 --- a/bittensor/core/extrinsics/commit_weights.py +++ b/bittensor/core/extrinsics/commit_weights.py @@ -17,6 +17,7 @@ def _do_commit_weights( commit_hash: str, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> tuple[bool, Optional[str]]: """ Internal method to send a transaction to the Bittensor blockchain, committing the hash of a neuron's weights. @@ -29,6 +30,9 @@ def _do_commit_weights( commit_hash (str): The hash of the neuron's weights to be committed. wait_for_inclusion (bool): Waits for the transaction to be included in a block. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: tuple[bool, Optional[str]]: A tuple containing a success flag and an optional error message. @@ -50,6 +54,7 @@ def _do_commit_weights( wait_for_inclusion, wait_for_finalization, use_nonce=True, + period=period, sign_with="hotkey", nonce_key="hotkey", ) @@ -62,6 +67,7 @@ def commit_weights_extrinsic( commit_hash: str, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> tuple[bool, str]: """ Commits a hash of the neuron's weights to the Bittensor blockchain using the provided wallet. @@ -74,6 +80,9 @@ def commit_weights_extrinsic( commit_hash (str): The hash of the neuron's weights to be committed. wait_for_inclusion (bool): Waits for the transaction to be included in a block. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: tuple[bool, str]: ``True`` if the weight commitment is successful, False otherwise. And `msg`, a string @@ -90,6 +99,7 @@ def commit_weights_extrinsic( commit_hash=commit_hash, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) if success: From cf2a8cc70399cdf6e708d9b6207fba91e7cce90b Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 30 Apr 2025 23:27:17 -0700 Subject: [PATCH 076/251] fix fast_blocks logic in conftest.py --- tests/e2e_tests/conftest.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/e2e_tests/conftest.py b/tests/e2e_tests/conftest.py index 9eccd89f3f..60d939fd86 100644 --- a/tests/e2e_tests/conftest.py +++ b/tests/e2e_tests/conftest.py @@ -59,8 +59,8 @@ def local_chain(request): args = request.param if hasattr(request, "param") else None # passed env variable to control node mod (non-/fast-blocks) - non_fast_mode = os.getenv("NON_FAST_BLOCKS") == "1" - params = "" if args is None else f"{args}" + "True" if non_fast_mode is True else "False" + fast_blocks = "True" if (os.getenv("FAST_BLOCKS") == "1") is True else "False" + params = f"{fast_blocks}" if args is None else f"{fast_blocks} {args} " if shutil.which("docker") and not os.getenv("USE_DOCKER") == "0": yield from docker_runner(params) @@ -69,7 +69,7 @@ def local_chain(request): if sys.platform.startswith("linux"): docker_command = ( "Install docker with command " - "[blue]sudo apt-get update && sudo apt-get install docker.io -y[/blue]" + "[blue]sudo apt-gat update && sudo apt-get install docker.io -y[/blue]" " or use documentation [blue]https://docs.docker.com/engine/install/[/blue]" ) elif sys.platform == "darwin": @@ -95,7 +95,7 @@ def legacy_runner(params): logging.warning("LOCALNET_SH_PATH env variable is not set, e2e test skipped.") pytest.skip("LOCALNET_SH_PATH environment variable is not set.") - # Check if param is None, and handle it accordingly + # Check if param is None and handle it accordingly args = "" if params is None else f"{params}" # Compile commands to send to process From 1751d481e62dffe87fbea7b2965a74f0c0e4e268 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 30 Apr 2025 23:27:49 -0700 Subject: [PATCH 077/251] fix wait_for_block logic in subtensors --- bittensor/core/async_subtensor.py | 7 ++++--- bittensor/core/subtensor.py | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 6c58279425..cf4d6f62e1 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -3024,7 +3024,7 @@ async def tx_rate_limit( Optional[int]: The transaction rate limit of the network, None if not available. The transaction rate limit is an essential parameter for ensuring the stability and scalability of the Bittensor - network. It helps in managing network load and preventing congestion, thereby maintaining efficient and + network. It helps in managing a network load and preventing congestion, thereby maintaining efficient and timely transaction processing. """ block_hash = await self.determine_block_hash(block, block_hash, reuse_block) @@ -3039,14 +3039,14 @@ async def wait_for_block(self, block: Optional[int] = None): waits for the next block. Args: - block (Optional[int]): The block number to wait for. If None, waits for next block. + block (Optional[int]): The block number to wait for. If None, waits for the next block. Returns: bool: True if the target block was reached, False if timeout occurred. Example: await subtensor.wait_for_block() # Waits for next block - await subtensor.wait_for_block(block=1234) # Waits for specific block + await subtensor.wait_for_block(block=1234) # Waits for a specific block """ async def handler(block_data: dict): @@ -3055,6 +3055,7 @@ async def handler(block_data: dict): ) if block_data["header"]["number"] >= target_block: return True + return None current_block = await self.substrate.get_block() current_block_hash = current_block.get("header", {}).get("hash") diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index e45d032ae5..618c89b3a2 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -2503,7 +2503,7 @@ def wait_for_block(self, block: Optional[int] = None): waits for the next block. Args: - block (Optional[int]): The block number to wait for. If None, waits for next block. + block (Optional[int]): The block number to wait for. If None, waits for the next block. Returns: bool: True if the target block was reached, False if timeout occurred. @@ -2522,7 +2522,7 @@ def handler(block_data: dict): ) if block_data["header"]["number"] >= target_block: return True - return False + return None current_block = self.substrate.get_block() current_block_hash = current_block.get("header", {}).get("hash") From e8ab07bbdcc2198d6f7c06cc851206ef4b5ad83d Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 30 Apr 2025 23:40:43 -0700 Subject: [PATCH 078/251] update docstrings --- bittensor/core/extrinsics/asyncex/weights.py | 34 ++++++++++++-------- bittensor/core/extrinsics/commit_weights.py | 24 ++++++++------ 2 files changed, 36 insertions(+), 22 deletions(-) diff --git a/bittensor/core/extrinsics/asyncex/weights.py b/bittensor/core/extrinsics/asyncex/weights.py index 0a6bf0b9ce..ceb519311a 100644 --- a/bittensor/core/extrinsics/asyncex/weights.py +++ b/bittensor/core/extrinsics/asyncex/weights.py @@ -23,7 +23,7 @@ async def _do_commit_weights( wait_for_inclusion: bool = False, wait_for_finalization: bool = False, period: Optional[int] = None, -) -> tuple[bool, Optional[str]]: +) -> tuple[bool, str]: """ Internal method to send a transaction to the Bittensor blockchain, committing the hash of a neuron's weights. This method constructs and submits the transaction, handling retries and blockchain communication. @@ -40,7 +40,9 @@ async def _do_commit_weights( You can think of it as an expiration date for the transaction. Returns: - tuple[bool, Optional[str]]: A tuple containing a success flag and an optional error message. + tuple[bool, str]: + `True` if the weight commitment is successful, `False` otherwise. + `msg` is a string value describing the success or potential error. This method ensures that the weight commitment is securely recorded on the Bittensor blockchain, providing a verifiable record of the neuron's weight distribution at a specific point in time. @@ -91,8 +93,9 @@ async def commit_weights_extrinsic( You can think of it as an expiration date for the transaction. Returns: - tuple[bool, str]: ``True`` if the weight commitment is successful, False otherwise. And `msg`, a string - value describing the success or potential error. + tuple[bool, str]: + `True` if the weight commitment is successful, `False` otherwise. + `msg` is a string value describing the success or potential error. This function provides a user-friendly interface for committing weights to the Bittensor blockchain, ensuring proper error handling and user interaction when required. @@ -145,7 +148,9 @@ async def _do_reveal_weights( wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. Returns: - tuple[bool, str]: A tuple containing a success flag and an optional error message. + tuple[bool, str]: + `True` if the weight commitment is successful, `False` otherwise. + `msg` is a string value describing the success or potential error. This method ensures that the weight revelation is securely recorded on the Bittensor blockchain, providing transparency and accountability for the neuron's weight distribution. @@ -200,8 +205,9 @@ async def reveal_weights_extrinsic( wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. Returns: - tuple[bool, str]: ``True`` if the weight revelation is successful, False otherwise. And `msg`, a string value - describing the success or potential error. + tuple[bool, str]: + `True` if the weight commitment is successful, `False` otherwise. + `msg` is a string value describing the success or potential error. This function provides a user-friendly interface for revealing weights on the Bittensor blockchain, ensuring proper error handling and user interaction when required. @@ -240,9 +246,8 @@ async def _do_set_weights( period: int = 5, ) -> tuple[bool, str]: # (success, error_message) """ - Internal method to send a transaction to the Bittensor blockchain, setting weights - for specified neurons. This method constructs and submits the transaction, handling - retries and blockchain communication. + Internal method to send a transaction to the Bittensor blockchain, setting weights for specified neurons. This + method constructs and submits the transaction, handling retries and blockchain communication. Args: subtensor (subtensor.core.async_subtensor.AsyncSubtensor): Async Subtensor instance. @@ -256,7 +261,9 @@ async def _do_set_weights( period (int, optional): The period in seconds to wait for extrinsic inclusion or finalization. Defaults to 5. Returns: - Tuple[bool, str]: A tuple containing a success flag and an optional error message. + tuple[bool, str]: + `True` if the weight commitment is successful, `False` otherwise. + `msg` is a string value describing the success or potential error. This method is vital for the dynamic weighting mechanism in Bittensor, where neurons adjust their trust in other neurons based on observed performance and contributions. @@ -312,8 +319,9 @@ async def set_weights_extrinsic( period (int, optional): The period in seconds to wait for extrinsic inclusion or finalization. Defaults to 5. Returns: - success (bool): Flag is ``True`` if extrinsic was finalized or included in the block. If we did not wait for - finalization / inclusion, the response is ``True``. + tuple[bool, str]: + `True` if the weight commitment is successful, `False` otherwise. + `msg` is a string value describing the success or potential error. """ weight_uids, weight_vals = convert_and_normalize_weights_and_uids(uids, weights) diff --git a/bittensor/core/extrinsics/commit_weights.py b/bittensor/core/extrinsics/commit_weights.py index 4b077eac30..3f8e2c2a6c 100644 --- a/bittensor/core/extrinsics/commit_weights.py +++ b/bittensor/core/extrinsics/commit_weights.py @@ -18,7 +18,7 @@ def _do_commit_weights( wait_for_inclusion: bool = False, wait_for_finalization: bool = False, period: Optional[int] = None, -) -> tuple[bool, Optional[str]]: +) -> tuple[bool, str]: """ Internal method to send a transaction to the Bittensor blockchain, committing the hash of a neuron's weights. This method constructs and submits the transaction, handling retries and blockchain communication. @@ -35,7 +35,9 @@ def _do_commit_weights( You can think of it as an expiration date for the transaction. Returns: - tuple[bool, Optional[str]]: A tuple containing a success flag and an optional error message. + tuple[bool, str]: + `True` if the weight commitment is successful, `False` otherwise. + `msg` is a string value describing the success or potential error. This method ensures that the weight commitment is securely recorded on the Bittensor blockchain, providing a verifiable record of the neuron's weight distribution at a specific point in time. @@ -85,8 +87,9 @@ def commit_weights_extrinsic( You can think of it as an expiration date for the transaction. Returns: - tuple[bool, str]: ``True`` if the weight commitment is successful, False otherwise. And `msg`, a string - value describing the success or potential error. + tuple[bool, str]: + `True` if the weight commitment is successful, `False` otherwise. + `msg` is a string value describing the success or potential error. This function provides a user-friendly interface for committing weights to the Bittensor blockchain, ensuring proper error handling and user interaction when required. @@ -121,7 +124,7 @@ def _do_reveal_weights( version_key: int, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, -) -> tuple[bool, Optional[dict]]: +) -> tuple[bool, str]: """ Internal method to send a transaction to the Bittensor blockchain, revealing the weights for a specific subnet. This method constructs and submits the transaction, handling retries and blockchain communication. @@ -138,7 +141,9 @@ def _do_reveal_weights( wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. Returns: - tuple[bool, Optional[str]]: A tuple containing a success flag and an optional error message. + tuple[bool, str]: + `True` if the weight commitment is successful, `False` otherwise. + `msg` is a string value describing the success or potential error. This method ensures that the weight revelation is securely recorded on the Bittensor blockchain, providing transparency and accountability for the neuron's weight distribution. @@ -193,8 +198,9 @@ def reveal_weights_extrinsic( wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. Returns: - tuple[bool, str]: ``True`` if the weight revelation is successful, False otherwise. And `msg`, a string value - describing the success or potential error. + tuple[bool, str]: + `True` if the weight commitment is successful, `False` otherwise. + `msg` is a string value describing the success or potential error. This function provides a user-friendly interface for revealing weights on the Bittensor blockchain, ensuring proper error handling and user interaction when required. @@ -217,6 +223,6 @@ def reveal_weights_extrinsic( logging.info(success_message) return True, success_message - error_message = format_error_message(error_message) + error_message = error_message logging.error(f"Failed to reveal weights: {error_message}") return False, error_message From 0213d0314118655ded79cbf230d27beff2668dd3 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 30 Apr 2025 23:53:10 -0700 Subject: [PATCH 079/251] keep fast-blocks mode by default --- tests/e2e_tests/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e_tests/conftest.py b/tests/e2e_tests/conftest.py index 60d939fd86..a247b85a5a 100644 --- a/tests/e2e_tests/conftest.py +++ b/tests/e2e_tests/conftest.py @@ -59,7 +59,7 @@ def local_chain(request): args = request.param if hasattr(request, "param") else None # passed env variable to control node mod (non-/fast-blocks) - fast_blocks = "True" if (os.getenv("FAST_BLOCKS") == "1") is True else "False" + fast_blocks = "False" if (os.getenv("FAST_BLOCKS") == "0") is True else "True" params = f"{fast_blocks}" if args is None else f"{fast_blocks} {args} " if shutil.which("docker") and not os.getenv("USE_DOCKER") == "0": From 73e9fb25f0e75845f689aa6296b342a2516fc2bf Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 30 Apr 2025 23:59:46 -0700 Subject: [PATCH 080/251] update `reveal_weights_extrinsic` for both subtensors --- bittensor/core/extrinsics/asyncex/weights.py | 10 ++++++++++ bittensor/core/extrinsics/commit_weights.py | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/bittensor/core/extrinsics/asyncex/weights.py b/bittensor/core/extrinsics/asyncex/weights.py index ceb519311a..508703b31a 100644 --- a/bittensor/core/extrinsics/asyncex/weights.py +++ b/bittensor/core/extrinsics/asyncex/weights.py @@ -130,6 +130,7 @@ async def _do_reveal_weights( version_key: int, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> tuple[bool, str]: """ Internal method to send a transaction to the Bittensor blockchain, revealing the weights for a specific subnet. @@ -146,6 +147,9 @@ async def _do_reveal_weights( version_key (int): Version key for compatibility with the network. wait_for_inclusion (bool): Waits for the transaction to be included in a block. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: tuple[bool, str]: @@ -173,6 +177,7 @@ async def _do_reveal_weights( wait_for_inclusion, wait_for_finalization, sign_with="hotkey", + period=period, nonce_key="hotkey", use_nonce=True, ) @@ -188,6 +193,7 @@ async def reveal_weights_extrinsic( version_key: int, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> tuple[bool, str]: """ Reveals the weights for a specific subnet on the Bittensor blockchain using the provided wallet. @@ -203,6 +209,9 @@ async def reveal_weights_extrinsic( version_key (int): Version key for compatibility with the network. wait_for_inclusion (bool): Waits for the transaction to be included in a block. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: tuple[bool, str]: @@ -223,6 +232,7 @@ async def reveal_weights_extrinsic( version_key=version_key, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) if success: diff --git a/bittensor/core/extrinsics/commit_weights.py b/bittensor/core/extrinsics/commit_weights.py index 3f8e2c2a6c..14223c2e1c 100644 --- a/bittensor/core/extrinsics/commit_weights.py +++ b/bittensor/core/extrinsics/commit_weights.py @@ -124,6 +124,7 @@ def _do_reveal_weights( version_key: int, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> tuple[bool, str]: """ Internal method to send a transaction to the Bittensor blockchain, revealing the weights for a specific subnet. @@ -139,6 +140,9 @@ def _do_reveal_weights( version_key (int): Version key for compatibility with the network. wait_for_inclusion (bool): Waits for the transaction to be included in a block. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: tuple[bool, str]: @@ -166,6 +170,7 @@ def _do_reveal_weights( wait_for_inclusion, wait_for_finalization, use_nonce=True, + period=period, sign_with="hotkey", nonce_key="hotkey", ) @@ -181,6 +186,7 @@ def reveal_weights_extrinsic( version_key: int, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> tuple[bool, str]: """ Reveals the weights for a specific subnet on the Bittensor blockchain using the provided wallet. @@ -196,6 +202,9 @@ def reveal_weights_extrinsic( version_key (int): Version key for compatibility with the network. wait_for_inclusion (bool): Waits for the transaction to be included in a block. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: tuple[bool, str]: @@ -216,6 +225,7 @@ def reveal_weights_extrinsic( version_key=version_key, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) if success: From c8e7e99b190d4f398580f5715bb8ab8ad0fca083 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 00:01:52 -0700 Subject: [PATCH 081/251] update `Subtensor.reveal_weights` extrinsic call --- bittensor/core/subtensor.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 618c89b3a2..4b9ad102e4 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -3062,6 +3062,7 @@ def reveal_weights( wait_for_inclusion: bool = False, wait_for_finalization: bool = False, max_retries: int = 5, + period: Optional[int] = 16, ) -> tuple[bool, str]: """ Reveals the weights for a specific subnet on the Bittensor blockchain using the provided wallet. @@ -3074,11 +3075,14 @@ def reveal_weights( weights (np.ndarray): NumPy array of weight values corresponding to each UID. salt (np.ndarray): NumPy array of salt values corresponding to the hash function. version_key (int): Version key for compatibility with the network. Default is ``int representation of - Bittensor version``. + a Bittensor version``. wait_for_inclusion (bool): Waits for the transaction to be included in a block. Default is ``False``. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. Default is ``False``. max_retries (int): The number of maximum attempts to reveal weights. Default is ``5``. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: tuple[bool, str]: ``True`` if the weight revelation is successful, False otherwise. And `msg`, a string @@ -3103,12 +3107,12 @@ def reveal_weights( version_key=version_key, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) if success: break except Exception as e: logging.error(f"Error revealing weights: {e}") - finally: retries += 1 return success, message From 422eddf8ef93316af9014f7f490cfa6d83312730 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 00:08:56 -0700 Subject: [PATCH 082/251] fix related unit tests --- tests/unit_tests/extrinsics/asyncex/test_weights.py | 2 ++ tests/unit_tests/test_async_subtensor.py | 1 + tests/unit_tests/test_subtensor.py | 2 ++ 3 files changed, 5 insertions(+) diff --git a/tests/unit_tests/extrinsics/asyncex/test_weights.py b/tests/unit_tests/extrinsics/asyncex/test_weights.py index c01490055d..e02f006e9b 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_weights.py +++ b/tests/unit_tests/extrinsics/asyncex/test_weights.py @@ -437,6 +437,7 @@ async def test_commit_weights_extrinsic_success(subtensor, fake_wallet, mocker): commit_hash=fake_commit_hash, wait_for_inclusion=True, wait_for_finalization=True, + period=None, ) assert result is True assert message == "✅ [green]Successfully committed weights.[green]" @@ -471,6 +472,7 @@ async def test_commit_weights_extrinsic_failure(subtensor, fake_wallet, mocker): commit_hash=fake_commit_hash, wait_for_inclusion=True, wait_for_finalization=True, + period=None, ) assert result is False assert message == "Commit failed." diff --git a/tests/unit_tests/test_async_subtensor.py b/tests/unit_tests/test_async_subtensor.py index bb6bb5b49f..c0fd7dbcc5 100644 --- a/tests/unit_tests/test_async_subtensor.py +++ b/tests/unit_tests/test_async_subtensor.py @@ -2853,6 +2853,7 @@ async def test_commit_weights_success(subtensor, fake_wallet, mocker): commit_hash="fake_commit_hash", wait_for_inclusion=False, wait_for_finalization=False, + period=16, ) assert result is True assert message == "Success" diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index f6bb50f619..c1ef86bbd2 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -1963,6 +1963,7 @@ def test_commit_weights(subtensor, fake_wallet, mocker): commit_hash=mocked_generate_weight_hash.return_value, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=16 ) assert result == expected_result @@ -2002,6 +2003,7 @@ def test_reveal_weights(subtensor, fake_wallet, mocker): salt=salt, wait_for_inclusion=False, wait_for_finalization=False, + period=16 ) From 662767ca5e4663ff21f3942f30e678025a0ea80f Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 00:11:14 -0700 Subject: [PATCH 083/251] fmt --- bittensor/core/extrinsics/asyncex/weights.py | 5 ----- bittensor/core/extrinsics/commit_weights.py | 1 - 2 files changed, 6 deletions(-) diff --git a/bittensor/core/extrinsics/asyncex/weights.py b/bittensor/core/extrinsics/asyncex/weights.py index 508703b31a..33c0e98b9d 100644 --- a/bittensor/core/extrinsics/asyncex/weights.py +++ b/bittensor/core/extrinsics/asyncex/weights.py @@ -100,7 +100,6 @@ async def commit_weights_extrinsic( This function provides a user-friendly interface for committing weights to the Bittensor blockchain, ensuring proper error handling and user interaction when required. """ - success, error_message = await _do_commit_weights( subtensor=subtensor, wallet=wallet, @@ -159,7 +158,6 @@ async def _do_reveal_weights( This method ensures that the weight revelation is securely recorded on the Bittensor blockchain, providing transparency and accountability for the neuron's weight distribution. """ - call = await subtensor.substrate.compose_call( call_module="SubtensorModule", call_function="reveal_weights", @@ -221,7 +219,6 @@ async def reveal_weights_extrinsic( This function provides a user-friendly interface for revealing weights on the Bittensor blockchain, ensuring proper error handling and user interaction when required. """ - success, error_message = await _do_reveal_weights( subtensor=subtensor, wallet=wallet, @@ -278,7 +275,6 @@ async def _do_set_weights( This method is vital for the dynamic weighting mechanism in Bittensor, where neurons adjust their trust in other neurons based on observed performance and contributions. """ - call = await subtensor.substrate.compose_call( call_module="SubtensorModule", call_function="set_weights", @@ -333,7 +329,6 @@ async def set_weights_extrinsic( `True` if the weight commitment is successful, `False` otherwise. `msg` is a string value describing the success or potential error. """ - weight_uids, weight_vals = convert_and_normalize_weights_and_uids(uids, weights) logging.info( diff --git a/bittensor/core/extrinsics/commit_weights.py b/bittensor/core/extrinsics/commit_weights.py index 14223c2e1c..c07878ac16 100644 --- a/bittensor/core/extrinsics/commit_weights.py +++ b/bittensor/core/extrinsics/commit_weights.py @@ -2,7 +2,6 @@ from typing import TYPE_CHECKING, Optional -from bittensor.utils import format_error_message from bittensor.utils.btlogging import logging if TYPE_CHECKING: From 84e71089327b42eb8bd1329b5d50f3255da85cdf Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 00:16:41 -0700 Subject: [PATCH 084/251] bittensor.core.extrinsics.move_stake.transfer_stake_extrinsic --- bittensor/core/extrinsics/move_stake.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/bittensor/core/extrinsics/move_stake.py b/bittensor/core/extrinsics/move_stake.py index fdf9d406a8..3a1366b49b 100644 --- a/bittensor/core/extrinsics/move_stake.py +++ b/bittensor/core/extrinsics/move_stake.py @@ -17,6 +17,7 @@ def _get_stake_in_origin_and_dest( origin_netuid: int, destination_netuid: int, ) -> tuple[Balance, Balance]: + """Gets the current stake balances for both origin and destination addresses in their respective subnets.""" block = subtensor.get_current_block() stake_in_origin = subtensor.get_stake( coldkey_ss58=origin_coldkey_ss58, @@ -43,6 +44,7 @@ def transfer_stake_extrinsic( amount: Optional[Balance] = None, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> bool: """ Transfers stake from one subnet to another while changing the coldkey owner. @@ -57,6 +59,9 @@ def transfer_stake_extrinsic( amount (Union[Balance, float, int]): Amount to transfer. wait_for_inclusion (bool): If true, waits for inclusion before returning. wait_for_finalization (bool): If true, waits for finalization before returning. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: success (bool): True if the transfer was successful. @@ -113,6 +118,7 @@ def transfer_stake_extrinsic( wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) if success: From 81a5669169c73e50ec0a76e03c3bd9e35cbb8c12 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 00:16:51 -0700 Subject: [PATCH 085/251] bittensor.core.subtensor.Subtensor.transfer_stake --- bittensor/core/subtensor.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 4b9ad102e4..4c44d75ebc 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -3499,6 +3499,7 @@ def transfer_stake( amount: Balance, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> bool: """ Transfers stake from one subnet to another while changing the coldkey owner. @@ -3512,6 +3513,9 @@ def transfer_stake( amount (Union[Balance, float, int]): Amount to transfer. wait_for_inclusion (bool): If true, waits for inclusion before returning. wait_for_finalization (bool): If true, waits for finalization before returning. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: success (bool): True if the transfer was successful. @@ -3527,6 +3531,7 @@ def transfer_stake( amount=amount, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) def unstake( From 6dc3e0e2bbdfcb18d2867480f09b72652500993f Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 00:24:55 -0700 Subject: [PATCH 086/251] bittensor.core.extrinsics.move_stake.swap_stake_extrinsic --- bittensor/core/extrinsics/move_stake.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/bittensor/core/extrinsics/move_stake.py b/bittensor/core/extrinsics/move_stake.py index 3a1366b49b..9bde9e8d48 100644 --- a/bittensor/core/extrinsics/move_stake.py +++ b/bittensor/core/extrinsics/move_stake.py @@ -166,6 +166,7 @@ def swap_stake_extrinsic( safe_staking: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, + period: Optional[int] = None, ) -> bool: """ Moves stake between subnets while keeping the same coldkey-hotkey pair ownership. @@ -181,7 +182,10 @@ def swap_stake_extrinsic( wait_for_finalization (bool): If true, waits for finalization before returning. safe_staking (bool): If true, enables price safety checks to protect against price impact. allow_partial_stake (bool): If true, allows partial stake swaps when the full amount would exceed the price tolerance. - rate_tolerance (float): Maximum allowed increase in price ratio (0.005 = 0.5%). + rate_tolerance (float): Maximum allowed increase in a price ratio (0.005 = 0.5%). + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: success (bool): True if the swap was successful. @@ -261,6 +265,7 @@ def swap_stake_extrinsic( wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) if success: From 5ab22dffba2f4de98a23cf4a3995af996f00d9ca Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 00:25:04 -0700 Subject: [PATCH 087/251] bittensor.core.subtensor.Subtensor.swap_stake --- bittensor/core/subtensor.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 4c44d75ebc..49dfca3141 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -3402,6 +3402,7 @@ def swap_stake( safe_staking: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, + period: Optional[int] = None, ) -> bool: """ Moves stake between subnets while keeping the same coldkey-hotkey pair ownership. @@ -3424,6 +3425,9 @@ def swap_stake( rate_tolerance (float): The maximum allowed increase in the price ratio between subnets (origin_price/destination_price). For example, 0.005 = 0.5% maximum increase. Only used when safe_staking is True. Default is 0.005. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: @@ -3449,6 +3453,7 @@ def swap_stake( safe_staking=safe_staking, allow_partial_stake=allow_partial_stake, rate_tolerance=rate_tolerance, + period=period, ) def transfer( From 9050c204f164c96280acbcd537e0fbfebbfe195a Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 00:25:12 -0700 Subject: [PATCH 088/251] fix related tests --- tests/unit_tests/test_subtensor.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index c1ef86bbd2..bfc9b45d94 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -3021,6 +3021,7 @@ def test_swap_stake_success(mocker, subtensor, fake_wallet): safe_staking=False, allow_partial_stake=False, rate_tolerance=0.005, + period=None, ) assert result == mock_swap_stake_extrinsic.return_value @@ -3065,6 +3066,7 @@ def test_swap_stake_with_safe_staking(mocker, subtensor, fake_wallet): safe_staking=True, allow_partial_stake=True, rate_tolerance=fake_rate_tolerance, + period=None, ) assert result == mock_swap_stake_extrinsic.return_value From 0f56efc27bbcd90aadf36222977a30d1e5569428 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 00:29:20 -0700 Subject: [PATCH 089/251] bittensor.core.extrinsics.move_stake.move_stake_extrinsic --- bittensor/core/extrinsics/move_stake.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/bittensor/core/extrinsics/move_stake.py b/bittensor/core/extrinsics/move_stake.py index 9bde9e8d48..a815c14dc0 100644 --- a/bittensor/core/extrinsics/move_stake.py +++ b/bittensor/core/extrinsics/move_stake.py @@ -316,6 +316,7 @@ def move_stake_extrinsic( amount: Optional[Balance] = None, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> bool: """ Moves stake to a different hotkey and/or subnet while keeping the same coldkey owner. @@ -330,6 +331,9 @@ def move_stake_extrinsic( amount (Union[Balance, float]): Amount to move. wait_for_inclusion (bool): If true, waits for inclusion before returning. wait_for_finalization (bool): If true, waits for finalization before returning. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: success (bool): True if the move was successful. @@ -375,6 +379,7 @@ def move_stake_extrinsic( wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) if success: From 474b2ee7b928e62a4c71d5138f1d5057f89a56f1 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 00:29:30 -0700 Subject: [PATCH 090/251] bittensor.core.subtensor.Subtensor.move_stake --- bittensor/core/subtensor.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 49dfca3141..b6cf393e8c 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -2936,6 +2936,7 @@ def move_stake( amount: Balance, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> bool: """ Moves stake to a different hotkey and/or subnet. @@ -2949,6 +2950,9 @@ def move_stake( amount (Balance): Amount of stake to move. wait_for_inclusion (bool): Waits for the transaction to be included in a block. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: success (bool): True if the stake movement was successful. @@ -2964,6 +2968,7 @@ def move_stake( amount=amount, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) def register( From d549aa2678a4e0716c43a3c7fcd7ac2000357015 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 00:32:17 -0700 Subject: [PATCH 091/251] bittensor/core/extrinsics/asyncex/move_stake.py --- .../core/extrinsics/asyncex/move_stake.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/bittensor/core/extrinsics/asyncex/move_stake.py b/bittensor/core/extrinsics/asyncex/move_stake.py index 6ab0b13028..ee21e00aeb 100644 --- a/bittensor/core/extrinsics/asyncex/move_stake.py +++ b/bittensor/core/extrinsics/asyncex/move_stake.py @@ -1,5 +1,5 @@ import asyncio -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Optional from bittensor.utils.balance import Balance from bittensor.utils.btlogging import logging @@ -18,6 +18,7 @@ async def _get_stake_in_origin_and_dest( origin_netuid: int, destination_netuid: int, ) -> tuple[Balance, Balance]: + """Gets the current stake balances for both origin and destination addresses in their respective subnets.""" block_hash = await subtensor.substrate.get_chain_head() stake_in_origin, stake_in_destination = await asyncio.gather( subtensor.get_stake( @@ -46,6 +47,7 @@ async def transfer_stake_extrinsic( amount: Balance, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> bool: """ Transfers stake from one coldkey to another in the Bittensor network. @@ -60,6 +62,9 @@ async def transfer_stake_extrinsic( amount (Balance): The amount of stake to transfer as a `Balance` object. wait_for_inclusion (bool): If True, waits for transaction inclusion in a block. Defaults to `True`. wait_for_finalization (bool): If True, waits for transaction finalization. Defaults to `False`. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: bool: True if the transfer was successful, False otherwise. @@ -116,6 +121,7 @@ async def transfer_stake_extrinsic( wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) if success: @@ -163,6 +169,7 @@ async def swap_stake_extrinsic( safe_staking: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, + period: Optional[int] = None, ) -> bool: """ Swaps stake from one subnet to another for a given hotkey in the Bittensor network. @@ -179,6 +186,9 @@ async def swap_stake_extrinsic( safe_staking (bool): If true, enables price safety checks to protect against price impact. allow_partial_stake (bool): If true, allows partial stake swaps when the full amount would exceed the price tolerance. rate_tolerance (float): Maximum allowed increase in price ratio (0.005 = 0.5%). + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: bool: True if the swap was successful, False otherwise. @@ -259,6 +269,7 @@ async def swap_stake_extrinsic( wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) if success: @@ -309,6 +320,7 @@ async def move_stake_extrinsic( amount: Balance, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> bool: """ Moves stake from one hotkey to another within subnets in the Bittensor network. @@ -323,6 +335,9 @@ async def move_stake_extrinsic( amount (Balance): The amount of stake to move as a `Balance` object. wait_for_inclusion (bool): If True, waits for transaction inclusion in a block. Defaults to True. wait_for_finalization (bool): If True, waits for transaction finalization. Defaults to False. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: bool: True if the move was successful, False otherwise. @@ -369,6 +384,7 @@ async def move_stake_extrinsic( wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) if success: From adeda3f2ce4cc57700e1f8c7685097cdca170a3a Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 00:35:05 -0700 Subject: [PATCH 092/251] related changes in bittensor/core/async_subtensor.py --- bittensor/core/async_subtensor.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index cf4d6f62e1..6f708cc899 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -3506,6 +3506,7 @@ async def move_stake( amount: Balance, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> bool: """ Moves stake to a different hotkey and/or subnet. @@ -3519,6 +3520,9 @@ async def move_stake( amount (Balance): Amount of stake to move. wait_for_inclusion (bool): Waits for the transaction to be included in a block. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: success (bool): True if the stake movement was successful. @@ -3534,6 +3538,7 @@ async def move_stake( amount=amount, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) async def register( @@ -4128,6 +4133,7 @@ async def swap_stake( safe_staking: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, + period: Optional[int] = None, ) -> bool: """ Moves stake between subnets while keeping the same coldkey-hotkey pair ownership. @@ -4150,6 +4156,9 @@ async def swap_stake( rate_tolerance (float): The maximum allowed increase in the price ratio between subnets (origin_price/destination_price). For example, 0.005 = 0.5% maximum increase. Only used when safe_staking is True. Default is 0.005. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: success (bool): True if the extrinsic was successful. @@ -4174,6 +4183,7 @@ async def swap_stake( safe_staking=safe_staking, allow_partial_stake=allow_partial_stake, rate_tolerance=rate_tolerance, + period=period ) async def transfer_stake( @@ -4186,6 +4196,7 @@ async def transfer_stake( amount: Balance, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> bool: """ Transfers stake from one subnet to another while changing the coldkey owner. @@ -4199,6 +4210,9 @@ async def transfer_stake( amount (Balance): Amount to transfer. wait_for_inclusion (bool): If true, waits for inclusion before returning. wait_for_finalization (bool): If true, waits for finalization before returning. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: success (bool): True if the transfer was successful. @@ -4214,6 +4228,7 @@ async def transfer_stake( amount=amount, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) async def transfer( From c73c4ac8171a6dacfbb7af432c43449bb67e2e4c Mon Sep 17 00:00:00 2001 From: Roman <167799377+basfroman@users.noreply.github.com> Date: Thu, 1 May 2025 09:53:37 -0700 Subject: [PATCH 093/251] Update bittensor/utils/__init__.py good catch Co-authored-by: BD Himes <37844818+thewhaleking@users.noreply.github.com> --- bittensor/utils/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bittensor/utils/__init__.py b/bittensor/utils/__init__.py index cbcaa3dd0f..5efe1cbb25 100644 --- a/bittensor/utils/__init__.py +++ b/bittensor/utils/__init__.py @@ -248,7 +248,7 @@ def format_error_message(error_message: Union[dict, Exception]) -> str: err_type = error_message.get("type", err_type) err_name = error_message.get("name", err_name) err_docs = error_message.get("docs", [err_description]) - err_description = " ".join(err_docs) if err_docs else err_description + err_description = " ".join(err_docs) elif error_message.get("code") and error_message.get("message"): err_type = error_message.get("code", err_name) From c0eebe01fc1aa36322baeff474cc525f613f5b8e Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 11:58:32 -0700 Subject: [PATCH 094/251] update `bittensor/core/extrinsics/registration.py` --- bittensor/core/extrinsics/registration.py | 41 ++++++++++++++++++++--- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/bittensor/core/extrinsics/registration.py b/bittensor/core/extrinsics/registration.py index c1feea3a30..ed53319974 100644 --- a/bittensor/core/extrinsics/registration.py +++ b/bittensor/core/extrinsics/registration.py @@ -25,6 +25,7 @@ def _do_burned_register( wallet: "Wallet", wait_for_inclusion: bool = False, wait_for_finalization: bool = True, + period: Optional[int] = None, ) -> tuple[bool, str]: """ Performs a burned register extrinsic call to the Subtensor chain. @@ -37,6 +38,9 @@ def _do_burned_register( wallet (bittensor_wallet.Wallet): The wallet to be registered. wait_for_inclusion (bool): Whether to wait for the transaction to be included in a block. Default is False. wait_for_finalization (bool): Whether to wait for the transaction to be finalized. Default is True. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: Tuple[bool, Optional[str]]: A tuple containing a boolean indicating success or failure, and an optional error @@ -57,6 +61,7 @@ def _do_burned_register( wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) @@ -66,6 +71,7 @@ def burned_register_extrinsic( netuid: int, wait_for_inclusion: bool = False, wait_for_finalization: bool = True, + period: Optional[int] = None, ) -> bool: """Registers the wallet to chain by recycling TAO. @@ -77,6 +83,9 @@ def burned_register_extrinsic( returns ``False`` if the extrinsic fails to enter the block within the timeout. wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning ``True``, or returns ``False`` if the extrinsic fails to be finalized within the timeout. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: success (bool): Flag is ``True`` if extrinsic was finalized or included in the block. If we did not wait for @@ -120,6 +129,7 @@ def burned_register_extrinsic( wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) if not success: @@ -154,6 +164,7 @@ def _do_pow_register( pow_result: "POWSolution", wait_for_inclusion: bool = False, wait_for_finalization: bool = True, + period: Optional[int] = None, ) -> tuple[bool, Optional[str]]: """Sends a (POW) register extrinsic to the chain. @@ -164,6 +175,9 @@ def _do_pow_register( pow_result (POWSolution): The PoW result to register. wait_for_inclusion (bool): If ``True``, waits for the extrinsic to be included in a block. Default to `False`. wait_for_finalization (bool): If ``True``, waits for the extrinsic to be finalized. Default to `True`. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: success (bool): ``True`` if the extrinsic was included in a block. @@ -188,6 +202,7 @@ def _do_pow_register( wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) @@ -196,6 +211,7 @@ def register_subnet_extrinsic( wallet: "Wallet", wait_for_inclusion: bool = False, wait_for_finalization: bool = True, + period: Optional[int] = None, ) -> bool: """ Registers a new subnetwork on the Bittensor blockchain. @@ -205,6 +221,9 @@ def register_subnet_extrinsic( wallet (Wallet): The wallet to be used for subnet registration. wait_for_inclusion (bool): If set, waits for the extrinsic to enter a block before returning true. wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning true. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: bool: True if the subnet registration was successful, False otherwise. @@ -232,16 +251,20 @@ def register_subnet_extrinsic( wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) + if not wait_for_finalization and not wait_for_inclusion: + return True + if success: logging.success( ":white_heavy_check_mark: [green]Successfully registered subnet[/green]" ) return True - else: - logging.error(f"Failed to register subnet: {message}") - return False + + logging.error(f"Failed to register subnet: {message}") + return False def register_extrinsic( @@ -258,6 +281,7 @@ def register_extrinsic( num_processes: Optional[int] = None, update_interval: Optional[int] = None, log_verbose: bool = False, + period: Optional[int] = None, ) -> bool: """Registers the wallet to the chain. @@ -277,6 +301,9 @@ def register_extrinsic( num_processes: The number of processes to use to register. update_interval: The number of nonces to solve between updates. log_verbose: If `True`, the registration process will log more information. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: `True` if extrinsic was finalized or included in the block. If we did not wait for finalization/inclusion, the @@ -366,7 +393,7 @@ def register_extrinsic( # pow successful, proceed to submit pow to chain for registration else: logging.info(":satellite: [magenta]Submitting POW...[/magenta]") - # check if pow result is still valid + # check if a pow result is still valid while not pow_result.is_stale(subtensor=subtensor): result: tuple[bool, Optional[str]] = _do_pow_register( subtensor=subtensor, @@ -375,6 +402,7 @@ def register_extrinsic( pow_result=pow_result, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) success, err_msg = result @@ -439,6 +467,7 @@ def set_subnet_identity_extrinsic( additional: str, wait_for_inclusion: bool = False, wait_for_finalization: bool = True, + period: Optional[int] = None, ) -> tuple[bool, str]: """ Set the identity information for a given subnet. @@ -456,6 +485,9 @@ def set_subnet_identity_extrinsic( additional (str): Any additional metadata or information related to the subnet. wait_for_inclusion (bool): Whether to wait for the extrinsic inclusion in a block (default: False). wait_for_finalization (bool): Whether to wait for the extrinsic finalization in a block (default: True). + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: tuple[bool, str]: A tuple where the first element indicates success or failure (True/False), and the second @@ -487,6 +519,7 @@ def set_subnet_identity_extrinsic( wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) if not wait_for_finalization and not wait_for_inclusion: From 54e026120245dcf24c96aad9db2c71aa79832fa7 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 11:58:53 -0700 Subject: [PATCH 095/251] update `bittensor/core/extrinsics/asyncex/registration.py` --- .../core/extrinsics/asyncex/registration.py | 57 +++++++++++++------ 1 file changed, 41 insertions(+), 16 deletions(-) diff --git a/bittensor/core/extrinsics/asyncex/registration.py b/bittensor/core/extrinsics/asyncex/registration.py index cfdd78e6f5..0d5cff1692 100644 --- a/bittensor/core/extrinsics/asyncex/registration.py +++ b/bittensor/core/extrinsics/asyncex/registration.py @@ -26,6 +26,7 @@ async def _do_burned_register( wallet: "Wallet", wait_for_inclusion: bool = False, wait_for_finalization: bool = True, + period: Optional[int] = None, ) -> tuple[bool, str]: """ Performs a burned register extrinsic call to the Subtensor chain. @@ -38,6 +39,9 @@ async def _do_burned_register( wallet (bittensor_wallet.Wallet): The wallet to be registered. wait_for_inclusion (bool): Whether to wait for the transaction to be included in a block. Default is False. wait_for_finalization (bool): Whether to wait for the transaction to be finalized. Default is True. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: Tuple[bool, Optional[str]]: A tuple containing a boolean indicating success or failure, and an optional error @@ -58,6 +62,7 @@ async def _do_burned_register( wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) @@ -67,6 +72,7 @@ async def burned_register_extrinsic( netuid: int, wait_for_inclusion: bool = False, wait_for_finalization: bool = True, + period: Optional[int] = None, ) -> bool: """Registers the wallet to chain by recycling TAO. @@ -78,6 +84,9 @@ async def burned_register_extrinsic( returns ``False`` if the extrinsic fails to enter the block within the timeout. wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning ``True``, or returns ``False`` if the extrinsic fails to be finalized within the timeout. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: success (bool): Flag is ``True`` if extrinsic was finalized or included in the block. If we did not wait for @@ -98,7 +107,7 @@ async def burned_register_extrinsic( f":satellite: [magenta]Checking Account on subnet[/magenta] [blue]{netuid}[/blue][magenta] ...[/magenta]" ) - # We could do this as_completed because we don't actually need old_balance and recycle + # We could do this as_completed because we don't need old_balance and recycle # if neuron is null, but the complexity isn't worth it considering the small performance # gains we'd hypothetically receive in this situation neuron, old_balance, recycle_amount = await asyncio.gather( @@ -126,6 +135,7 @@ async def burned_register_extrinsic( wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) if not success: @@ -162,6 +172,7 @@ async def _do_pow_register( pow_result: "POWSolution", wait_for_inclusion: bool = False, wait_for_finalization: bool = True, + period: Optional[int] = None, ) -> tuple[bool, Optional[str]]: """Sends a (POW) register extrinsic to the chain. @@ -172,6 +183,9 @@ async def _do_pow_register( pow_result (POWSolution): The PoW result to register. wait_for_inclusion (bool): If ``True``, waits for the extrinsic to be included in a block. Default to `False`. wait_for_finalization (bool): If ``True``, waits for the extrinsic to be finalized. Default to `True`. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: success (bool): ``True`` if the extrinsic was included in a block. @@ -196,6 +210,7 @@ async def _do_pow_register( wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) @@ -213,6 +228,7 @@ async def register_extrinsic( num_processes: Optional[int] = None, update_interval: Optional[int] = None, log_verbose: bool = False, + period: Optional[int] = None, ) -> bool: """Registers the wallet to the chain. @@ -233,6 +249,9 @@ async def register_extrinsic( num_processes: The number of processes to use to register. update_interval: The number of nonces to solve between updates. log_verbose: If `True`, the registration process will log more information. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: `True` if extrinsic was finalized or included in the block. If we did not wait for finalization/inclusion, the @@ -321,7 +340,7 @@ async def register_extrinsic( # pow successful, proceed to submit pow to chain for registration else: logging.info(":satellite: [magenta]Submitting POW...[/magenta]") - # check if pow result is still valid + # check if a pow result is still valid while not await pow_result.is_stale_async(subtensor=subtensor): result: tuple[bool, Optional[str]] = await _do_pow_register( subtensor=subtensor, @@ -330,6 +349,7 @@ async def register_extrinsic( pow_result=pow_result, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) success, err_msg = result @@ -386,6 +406,7 @@ async def register_subnet_extrinsic( wallet: "Wallet", wait_for_inclusion: bool = False, wait_for_finalization: bool = True, + period: Optional[int] = None, ) -> bool: """ Registers a new subnetwork on the Bittensor blockchain asynchronously. @@ -395,6 +416,9 @@ async def register_subnet_extrinsic( wallet (Wallet): The wallet to be used for subnet registration. wait_for_inclusion (bool): If set, waits for the extrinsic to enter a block before returning true. wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning true. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: bool: True if the subnet registration was successful, False otherwise. @@ -417,29 +441,25 @@ async def register_subnet_extrinsic( }, ) - extrinsic = await subtensor.substrate.create_signed_extrinsic( - call=call, keypair=wallet.coldkey - ) - - response = await subtensor.substrate.submit_extrinsic( - extrinsic, + success, message = await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) if not wait_for_finalization and not wait_for_inclusion: return True - if not await response.is_success: - logging.error( - f"Failed to register subnet: {format_error_message(await response.error_message)}" + if success: + logging.success( + ":white_heavy_check_mark: [green]Successfully registered subnet[/green]" ) - return False + return True - logging.success( - ":white_heavy_check_mark: [green]Successfully registered subnet[/green]" - ) - return True + logging.error(f"Failed to register subnet: {message}") + return False async def set_subnet_identity_extrinsic( @@ -455,6 +475,7 @@ async def set_subnet_identity_extrinsic( additional: str, wait_for_inclusion: bool = False, wait_for_finalization: bool = True, + period: Optional[int] = None, ) -> tuple[bool, str]: """ Set the identity information for a given subnet. @@ -472,6 +493,9 @@ async def set_subnet_identity_extrinsic( additional (str): Any additional metadata or information related to the subnet. wait_for_inclusion (bool): Whether to wait for the extrinsic inclusion in a block (default: False). wait_for_finalization (bool): Whether to wait for the extrinsic finalization in a block (default: True). + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: tuple[bool, str]: A tuple where the first element indicates success or failure (True/False), and the second @@ -503,6 +527,7 @@ async def set_subnet_identity_extrinsic( wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) if not wait_for_finalization and not wait_for_inclusion: From 04cc497719a6a6f36ad8a645d767ecc72148efa6 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 11:59:31 -0700 Subject: [PATCH 096/251] update `bittensor/core/async_subtensor.py` --- bittensor/core/async_subtensor.py | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index b9ef190488..98c09b26e5 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -3382,6 +3382,7 @@ async def burned_register( netuid: int, wait_for_inclusion: bool = False, wait_for_finalization: bool = True, + period: Optional[int] = None, ) -> bool: """ Registers a neuron on the Bittensor network by recycling TAO. This method of registration involves recycling @@ -3394,6 +3395,9 @@ async def burned_register( `False`. wait_for_finalization (bool, optional): Waits for the transaction to be finalized on the blockchain. Defaults to `True`. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: bool: ``True`` if the registration is successful, False otherwise. @@ -3413,6 +3417,7 @@ async def burned_register( netuid=netuid, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period ) async def commit_weights( @@ -3554,6 +3559,7 @@ async def register( num_processes: Optional[int] = None, update_interval: Optional[int] = None, log_verbose: bool = False, + period: Optional[int] = None, ): """ Registers a neuron on the Bittensor network using the provided wallet. @@ -3576,6 +3582,9 @@ async def register( num_processes (Optional[int]): The number of processes to use to register. Default to `None`. update_interval (Optional[int]): The number of nonces to solve between updates. Default to `None`. log_verbose (bool): If ``true``, the registration process will log more information. Default to `False`. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: bool: ``True`` if the registration is successful, False otherwise. @@ -3597,6 +3606,7 @@ async def register( dev_id=dev_id, output_in_place=output_in_place, log_verbose=log_verbose, + period=period, ) async def register_subnet( @@ -3604,6 +3614,7 @@ async def register_subnet( wallet: "Wallet", wait_for_inclusion: bool = False, wait_for_finalization: bool = True, + period: Optional[int] = None, ) -> bool: """ Registers a new subnetwork on the Bittensor network. @@ -3614,6 +3625,9 @@ async def register_subnet( false if the extrinsic fails to enter the block within the timeout. Default is False. wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning true, or returns false if the extrinsic fails to be finalized within the timeout. Default is True. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: bool: True if the subnet registration was successful, False otherwise. @@ -3624,6 +3638,7 @@ async def register_subnet( wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) async def reveal_weights( @@ -3907,6 +3922,7 @@ async def set_subnet_identity( subnet_identity: SubnetIdentity, wait_for_inclusion: bool = False, wait_for_finalization: bool = True, + period: Optional[int] = None, ) -> tuple[bool, str]: """ Sets the identity of a subnet for a specific wallet and network. @@ -3918,6 +3934,9 @@ async def set_subnet_identity( repository, contact, URL, discord, description, and any additional metadata. wait_for_inclusion (bool): Indicates if the function should wait for the transaction to be included in the block. wait_for_finalization (bool): Indicates if the function should wait for the transaction to reach finalization. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: tuple[bool, str]: A tuple where the first element is a boolean indicating success or failure of the @@ -3936,6 +3955,7 @@ async def set_subnet_identity( additional=subnet_identity.additional, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) async def set_weights( @@ -3964,12 +3984,12 @@ async def set_weights( weights (Union[NDArray[np.float32], torch.FloatTensor, list]): The corresponding weights to be set for each UID. version_key (int): Version key for compatibility with the network. Default is int representation of - Bittensor version. + a Bittensor version. wait_for_inclusion (bool): Waits for the transaction to be included in a block. Default is ``False``. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. Default is ``False``. max_retries (int): The number of maximum attempts to set weights. Default is ``5``. - block_time (float): The amount of seconds for block duration. Default is 12.0 seconds. + block_time (float): The number of seconds for block duration. Default is 12.0 seconds. period (int, optional): The period in seconds to wait for extrinsic inclusion or finalization. Defaults to 5. Returns: From f131a7cdc3abafb5fcc6d0852e73ab01c5113f1f Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 11:59:40 -0700 Subject: [PATCH 097/251] update `bittensor/core/subtensor.py` --- bittensor/core/subtensor.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 57e6d42eea..9b08a25221 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -2812,6 +2812,7 @@ def burned_register( netuid: int, wait_for_inclusion: bool = False, wait_for_finalization: bool = True, + period: Optional[int] = None, ) -> bool: """ Registers a neuron on the Bittensor network by recycling TAO. This method of registration involves recycling @@ -2824,6 +2825,9 @@ def burned_register( `False`. wait_for_finalization (bool, optional): Waits for the transaction to be finalized on the blockchain. Defaults to `True`. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: bool: ``True`` if the registration is successful, False otherwise. @@ -2843,6 +2847,7 @@ def burned_register( netuid=netuid, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) def commit_weights( @@ -2984,6 +2989,7 @@ def register( num_processes: Optional[int] = None, update_interval: Optional[int] = None, log_verbose: bool = False, + period: Optional[int] = None, ) -> bool: """ Registers a neuron on the Bittensor network using the provided wallet. @@ -3006,6 +3012,9 @@ def register( num_processes (Optional[int]): The number of processes to use to register. Default to `None`. update_interval (Optional[int]): The number of nonces to solve between updates. Default to `None`. log_verbose (bool): If ``true``, the registration process will log more information. Default to `False`. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: bool: ``True`` if the registration is successful, False otherwise. @@ -3027,6 +3036,7 @@ def register( dev_id=dev_id, output_in_place=output_in_place, log_verbose=log_verbose, + period=period, ) def register_subnet( @@ -3034,6 +3044,7 @@ def register_subnet( wallet: "Wallet", wait_for_inclusion: bool = False, wait_for_finalization: bool = True, + period: Optional[int] = None, ) -> bool: """ Registers a new subnetwork on the Bittensor network. @@ -3044,6 +3055,9 @@ def register_subnet( false if the extrinsic fails to enter the block within the timeout. Default is False. wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning true, or returns false if the extrinsic fails to be finalized within the timeout. Default is True. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: bool: True if the subnet registration was successful, False otherwise. @@ -3053,6 +3067,7 @@ def register_subnet( wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) def reveal_weights( @@ -3192,6 +3207,7 @@ def set_subnet_identity( subnet_identity: SubnetIdentity, wait_for_inclusion: bool = False, wait_for_finalization: bool = True, + period: Optional[int] = None, ) -> tuple[bool, str]: """ Sets the identity of a subnet for a specific wallet and network. @@ -3203,6 +3219,9 @@ def set_subnet_identity( repository, contact, URL, discord, description, and any additional metadata. wait_for_inclusion (bool): Indicates if the function should wait for the transaction to be included in the block. wait_for_finalization (bool): Indicates if the function should wait for the transaction to reach finalization. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: tuple[bool, str]: A tuple where the first element is a boolean indicating success or failure of the @@ -3221,6 +3240,7 @@ def set_subnet_identity( additional=subnet_identity.additional, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) def set_weights( From 0e743f71cc15c21a93d5f51fd520f55343119b63 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 11:59:54 -0700 Subject: [PATCH 098/251] fix related tests --- tests/e2e_tests/test_axon.py | 2 +- tests/e2e_tests/test_set_subnet_identity_extrinsic.py | 2 +- tests/unit_tests/extrinsics/asyncex/test_registration.py | 3 +++ tests/unit_tests/extrinsics/test_registration.py | 2 ++ tests/unit_tests/test_async_subtensor.py | 2 ++ tests/unit_tests/test_subtensor.py | 1 + 6 files changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/e2e_tests/test_axon.py b/tests/e2e_tests/test_axon.py index 0a139eb457..613027c691 100644 --- a/tests/e2e_tests/test_axon.py +++ b/tests/e2e_tests/test_axon.py @@ -13,7 +13,7 @@ async def test_axon(subtensor, templates, alice_wallet): Steps: 1. Register a subnet and register Alice 2. Check if metagraph.axon is updated and check axon attributes - 3. Run Alice as a miner on the subnet + 3. Run Alice as a miner on subnet 4. Check the metagraph again after running the miner and verify all attributes Raises: AssertionError: If any of the checks or verifications fail diff --git a/tests/e2e_tests/test_set_subnet_identity_extrinsic.py b/tests/e2e_tests/test_set_subnet_identity_extrinsic.py index 60d91fa3d1..11c8d76d7e 100644 --- a/tests/e2e_tests/test_set_subnet_identity_extrinsic.py +++ b/tests/e2e_tests/test_set_subnet_identity_extrinsic.py @@ -10,7 +10,7 @@ async def test_set_subnet_identity_extrinsic_happy_pass(subtensor, alice_wallet) "[magenta]Testing `set_subnet_identity_extrinsic` with success result.[/magenta]" ) - netuid = 2 + netuid = subtensor.get_total_subnets() # 2 # Register a subnet, netuid 2 assert subtensor.register_subnet(alice_wallet), "Subnet wasn't created" diff --git a/tests/unit_tests/extrinsics/asyncex/test_registration.py b/tests/unit_tests/extrinsics/asyncex/test_registration.py index b53fd946e0..66d1c667f0 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_registration.py +++ b/tests/unit_tests/extrinsics/asyncex/test_registration.py @@ -450,6 +450,7 @@ async def is_stale_side_effect(*_, **__): pow_result=fake_pow_result, wait_for_inclusion=True, wait_for_finalization=True, + period=None, ) assert result is False @@ -510,6 +511,7 @@ async def test_set_subnet_identity_extrinsic_is_success(subtensor, fake_wallet, wallet=fake_wallet, wait_for_inclusion=False, wait_for_finalization=True, + period=None, ) assert result == (True, "Identities for subnet 123 are set.") @@ -574,6 +576,7 @@ async def test_set_subnet_identity_extrinsic_is_failed(subtensor, fake_wallet, m wallet=fake_wallet, wait_for_inclusion=True, wait_for_finalization=True, + period=None, ) assert result == ( diff --git a/tests/unit_tests/extrinsics/test_registration.py b/tests/unit_tests/extrinsics/test_registration.py index b2d816d4dd..77c6a0d3aa 100644 --- a/tests/unit_tests/extrinsics/test_registration.py +++ b/tests/unit_tests/extrinsics/test_registration.py @@ -286,6 +286,7 @@ def test_set_subnet_identity_extrinsic_is_success(mock_subtensor, mock_wallet, m wallet=mock_wallet, wait_for_inclusion=False, wait_for_finalization=True, + period=None, ) assert result == (True, "Identities for subnet 123 are set.") @@ -347,6 +348,7 @@ def test_set_subnet_identity_extrinsic_is_failed(mock_subtensor, mock_wallet, mo wallet=mock_wallet, wait_for_inclusion=False, wait_for_finalization=True, + period=None, ) assert result == ( diff --git a/tests/unit_tests/test_async_subtensor.py b/tests/unit_tests/test_async_subtensor.py index c0fd7dbcc5..1c409a6658 100644 --- a/tests/unit_tests/test_async_subtensor.py +++ b/tests/unit_tests/test_async_subtensor.py @@ -2551,6 +2551,7 @@ async def test_register_success(subtensor, fake_wallet, mocker): wait_for_finalization=True, wait_for_inclusion=False, wallet=fake_wallet, + period=None, ) assert result == mocked_register_extrinsic.return_value @@ -2956,6 +2957,7 @@ async def test_set_subnet_identity(mocker, subtensor, fake_wallet): additional=fake_subnet_identity.additional, wait_for_finalization=True, wait_for_inclusion=False, + period=None, ) assert result == mocked_extrinsic.return_value diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index bfc9b45d94..9160ae4c2a 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -3206,6 +3206,7 @@ def test_set_subnet_identity(mocker, subtensor, fake_wallet): additional=fake_subnet_identity.additional, wait_for_finalization=True, wait_for_inclusion=False, + period=None, ) assert result == mocked_extrinsic.return_value From 904b4cd980efb85fb44108c531e3952788a8a2d2 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 12:00:08 -0700 Subject: [PATCH 099/251] optimize imports in utils --- bittensor/core/extrinsics/utils.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bittensor/core/extrinsics/utils.py b/bittensor/core/extrinsics/utils.py index 1ee776895b..e0d2cb8e4f 100644 --- a/bittensor/core/extrinsics/utils.py +++ b/bittensor/core/extrinsics/utils.py @@ -1,8 +1,10 @@ """Module with helper functions for extrinsics.""" from typing import TYPE_CHECKING, Union + import numpy as np from numpy.typing import NDArray + from bittensor.utils.balance import Balance from bittensor.utils.weight_utils import convert_weights_and_uids_for_emit From f813b9c7347b09f189dee20423ae4b42e963ef61 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 16:42:48 -0700 Subject: [PATCH 100/251] update `bittensor/core/extrinsics/root.py` --- bittensor/core/extrinsics/root.py | 67 ++++++++++++++++--------------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/bittensor/core/extrinsics/root.py b/bittensor/core/extrinsics/root.py index 43fd65a5a1..7272eae296 100644 --- a/bittensor/core/extrinsics/root.py +++ b/bittensor/core/extrinsics/root.py @@ -1,5 +1,5 @@ import time -from typing import Union, TYPE_CHECKING +from typing import Optional, Union, TYPE_CHECKING import numpy as np from numpy.typing import NDArray @@ -16,6 +16,7 @@ from bittensor.utils.weight_utils import ( normalize_max_weight, convert_weights_and_uids_for_emit, + convert_netuids_and_weights ) if TYPE_CHECKING: @@ -50,8 +51,9 @@ def root_register_extrinsic( wallet: "Wallet", wait_for_inclusion: bool = False, wait_for_finalization: bool = True, + period: Optional[int] = None, ) -> bool: - """Registers the wallet to root network. + """Registers the wallet to the root network. Arguments: subtensor (bittensor.core.subtensor.Subtensor): The Subtensor object @@ -60,6 +62,9 @@ def root_register_extrinsic( `False` if the extrinsic fails to enter the block within the timeout. wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning `True`, or returns `False` if the extrinsic fails to be finalized within the timeout. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: `True` if extrinsic was finalized or included in the block. If we did not wait for finalization/inclusion, the @@ -118,6 +123,7 @@ def root_register_extrinsic( wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) if not success: @@ -152,7 +158,7 @@ def _do_set_root_weights( version_key: int = 0, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, - period: int = 5, + period: Optional[int] = 8, ) -> tuple[bool, str]: """ Sets the root weights on the Subnet for the given wallet hotkey account. @@ -172,7 +178,9 @@ def _do_set_root_weights( False. wait_for_finalization (bool, optional): If True, waits for the extrinsic to be finalized on the chain. Defaults to False. - period (int, optional): The period in seconds to wait for extrinsic inclusion or finalization. Defaults to 5. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: tuple: Returns a tuple containing a boolean indicating success and a message describing the result of the @@ -190,28 +198,23 @@ def _do_set_root_weights( }, ) - next_nonce = subtensor.substrate.get_account_next_index(wallet.hotkey.ss58_address) - - # Period dictates how long the extrinsic will stay as part of waiting pool - extrinsic = subtensor.substrate.create_signed_extrinsic( + success, message = subtensor.sign_and_send_extrinsic( call=call, - keypair=wallet.coldkey, - era={"period": period}, - nonce=next_nonce, - ) - response = subtensor.substrate.submit_extrinsic( - extrinsic=extrinsic, + wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + use_nonce=True, + period=period, ) + # We only wait here if we expect finalization. if not wait_for_finalization and not wait_for_inclusion: return True, "Not waiting for finalization or inclusion." - if response.is_success: + if success: return True, "Successfully set weights." - return False, format_error_message(response.error_message) + return False, message def set_root_weights_extrinsic( @@ -222,20 +225,24 @@ def set_root_weights_extrinsic( version_key: int = 0, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> bool: - """Sets the given weights and values on chain for wallet hotkey account. + """Sets the given weights and values on a chain for a wallet hotkey account. Arguments: subtensor (bittensor.core.subtensor.Subtensor): The Subtensor object wallet (bittensor_wallet.Wallet): Bittensor wallet object. netuids (Union[NDArray[np.int64], list[int]]): The `netuid` of the subnet to set weights for. - weights (Union[NDArray[np.float32], list[float]]): Weights to set. These must be `float` s and must correspond + weights (Union[NDArray[np.float32], list[float]]): Weights to set. These must be `float's and must correspond to the passed `netuid` s. version_key (int): The version key of the validator. wait_for_inclusion (bool): If set, waits for the extrinsic to enter a block before returning `True`, or returns `False` if the extrinsic fails to enter the block within the timeout. wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning True`, or returns `False` if the extrinsic fails to be finalized within the timeout. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: `True` if extrinsic was finalized or included in the block. If we did not wait for finalization/inclusion, the @@ -253,13 +260,10 @@ def set_root_weights_extrinsic( logging.error(unlock.message) return False - # First convert types. - if isinstance(netuids, list): - netuids = np.array(netuids, dtype=np.int64) - if isinstance(weights, list): - weights = np.array(weights, dtype=np.float32) + # Convert types. + netuids, weights = convert_netuids_and_weights(netuids, weights) - logging.debug("Fetching weight limits") + logging.debug("[magenta]Fetching weight limits ...[/magenta]") min_allowed_weights, max_weight_limit = _get_limits(subtensor) # Get non zero values. @@ -273,7 +277,7 @@ def set_root_weights_extrinsic( ) # Normalize the weights to max value. - logging.info("Normalizing weights") + logging.info("[magenta]Normalizing weights ...[/magenta]") formatted_weights = normalize_max_weight(x=weights, limit=max_weight_limit) logging.info( f"Raw weights -> Normalized weights: [blue]{weights}[/blue] -> [green]{formatted_weights}[/green]" @@ -283,7 +287,7 @@ def set_root_weights_extrinsic( logging.info(":satellite: [magenta]Setting root weights...[magenta]") weight_uids, weight_vals = convert_weights_and_uids_for_emit(netuids, weights) - success, error_message = _do_set_root_weights( + success, message = _do_set_root_weights( subtensor=subtensor, wallet=wallet, netuids=weight_uids, @@ -291,18 +295,15 @@ def set_root_weights_extrinsic( version_key=version_key, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) - if not wait_for_finalization and not wait_for_inclusion: - return True - if success is True: logging.info(":white_heavy_check_mark: [green]Finalized[/green]") return True - else: - fmt_err = error_message - logging.error(f":cross_mark: [red]Failed error:[/red] {fmt_err}") - return False + + logging.error(f":cross_mark: [red]Failed error:[/red] {message}") + return False except SubstrateRequestException as e: fmt_err = format_error_message(e) From 7fe6084342e3c57ec32095ab054316289006437b Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 16:43:04 -0700 Subject: [PATCH 101/251] update `bittensor/core/extrinsics/asyncex/root.py` --- bittensor/core/extrinsics/asyncex/root.py | 66 +++++++++++------------ 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/bittensor/core/extrinsics/asyncex/root.py b/bittensor/core/extrinsics/asyncex/root.py index 6c30f631eb..0170da5236 100644 --- a/bittensor/core/extrinsics/asyncex/root.py +++ b/bittensor/core/extrinsics/asyncex/root.py @@ -1,7 +1,6 @@ import asyncio -from typing import Union, TYPE_CHECKING +from typing import Optional, Union, TYPE_CHECKING -from bittensor_wallet import Wallet import numpy as np from numpy.typing import NDArray @@ -12,9 +11,11 @@ from bittensor.utils.weight_utils import ( normalize_max_weight, convert_weights_and_uids_for_emit, + convert_netuids_and_weights ) if TYPE_CHECKING: + from bittensor_wallet import Wallet from bittensor.core.async_subtensor import AsyncSubtensor @@ -47,8 +48,9 @@ async def root_register_extrinsic( wallet: "Wallet", wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + period: Optional[int] = None, ) -> bool: - """Registers the wallet to root network. + """Registers the wallet to the root network. Arguments: subtensor (bittensor.core.async_subtensor.AsyncSubtensor): The AsyncSubtensor object @@ -57,6 +59,9 @@ async def root_register_extrinsic( `False` if the extrinsic fails to enter the block within the timeout. wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning `True`, or returns `False` if the extrinsic fails to be finalized within the timeout. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: `True` if extrinsic was finalized or included in the block. If we did not wait for finalization/inclusion, @@ -112,15 +117,16 @@ async def root_register_extrinsic( call_function="root_register", call_params={"hotkey": wallet.hotkey.ss58_address}, ) - success, err_msg = await subtensor.sign_and_send_extrinsic( + success, message = await subtensor.sign_and_send_extrinsic( call, wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) if not success: - logging.error(f":cross_mark: [red]Failed error:[/red] {err_msg}") + logging.error(f":cross_mark: [red]Failed error:[/red] {message}") await asyncio.sleep(0.5) return False @@ -151,7 +157,7 @@ async def _do_set_root_weights( version_key: int = 0, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, - period: int = 5, + period: Optional[int] = 8, ) -> tuple[bool, str]: """ Sets the root weights on the Subnet for the given wallet hotkey account. @@ -171,7 +177,9 @@ async def _do_set_root_weights( False. wait_for_finalization (bool, optional): If True, waits for the extrinsic to be finalized on the chain. Defaults to False. - period (int, optional): The period in seconds to wait for extrinsic inclusion or finalization. Defaults to 5. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: tuple: Returns a tuple containing a boolean indicating success and a message describing the result of the @@ -189,30 +197,23 @@ async def _do_set_root_weights( }, ) - next_nonce = await subtensor.substrate.get_account_next_index( - wallet.hotkey.ss58_address - ) - - # Period dictates how long the extrinsic will stay as part of waiting pool - extrinsic = await subtensor.substrate.create_signed_extrinsic( + success, message = await subtensor.sign_and_send_extrinsic( call=call, - keypair=wallet.coldkey, - era={"period": period}, - nonce=next_nonce, - ) - response = await subtensor.substrate.submit_extrinsic( - extrinsic=extrinsic, + wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + use_nonce=True, + period=period, ) + # We only wait here if we expect finalization. if not wait_for_finalization and not wait_for_inclusion: return True, "Not waiting for finalization or inclusion." - if await response.is_success: + if success: return True, "Successfully set weights." - return False, format_error_message(await response.error_message) + return False, message async def set_root_weights_extrinsic( @@ -223,20 +224,24 @@ async def set_root_weights_extrinsic( version_key: int = 0, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> bool: - """Sets the given weights and values on chain for wallet hotkey account. + """Sets the given weights and values on a chain for a wallet hotkey account. Arguments: subtensor (bittensor.core.async_subtensor.AsyncSubtensor): The AsyncSubtensor object wallet (bittensor_wallet.Wallet): Bittensor wallet object. netuids (Union[NDArray[np.int64], list[int]]): The `netuid` of the subnet to set weights for. - weights (Union[NDArray[np.float32], list[float]]): Weights to set. These must be `float` s and must correspond + weights (Union[NDArray[np.float32], list[Float]]): Weights to set. These must be `Float`s and must correspond to the passed `netuid` s. version_key (int): The version key of the validator. wait_for_inclusion (bool): If set, waits for the extrinsic to enter a block before returning `True`, or returns `False` if the extrinsic fails to enter the block within the timeout. wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning ` True`, or returns `False` if the extrinsic fails to be finalized within the timeout. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: `True` if extrinsic was finalized or included in the block. If we did not wait for finalization/inclusion, the @@ -254,13 +259,10 @@ async def set_root_weights_extrinsic( logging.error(unlock.message) return False - # First convert types. - if isinstance(netuids, list): - netuids = np.array(netuids, dtype=np.int64) - if isinstance(weights, list): - weights = np.array(weights, dtype=np.float32) + # Convert types. + netuids, weights = convert_netuids_and_weights(netuids, weights) - logging.debug("Fetching weight limits") + logging.debug("[magenta]Fetching weight limits ...[/magenta]") min_allowed_weights, max_weight_limit = await _get_limits(subtensor) # Get non zero values. @@ -274,7 +276,7 @@ async def set_root_weights_extrinsic( ) # Normalize the weights to max value. - logging.info("Normalizing weights") + logging.info("[magenta]Normalizing weights ...[/magenta]") formatted_weights = normalize_max_weight(x=weights, limit=max_weight_limit) logging.info( f"Raw weights -> Normalized weights: [blue]{weights}[/blue] -> [green]{formatted_weights}[/green]" @@ -292,11 +294,9 @@ async def set_root_weights_extrinsic( version_key=version_key, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) - if not wait_for_finalization and not wait_for_inclusion: - return True - if success is True: logging.info(":white_heavy_check_mark: [green]Finalized[/green]") return True From 1aed64081ed44ae11480cb2fcc855d532aa9a929 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 16:46:08 -0700 Subject: [PATCH 102/251] refactoring related with `bittensor.utils.weight_utils.convert_and_normalize_weights_and_uids` --- .../core/extrinsics/asyncex/commit_reveal.py | 2 +- bittensor/core/extrinsics/asyncex/weights.py | 2 +- bittensor/core/extrinsics/commit_reveal.py | 2 +- bittensor/core/extrinsics/set_weights.py | 2 +- bittensor/core/extrinsics/utils.py | 26 ---------- bittensor/utils/weight_utils.py | 50 ++++++++++++++++++- 6 files changed, 52 insertions(+), 32 deletions(-) diff --git a/bittensor/core/extrinsics/asyncex/commit_reveal.py b/bittensor/core/extrinsics/asyncex/commit_reveal.py index 7994bbbd76..74d2de5c3a 100644 --- a/bittensor/core/extrinsics/asyncex/commit_reveal.py +++ b/bittensor/core/extrinsics/asyncex/commit_reveal.py @@ -6,9 +6,9 @@ from bittensor_drand import get_encrypted_commit from numpy.typing import NDArray -from bittensor.core.extrinsics.utils import convert_and_normalize_weights_and_uids from bittensor.core.settings import version_as_int from bittensor.utils.btlogging import logging +from bittensor.utils.weight_utils import convert_and_normalize_weights_and_uids if TYPE_CHECKING: from bittensor_wallet import Wallet diff --git a/bittensor/core/extrinsics/asyncex/weights.py b/bittensor/core/extrinsics/asyncex/weights.py index 33c0e98b9d..ef5cbfdba9 100644 --- a/bittensor/core/extrinsics/asyncex/weights.py +++ b/bittensor/core/extrinsics/asyncex/weights.py @@ -5,9 +5,9 @@ import numpy as np from numpy.typing import NDArray -from bittensor.core.extrinsics.utils import convert_and_normalize_weights_and_uids from bittensor.core.settings import version_as_int from bittensor.utils.btlogging import logging +from bittensor.utils.weight_utils import convert_and_normalize_weights_and_uids if TYPE_CHECKING: from bittensor_wallet import Wallet diff --git a/bittensor/core/extrinsics/commit_reveal.py b/bittensor/core/extrinsics/commit_reveal.py index 73e62aaf8f..ed7e57fc4f 100644 --- a/bittensor/core/extrinsics/commit_reveal.py +++ b/bittensor/core/extrinsics/commit_reveal.py @@ -6,9 +6,9 @@ from bittensor_drand import get_encrypted_commit from numpy.typing import NDArray -from bittensor.core.extrinsics.utils import convert_and_normalize_weights_and_uids from bittensor.core.settings import version_as_int from bittensor.utils.btlogging import logging +from bittensor.utils.weight_utils import convert_and_normalize_weights_and_uids if TYPE_CHECKING: from bittensor_wallet import Wallet diff --git a/bittensor/core/extrinsics/set_weights.py b/bittensor/core/extrinsics/set_weights.py index 7e5714afa3..90f4cff32d 100644 --- a/bittensor/core/extrinsics/set_weights.py +++ b/bittensor/core/extrinsics/set_weights.py @@ -5,10 +5,10 @@ import numpy as np from numpy.typing import NDArray -from bittensor.core.extrinsics.utils import convert_and_normalize_weights_and_uids from bittensor.core.settings import version_as_int from bittensor.utils import format_error_message from bittensor.utils.btlogging import logging +from bittensor.utils.weight_utils import convert_and_normalize_weights_and_uids if TYPE_CHECKING: from bittensor.core.subtensor import Subtensor diff --git a/bittensor/core/extrinsics/utils.py b/bittensor/core/extrinsics/utils.py index e0d2cb8e4f..a23126d1c8 100644 --- a/bittensor/core/extrinsics/utils.py +++ b/bittensor/core/extrinsics/utils.py @@ -47,29 +47,3 @@ def get_old_stakes( ) for hotkey_ss58, netuid in zip(hotkey_ss58s, netuids) ] - - -def convert_and_normalize_weights_and_uids( - uids: Union[NDArray[np.int64], "torch.LongTensor", list], - weights: Union[NDArray[np.float32], "torch.FloatTensor", list], -) -> tuple[list[int], list[int]]: - """Converts weights and uids to numpy arrays if they are not already. - - Arguments: - uids (Union[NDArray[np.int64], torch.LongTensor, list]): The ``uint64`` uids of destination neurons. - weights (Union[NDArray[np.float32], torch.FloatTensor, list]): The weights to set. These must be ``float`` s - and correspond to the passed ``uid`` s. - - Returns: - weight_uids, weight_vals: Bytes converted weights and uids - """ - if isinstance(uids, list): - uids = np.array(uids, dtype=np.int64) - if isinstance(weights, list): - weights = np.array(weights, dtype=np.float32) - - # Reformat and normalize. - weight_uids, weight_vals = convert_weights_and_uids_for_emit( - uids, weights - ) - return weight_uids, weight_vals diff --git a/bittensor/utils/weight_utils.py b/bittensor/utils/weight_utils.py index 5c98b1f383..4939b7bb4e 100644 --- a/bittensor/utils/weight_utils.py +++ b/bittensor/utils/weight_utils.py @@ -190,7 +190,7 @@ def convert_weights_and_uids_for_emit( f"Passed weights and uids must have the same length, got {len(uids)} and {len(weights)}" ) if sum(weights) == 0: - return [], [] # Nothing to set on chain. + return [], [] # Nothing to set on a chain. else: max_weight = float(max(weights)) weights = [ @@ -249,7 +249,7 @@ def process_weights_for_netuid( logging.debug(f"subtensor: {subtensor}") logging.debug(f"metagraph: {metagraph}") - # Get latest metagraph from chain if metagraph is None. + # Get latest metagraph from a chain if metagraph is None. if metagraph is None: metagraph = subtensor.metagraph(netuid) @@ -440,3 +440,49 @@ def generate_weight_hash( commit_hash = "0x" + blake2b_hash.hexdigest() return commit_hash + + +def convert_netuids_and_weights( + netuids: Union[NDArray[np.int64], list], + weights: Union[NDArray[np.float32], list], +) -> tuple[np.ndarray, np.ndarray]: + """Converts netuids and weights to numpy arrays if they are not already. + + Arguments: + netuids (Union[NDArray[np.int64], list]): The uint64 uids of destination neurons. + weights (Union[NDArray[np.float32], list]): The weights to set. These must be floated. + + Returns: + tuple[ndarray, ndarray]: Bytes converted netuids and weights. + """ + if isinstance(netuids, list): + netuids = np.array(netuids, dtype=np.int64) + if isinstance(weights, list): + weights = np.array(weights, dtype=np.float32) + return netuids, weights + + +def convert_and_normalize_weights_and_uids( + uids: Union[NDArray[np.int64], "torch.LongTensor", list], + weights: Union[NDArray[np.float32], "torch.FloatTensor", list], +) -> tuple[list[int], list[int]]: + """Converts weights and uids to numpy arrays if they are not already. + + Arguments: + uids (Union[NDArray[np.int64], torch.LongTensor, list]): The ``uint64`` uids of destination neurons. + weights (Union[NDArray[np.float32], torch.FloatTensor, list]): The weights to set. These must be ``float`` s + and correspond to the passed ``uid`` s. + + Returns: + weight_uids, weight_vals: Bytes converted weights and uids + """ + if isinstance(uids, list): + uids = np.array(uids, dtype=np.int64) + if isinstance(weights, list): + weights = np.array(weights, dtype=np.float32) + + # Reformat and normalize. + weight_uids, weight_vals = convert_weights_and_uids_for_emit( + uids, weights + ) + return weight_uids, weight_vals From fb050f8f4a728a420e80d5f50ec17724f2b5a50f Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 16:48:14 -0700 Subject: [PATCH 103/251] update extrinsics related calls --- bittensor/core/async_subtensor.py | 18 +++++++++++++++--- bittensor/core/subtensor.py | 11 +++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 98c09b26e5..c0f146dd6d 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -3398,6 +3398,9 @@ async def burned_register( period (int): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: bool: ``True`` if the registration is successful, False otherwise. @@ -3409,6 +3412,7 @@ async def burned_register( wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) return await burned_register_extrinsic( @@ -3706,19 +3710,21 @@ async def reveal_weights( async def root_register( self, wallet: "Wallet", - block_hash: Optional[str] = None, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + period: Optional[int] = None, ) -> bool: """ Register neuron by recycling some TAO. Arguments: wallet (bittensor_wallet.Wallet): Bittensor wallet instance. - block_hash (Optional[str]): The hash of the blockchain block for the query. wait_for_inclusion (bool): Waits for the transaction to be included in a block. Default is ``False``. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. Default is ``False``. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: `True` if registration was successful, otherwise `False`. @@ -3729,6 +3735,7 @@ async def root_register( wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) async def root_set_weights( @@ -3739,9 +3746,10 @@ async def root_set_weights( version_key: int = 0, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, + period: Optional[int] = None, ) -> bool: """ - Set weights for root network. + Set weights for the root network. Arguments: wallet (bittensor_wallet.Wallet): bittensor wallet instance. @@ -3752,6 +3760,9 @@ async def root_set_weights( ``False``. wait_for_finalization (bool, optional): Waits for the transaction to be finalized on the blockchain. Defaults to ``False``. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: `True` if the setting of weights is successful, `False` otherwise. @@ -3768,6 +3779,7 @@ async def root_set_weights( version_key=version_key, wait_for_finalization=wait_for_finalization, wait_for_inclusion=wait_for_inclusion, + period=period, ) async def set_children( diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 9b08a25221..43130913f5 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -2839,6 +2839,7 @@ def burned_register( wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) return burned_register_extrinsic( @@ -3141,6 +3142,7 @@ def root_register( wallet: "Wallet", wait_for_inclusion: bool = False, wait_for_finalization: bool = True, + period: Optional[int] = None, ) -> bool: """ Register neuron by recycling some TAO. @@ -3150,6 +3152,9 @@ def root_register( wait_for_inclusion (bool): Waits for the transaction to be included in a block. Default is ``False``. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. Default is ``False``. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: `True` if registration was successful, otherwise `False`. @@ -3160,6 +3165,7 @@ def root_register( wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) def root_set_weights( @@ -3170,6 +3176,7 @@ def root_set_weights( version_key: int = 0, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> bool: """ Set weights for the root network. @@ -3183,6 +3190,9 @@ def root_set_weights( ``False``. wait_for_finalization (bool, optional): Waits for the transaction to be finalized on the blockchain. Defaults to ``False``. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: `True` if the setting of weights is successful, `False` otherwise. @@ -3198,6 +3208,7 @@ def root_set_weights( version_key=version_key, wait_for_finalization=wait_for_finalization, wait_for_inclusion=wait_for_inclusion, + period=period, ) def set_subnet_identity( From d7b156f4439e74e5df7e6c0de8fdf3ad29d63323 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 16:50:22 -0700 Subject: [PATCH 104/251] fix related tests --- .../extrinsics/asyncex/test_root.py | 60 ++++++------------- tests/unit_tests/extrinsics/test_root.py | 1 + tests/unit_tests/test_async_subtensor.py | 1 + tests/unit_tests/test_subtensor_extended.py | 53 ---------------- 4 files changed, 20 insertions(+), 95 deletions(-) diff --git a/tests/unit_tests/extrinsics/asyncex/test_root.py b/tests/unit_tests/extrinsics/asyncex/test_root.py index ecca5a9847..ca221b2cd4 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_root.py +++ b/tests/unit_tests/extrinsics/asyncex/test_root.py @@ -332,7 +332,7 @@ async def test_do_set_root_weights_success(subtensor, fake_wallet, mocker): fake_weights = [0.1, 0.2, 0.7] fake_call = mocker.AsyncMock() - fake_extrinsic = mocker.AsyncMock() + fake_extrinsic = True, "Successfully set weights." fake_response = mocker.Mock() fake_response.is_success = mocker.AsyncMock(return_value=True)() @@ -340,10 +340,7 @@ async def test_do_set_root_weights_success(subtensor, fake_wallet, mocker): mocker.patch.object(subtensor.substrate, "compose_call", return_value=fake_call) mocker.patch.object( - subtensor.substrate, "create_signed_extrinsic", return_value=fake_extrinsic - ) - mocker.patch.object( - subtensor.substrate, "submit_extrinsic", return_value=fake_response + subtensor, "sign_and_send_extrinsic", return_value=fake_extrinsic ) # Call @@ -369,14 +366,13 @@ async def test_do_set_root_weights_success(subtensor, fake_wallet, mocker): "hotkey": "fake_hotkey_address", }, ) - subtensor.substrate.create_signed_extrinsic.assert_called_once_with( + subtensor.sign_and_send_extrinsic.assert_called_once_with( call=fake_call, - keypair=fake_wallet.coldkey, - era={"period": 5}, - nonce=subtensor.substrate.get_account_next_index.return_value, - ) - subtensor.substrate.submit_extrinsic.assert_called_once_with( - extrinsic=fake_extrinsic, wait_for_inclusion=True, wait_for_finalization=True + wallet=fake_wallet, + wait_for_inclusion=True, + wait_for_finalization=True, + use_nonce=True, + period=8, ) assert result is True assert message == "Successfully set weights." @@ -391,29 +387,9 @@ async def test_do_set_root_weights_failure(subtensor, fake_wallet, mocker): fake_weights = [0.1, 0.2, 0.7] fake_call = mocker.AsyncMock() - fake_extrinsic = mocker.AsyncMock() - fake_response = mocker.Mock() - - async def fake_is_success(): - return False - - fake_response.is_success = fake_is_success() - fake_response.process_events = mocker.AsyncMock() - fake_response.error_message = mocker.AsyncMock()() mocker.patch.object(subtensor.substrate, "compose_call", return_value=fake_call) - mocker.patch.object( - subtensor.substrate, "create_signed_extrinsic", return_value=fake_extrinsic - ) - mocker.patch.object( - subtensor.substrate, "submit_extrinsic", return_value=fake_response - ) - - mocked_format_error_message = mocker.patch.object( - async_root, - "format_error_message", - return_value="Transaction failed", - ) + mocker.patch.object(subtensor, "sign_and_send_extrinsic", return_value=(False, "Transaction failed")) # Call result, message = await async_root._do_set_root_weights( @@ -428,7 +404,6 @@ async def fake_is_success(): # Asserts assert result is False - assert message == mocked_format_error_message.return_value @pytest.mark.asyncio @@ -441,14 +416,10 @@ async def test_do_set_root_weights_no_waiting(subtensor, fake_wallet, mocker): fake_call = mocker.AsyncMock() fake_extrinsic = mocker.AsyncMock() - fake_response = mocker.Mock() mocker.patch.object(subtensor.substrate, "compose_call", return_value=fake_call) mocker.patch.object( - subtensor.substrate, "create_signed_extrinsic", return_value=fake_extrinsic - ) - mocker.patch.object( - subtensor.substrate, "submit_extrinsic", return_value=fake_response + subtensor, "sign_and_send_extrinsic", return_value=(True, "Not waiting for finalization or inclusion.") ) # Call @@ -464,9 +435,13 @@ async def test_do_set_root_weights_no_waiting(subtensor, fake_wallet, mocker): # Asserts subtensor.substrate.compose_call.assert_called_once() - subtensor.substrate.create_signed_extrinsic.assert_called_once() - subtensor.substrate.submit_extrinsic.assert_called_once_with( - extrinsic=fake_extrinsic, wait_for_inclusion=False, wait_for_finalization=False + subtensor.sign_and_send_extrinsic.assert_called_once_with( + call=subtensor.substrate.compose_call.return_value, + wallet=fake_wallet, + wait_for_inclusion=False, + wait_for_finalization=False, + use_nonce=True, + period=8, ) assert result is True assert message == "Not waiting for finalization or inclusion." @@ -674,5 +649,6 @@ async def test_set_root_weights_extrinsic_request_exception( version_key=0, wait_for_inclusion=True, wait_for_finalization=True, + period=None, ) mocked_format_error_message.assert_called_once() diff --git a/tests/unit_tests/extrinsics/test_root.py b/tests/unit_tests/extrinsics/test_root.py index a22d456a2e..9324613fe8 100644 --- a/tests/unit_tests/extrinsics/test_root.py +++ b/tests/unit_tests/extrinsics/test_root.py @@ -103,6 +103,7 @@ def test_root_register_extrinsic( wallet=mock_wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=None ) diff --git a/tests/unit_tests/test_async_subtensor.py b/tests/unit_tests/test_async_subtensor.py index 1c409a6658..ddf5148b8c 100644 --- a/tests/unit_tests/test_async_subtensor.py +++ b/tests/unit_tests/test_async_subtensor.py @@ -2804,6 +2804,7 @@ async def test_root_set_weights_success(subtensor, fake_wallet, mocker): version_key=0, wait_for_finalization=True, wait_for_inclusion=True, + period=None, ) assert result == mocked_set_root_weights_extrinsic.return_value diff --git a/tests/unit_tests/test_subtensor_extended.py b/tests/unit_tests/test_subtensor_extended.py index ec67747f97..45f65f24db 100644 --- a/tests/unit_tests/test_subtensor_extended.py +++ b/tests/unit_tests/test_subtensor_extended.py @@ -1291,59 +1291,6 @@ def test_root_register_is_already_registered( mock_substrate.submit_extrinsic.assert_not_called() -def test_root_set_weights(mock_substrate, subtensor, fake_wallet, mocker): - MIN_ALLOWED_WEIGHTS = 0 - MAX_WEIGHTS_LIMIT = 1 - - mock_substrate.query.return_value = 1 - mocker.patch.object( - subtensor, - "get_hyperparameter", - autospec=True, - side_effect=[ - MIN_ALLOWED_WEIGHTS, - MAX_WEIGHTS_LIMIT, - ], - ) - - subtensor.root_set_weights( - fake_wallet, - netuids=[1, 2], - weights=[0.5, 0.5], - ) - - subtensor.get_hyperparameter.assert_has_calls( - [ - mocker.call("MinAllowedWeights", netuid=0), - mocker.call("MaxWeightsLimit", netuid=0), - ] - ) - mock_substrate.query.assert_called_once_with( - "SubtensorModule", - "Uids", - [0, fake_wallet.hotkey.ss58_address], - ) - - assert_submit_signed_extrinsic( - mock_substrate, - fake_wallet.coldkey, - call_module="SubtensorModule", - call_function="set_root_weights", - call_params={ - "dests": [1, 2], - "weights": [65535, 65535], - "netuid": 0, - "version_key": 0, - "hotkey": fake_wallet.hotkey.ss58_address, - }, - era={ - "period": 5, - }, - nonce=mock_substrate.get_account_next_index.return_value, - wait_for_finalization=False, - ) - - def test_root_set_weights_no_uid(mock_substrate, subtensor, fake_wallet, mocker): mock_substrate.query.return_value = None From dba73aa117c91b4ef37a6a59f79e60916582b1a7 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 18:29:37 -0700 Subject: [PATCH 105/251] update `bittensor/core/extrinsics/serving.py` --- bittensor/core/extrinsics/serving.py | 84 +++++++++++++++------------- 1 file changed, 45 insertions(+), 39 deletions(-) diff --git a/bittensor/core/extrinsics/serving.py b/bittensor/core/extrinsics/serving.py index aaed6ad38f..a9309cfe24 100644 --- a/bittensor/core/extrinsics/serving.py +++ b/bittensor/core/extrinsics/serving.py @@ -2,14 +2,13 @@ from bittensor.core.errors import MetadataError from bittensor.core.settings import version_as_int +from bittensor.core.types import AxonServeCallParams from bittensor.utils import ( - format_error_message, networking as net, unlock_key, Certificate, ) from bittensor.utils.btlogging import logging -from bittensor.core.types import AxonServeCallParams if TYPE_CHECKING: from bittensor_wallet import Wallet @@ -23,7 +22,8 @@ def do_serve_axon( call_params: "AxonServeCallParams", wait_for_inclusion: bool = False, wait_for_finalization: bool = True, -) -> tuple[bool, Optional[dict]]: + period: Optional[int] = None, +) -> tuple[bool, str]: """ Internal method to submit a serve axon transaction to the Bittensor blockchain. This method creates and submits a transaction, enabling a neuron's ``Axon`` to serve requests on the network. @@ -34,6 +34,9 @@ def do_serve_axon( call_params (bittensor.core.types.AxonServeCallParams): Parameters required for the serve axon call. wait_for_inclusion (bool): Waits for the transaction to be included in a block. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: tuple[bool, Optional[str]]: A tuple containing a success flag and an optional error message. @@ -51,21 +54,15 @@ def do_serve_axon( call_function=call_function, call_params=call_params.dict(), ) - extrinsic = subtensor.substrate.create_signed_extrinsic( - call=call, keypair=wallet.hotkey - ) - response = subtensor.substrate.submit_extrinsic( - extrinsic=extrinsic, + + success, message = subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) - if wait_for_inclusion or wait_for_finalization: - if response.is_success: - return True, None - - return False, response.error_message - - return True, None + return success, message def serve_extrinsic( @@ -80,6 +77,7 @@ def serve_extrinsic( wait_for_inclusion: bool = False, wait_for_finalization=True, certificate: Optional[Certificate] = None, + period: Optional[int] = None, ) -> bool: """Subscribes a Bittensor endpoint to the subtensor chain. @@ -98,6 +96,9 @@ def serve_extrinsic( ``True``, or returns ``False`` if the extrinsic fails to be finalized within the timeout. certificate (bittensor.utils.Certificate): Certificate to use for TLS. If ``None``, no TLS will be used. Defaults to ``None``. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: success (bool): Flag is ``True`` if extrinsic was finalized or included in the block. If we did not wait for @@ -130,33 +131,33 @@ def serve_extrinsic( neuron_up_to_date = not neuron.is_null and params == neuron if neuron_up_to_date: logging.debug( - f"Axon already served on: AxonInfo({wallet.hotkey.ss58_address},{ip}:{port}) " + f"Axon already served on: [blue]AxonInfo({wallet.hotkey.ss58_address}, {ip}:{port})[/blue]" ) return True logging.debug( - f"Serving axon with: AxonInfo({wallet.hotkey.ss58_address},{ip}:{port}) -> {subtensor.network}:{netuid}" + f"Serving axon with: [blue]AxonInfo({wallet.hotkey.ss58_address}, {ip}:{port})[/blue] -> " + f"[green]{subtensor.network}:{netuid}[/green]" ) - success, error_message = do_serve_axon( + success, message = do_serve_axon( subtensor=subtensor, wallet=wallet, call_params=params, wait_for_finalization=wait_for_finalization, wait_for_inclusion=wait_for_inclusion, + period=period, ) - if wait_for_inclusion or wait_for_finalization: - if success is True: - logging.debug( - f"Axon served with: AxonInfo({wallet.hotkey.ss58_address},{ip}:{port}) on {subtensor.network}:{netuid} " - ) - return True - else: - logging.error(f"Failed: {format_error_message(error_message)}") - return False - else: + if success: + logging.debug( + f"Axon served with: [blue]AxonInfo({wallet.hotkey.ss58_address}, {ip}:{port})[/blue] on " + f"[green]{subtensor.network}:{netuid}[/green]" + ) return True + logging.error(f"Failed: {message}") + return False + def serve_axon_extrinsic( subtensor: "Subtensor", @@ -165,6 +166,7 @@ def serve_axon_extrinsic( wait_for_inclusion: bool = False, wait_for_finalization: bool = True, certificate: Optional["Certificate"] = None, + period: Optional[int] = None, ) -> bool: """Serves the axon to the network. @@ -178,6 +180,9 @@ def serve_axon_extrinsic( ``True``, or returns ``False`` if the extrinsic fails to be finalized within the timeout. certificate (bittensor.utils.Certificate): Certificate to use for TLS. If ``None``, no TLS will be used. Defaults to ``None``. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: success (bool): Flag is ``True`` if extrinsic was finalized or included in the block. If we did not wait for @@ -213,6 +218,7 @@ def serve_axon_extrinsic( wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, certificate=certificate, + period=period, ) return serve_success @@ -225,6 +231,7 @@ def publish_metadata( data: Union[bytes, dict], wait_for_inclusion: bool = False, wait_for_finalization: bool = True, + period: Optional[int] = None, ) -> bool: """ Publishes metadata on the Bittensor network using the specified wallet and network identifier. @@ -242,12 +249,15 @@ def publish_metadata( block before returning. Defaults to ``False``. wait_for_finalization (bool, optional): If ``True``, the function will wait for the extrinsic to be finalized on the chain before returning. Defaults to ``True``. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: bool: ``True`` if the metadata was successfully published (and finalized if specified). ``False`` otherwise. Raises: - MetadataError: If there is an error in submitting the extrinsic or if the response from the blockchain indicates + MetadataError: If there is an error in submitting the extrinsic, or if the response from the blockchain indicates failure. """ @@ -264,21 +274,17 @@ def publish_metadata( }, ) - extrinsic = subtensor.substrate.create_signed_extrinsic( - call=call, keypair=wallet.hotkey - ) - response = subtensor.substrate.submit_extrinsic( - extrinsic=extrinsic, + success, message = subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) - # We only wait here if we expect finalization. - if not wait_for_finalization and not wait_for_inclusion: - return True - if response.is_success: + if success: return True - raise MetadataError(format_error_message(response.error_message)) + raise MetadataError(message) def get_metadata( From 904f9b2ff443381b814293081be8c901d78c9a2c Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 18:29:56 -0700 Subject: [PATCH 106/251] update `bittensor/core/extrinsics/asyncex/serving.py` --- bittensor/core/extrinsics/asyncex/serving.py | 85 +++++++++++--------- 1 file changed, 45 insertions(+), 40 deletions(-) diff --git a/bittensor/core/extrinsics/asyncex/serving.py b/bittensor/core/extrinsics/asyncex/serving.py index 558b58cae5..aafde7dd02 100644 --- a/bittensor/core/extrinsics/asyncex/serving.py +++ b/bittensor/core/extrinsics/asyncex/serving.py @@ -3,14 +3,13 @@ from bittensor.core.errors import MetadataError from bittensor.core.settings import version_as_int +from bittensor.core.types import AxonServeCallParams from bittensor.utils import ( - format_error_message, networking as net, unlock_key, Certificate, ) from bittensor.utils.btlogging import logging -from bittensor.core.types import AxonServeCallParams if TYPE_CHECKING: from bittensor.core.axon import Axon @@ -24,7 +23,8 @@ async def do_serve_axon( call_params: "AxonServeCallParams", wait_for_inclusion: bool = False, wait_for_finalization: bool = True, -) -> tuple[bool, Optional[dict]]: + period: Optional[int] = None, +) -> tuple[bool, str]: """ Internal method to submit a serve axon transaction to the Bittensor blockchain. This method creates and submits a transaction, enabling a neuron's ``Axon`` to serve requests on the network. @@ -35,9 +35,12 @@ async def do_serve_axon( call_params (bittensor.core.types.AxonServeCallParams): Parameters required for the serve axon call. wait_for_inclusion (bool): Waits for the transaction to be included in a block. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: - tuple[bool, Optional[str]]: A tuple containing a success flag and an optional error message. + tuple[bool, str]: A tuple containing a success flag and an optional error message. This function is crucial for initializing and announcing a neuron's ``Axon`` service on the network, enhancing the decentralized computation capabilities of Bittensor. @@ -53,21 +56,14 @@ async def do_serve_axon( call_function=call_function, call_params=call_params.dict(), ) - extrinsic = await subtensor.substrate.create_signed_extrinsic( - call=call, keypair=wallet.hotkey - ) - response = await subtensor.substrate.submit_extrinsic( - extrinsic=extrinsic, + success, message = await subtensor.sign_and_send_extrinsic( + call, + wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) - if wait_for_inclusion or wait_for_finalization: - if await response.is_success: - return True, None - - return False, await response.error_message - - return True, None + return success, message async def serve_extrinsic( @@ -82,6 +78,7 @@ async def serve_extrinsic( wait_for_inclusion: bool = False, wait_for_finalization=True, certificate: Optional[Certificate] = None, + period: Optional[int] = None, ) -> bool: """Subscribes a Bittensor endpoint to the subtensor chain. @@ -100,6 +97,9 @@ async def serve_extrinsic( ``True``, or returns ``False`` if the extrinsic fails to be finalized within the timeout. certificate (bittensor.utils.Certificate): Certificate to use for TLS. If ``None``, no TLS will be used. Defaults to ``None``. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: success (bool): Flag is ``True`` if extrinsic was finalized or included in the block. If we did not wait for @@ -132,33 +132,33 @@ async def serve_extrinsic( neuron_up_to_date = not neuron.is_null and params == neuron if neuron_up_to_date: logging.debug( - f"Axon already served on: AxonInfo({wallet.hotkey.ss58_address},{ip}:{port}) " + f"Axon already served on: [blue]AxonInfo({wallet.hotkey.ss58_address}, {ip}:{port})[/blue]" ) return True logging.debug( - f"Serving axon with: AxonInfo({wallet.hotkey.ss58_address},{ip}:{port}) -> {subtensor.network}:{netuid}" + f"Serving axon with: [blue]AxonInfo({wallet.hotkey.ss58_address}, {ip}:{port})[/blue] -> " + f"[green]{subtensor.network}:{netuid}[/green]" ) - success, error_message = await do_serve_axon( + success, message = do_serve_axon( subtensor=subtensor, wallet=wallet, call_params=params, wait_for_finalization=wait_for_finalization, wait_for_inclusion=wait_for_inclusion, + period=period, ) - if wait_for_inclusion or wait_for_finalization: - if success is True: - logging.debug( - f"Axon served with: AxonInfo({wallet.hotkey.ss58_address},{ip}:{port}) on {subtensor.network}:{netuid} " - ) - return True - else: - logging.error(f"Failed: {format_error_message(error_message)}") - return False - else: + if success: + logging.debug( + f"Axon served with: [blue]AxonInfo({wallet.hotkey.ss58_address}, {ip}:{port})[/blue] on " + f"[green]{subtensor.network}:{netuid}[/green]" + ) return True + logging.error(f"Failed: {message}") + return False + async def serve_axon_extrinsic( subtensor: "AsyncSubtensor", @@ -167,6 +167,7 @@ async def serve_axon_extrinsic( wait_for_inclusion: bool = False, wait_for_finalization: bool = True, certificate: Optional[Certificate] = None, + period: Optional[int] = None, ) -> bool: """Serves the axon to the network. @@ -180,6 +181,9 @@ async def serve_axon_extrinsic( ``True``, or returns ``False`` if the extrinsic fails to be finalized within the timeout. certificate (bittensor.utils.Certificate): Certificate to use for TLS. If ``None``, no TLS will be used. Defaults to ``None``. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: success (bool): Flag is ``True`` if extrinsic was finalized or included in the block. If we did not wait for @@ -217,6 +221,7 @@ async def serve_axon_extrinsic( wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, certificate=certificate, + period=period ) return serve_success @@ -229,6 +234,7 @@ async def publish_metadata( data: Union[bytes, dict], wait_for_inclusion: bool = False, wait_for_finalization: bool = True, + period: Optional[int] = None, ) -> bool: """ Publishes metadata on the Bittensor network using the specified wallet and network identifier. @@ -246,12 +252,15 @@ async def publish_metadata( block before returning. Defaults to ``False``. wait_for_finalization (bool, optional): If ``True``, the function will wait for the extrinsic to be finalized on the chain before returning. Defaults to ``True``. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: bool: ``True`` if the metadata was successfully published (and finalized if specified). ``False`` otherwise. Raises: - MetadataError: If there is an error in submitting the extrinsic or if the response from the blockchain indicates + MetadataError: If there is an error in submitting the extrinsic, or if the response from the blockchain indicates failure. """ @@ -269,21 +278,17 @@ async def publish_metadata( }, ) - extrinsic = await substrate.create_signed_extrinsic( - call=call, keypair=wallet.hotkey - ) - response = await substrate.submit_extrinsic( - extrinsic=extrinsic, + success, message = await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) - # We only wait here if we expect finalization. - if not wait_for_finalization and not wait_for_inclusion: - return True - if await response.is_success: + if success: return True - raise MetadataError(format_error_message(await response.error_message)) + raise MetadataError(message) async def get_metadata( From 9a27b6ac34b696889d6e3e33851f4bbe391c3210 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 18:30:50 -0700 Subject: [PATCH 107/251] related subtensors updates --- bittensor/core/async_subtensor.py | 20 ++++++++++++++++++-- bittensor/core/subtensor.py | 25 ++++++++++++++++++++----- 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index c0f146dd6d..451706fd1f 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -653,7 +653,7 @@ async def bonds( return b_map - async def commit(self, wallet: "Wallet", netuid: int, data: str) -> bool: + async def commit(self, wallet: "Wallet", netuid: int, data: str, period: Optional[int] = None) -> bool: """ Commits arbitrary data to the Bittensor network by publishing metadata. @@ -661,6 +661,12 @@ async def commit(self, wallet: "Wallet", netuid: int, data: str) -> bool: wallet (bittensor_wallet.Wallet): The wallet associated with the neuron committing the data. netuid (int): The unique identifier of the subnetwork. data (str): The data to be committed to the network. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. + + Return: + bool: `True` if the commit was successful, `False` otherwise. """ return await publish_metadata( subtensor=self, @@ -2850,6 +2856,7 @@ async def set_reveal_commitment( data: str, blocks_until_reveal: int = 360, block_time: Union[int, float] = 12, + period: Optional[int] = None ) -> tuple[bool, int]: """ Commits arbitrary data to the Bittensor network by publishing metadata. @@ -2859,8 +2866,11 @@ async def set_reveal_commitment( netuid (int): The unique identifier of the subnetwork. data (str): The data to be committed to the network. blocks_until_reveal (int): The number of blocks from now after which the data will be revealed. Defaults to `360`. - Then amount of blocks in one epoch. + The number of blocks in one epoch. block_time (Union[int, float]): The number of seconds between each block. Defaults to `12`. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: bool: `True` if the commitment was successful, `False` otherwise. @@ -2881,6 +2891,7 @@ async def set_reveal_commitment( netuid=netuid, data_type=f"TimelockEncrypted", data=data_, + period=period ), reveal_round async def subnet( @@ -4093,6 +4104,7 @@ async def serve_axon( wait_for_inclusion: bool = False, wait_for_finalization: bool = True, certificate: Optional[Certificate] = None, + period: Optional[int] = None, ) -> bool: """ Registers an ``Axon`` serving endpoint on the Bittensor network for a specific neuron. This function is used to @@ -4106,6 +4118,9 @@ async def serve_axon( ``True``. certificate (bittensor.utils.Certificate): Certificate to use for TLS. If ``None``, no TLS will be used. Defaults to ``None``. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: bool: ``True`` if the Axon serve registration is successful, False otherwise. @@ -4120,6 +4135,7 @@ async def serve_axon( wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, certificate=certificate, + period=period, ) async def start_call( diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 43130913f5..ac8e1b0710 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -452,7 +452,7 @@ def bonds( return b_map - def commit(self, wallet, netuid: int, data: str) -> bool: + def commit(self, wallet, netuid: int, data: str, period: Optional[int] = None) -> bool: """ Commits arbitrary data to the Bittensor network by publishing metadata. @@ -460,6 +460,12 @@ def commit(self, wallet, netuid: int, data: str) -> bool: wallet (bittensor_wallet.Wallet): The wallet associated with the neuron committing the data. netuid (int): The unique identifier of the subnetwork. data (str): The data to be committed to the network. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. + + Returns: + bool: `True` if the commitment was successful, `False` otherwise. """ return publish_metadata( subtensor=self, @@ -467,6 +473,7 @@ def commit(self, wallet, netuid: int, data: str) -> bool: netuid=netuid, data_type=f"Raw{len(data)}", data=data.encode(), + period=period, ) # add explicit alias @@ -476,7 +483,7 @@ def commit_reveal_enabled( self, netuid: int, block: Optional[int] = None ) -> Optional[bool]: """ - Check if commit-reveal mechanism is enabled for a given network at a specific block. + Check if a commit-reveal mechanism is enabled for a given network at a specific block. Arguments: netuid: The network identifier for which to check the commit-reveal mechanism. @@ -2368,6 +2375,7 @@ def set_reveal_commitment( data: str, blocks_until_reveal: int = 360, block_time: Union[int, float] = 12, + period: Optional[int] = None, ) -> tuple[bool, int]: """ Commits arbitrary data to the Bittensor network by publishing metadata. @@ -2377,9 +2385,11 @@ def set_reveal_commitment( netuid (int): The unique identifier of the subnetwork. data (str): The data to be committed to the network. blocks_until_reveal (int): The number of blocks from now after which the data will be revealed. Defaults to `360`. - Then amount of blocks in one epoch. + Then number of blocks in one epoch. block_time (Union[int, float]): The number of seconds between each block. Defaults to `12`. - + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: bool: `True` if the commitment was successful, `False` otherwise. @@ -2399,6 +2409,7 @@ def set_reveal_commitment( netuid=netuid, data_type=f"TimelockEncrypted", data=data_, + period=period, ), reveal_round def subnet(self, netuid: int, block: Optional[int] = None) -> Optional[DynamicInfo]: @@ -3366,6 +3377,7 @@ def serve_axon( wait_for_inclusion: bool = False, wait_for_finalization: bool = True, certificate: Optional[Certificate] = None, + period: Optional[int] = None, ) -> bool: """ Registers an ``Axon`` serving endpoint on the Bittensor network for a specific neuron. This function is used to @@ -3379,6 +3391,9 @@ def serve_axon( ``True``. certificate (bittensor.utils.Certificate): Certificate to use for TLS. If ``None``, no TLS will be used. Defaults to ``None``. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: bool: ``True`` if the Axon serve registration is successful, False otherwise. @@ -3393,6 +3408,7 @@ def serve_axon( wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, certificate=certificate, + period=period, ) def start_call( @@ -3464,7 +3480,6 @@ def swap_stake( the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. - Returns: success (bool): True if the extrinsic was successful. From e5d2264cc83b46d406522f23ede67245816d5c1e Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 18:31:43 -0700 Subject: [PATCH 108/251] fix tests --- tests/unit_tests/extrinsics/test_serving.py | 15 ++----- tests/unit_tests/test_subtensor.py | 48 +++++++++------------ 2 files changed, 23 insertions(+), 40 deletions(-) diff --git a/tests/unit_tests/extrinsics/test_serving.py b/tests/unit_tests/extrinsics/test_serving.py index 8fd01ef6ef..027199ca81 100644 --- a/tests/unit_tests/extrinsics/test_serving.py +++ b/tests/unit_tests/extrinsics/test_serving.py @@ -321,7 +321,7 @@ def test_serve_axon_extrinsic( 1, "Sha256", b"mock_bytes_data", - True, + (True, ""), True, "happy-path-wait", ), @@ -331,7 +331,7 @@ def test_serve_axon_extrinsic( 1, "Sha256", b"mock_bytes_data", - True, + (True, ""), True, "happy-path-no-wait", ), @@ -353,16 +353,7 @@ def test_publish_metadata( # Arrange with ( patch.object(mock_subtensor.substrate, "compose_call"), - patch.object(mock_subtensor.substrate, "create_signed_extrinsic"), - patch.object( - mock_subtensor.substrate, - "submit_extrinsic", - return_value=MagicMock( - is_success=response_success, - process_events=MagicMock(), - error_message="error", - ), - ), + patch.object(mock_subtensor, "sign_and_send_extrinsic", return_value=response_success), ): # Act result = serving.publish_metadata( diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index 9160ae4c2a..1eba075158 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -1234,6 +1234,7 @@ def test_serve_axon(subtensor, mocker): wait_for_inclusion=fake_wait_for_inclusion, wait_for_finalization=fake_wait_for_finalization, certificate=fake_certificate, + period=None, ) assert result == mocked_serve_axon_extrinsic.return_value @@ -1252,7 +1253,7 @@ def test_get_block_hash(subtensor, mocker): def test_commit(subtensor, fake_wallet, mocker): - """Test successful commit call.""" + """Test a successful commit call.""" # Preps fake_netuid = 1 fake_data = "some data to network" @@ -1268,6 +1269,7 @@ def test_commit(subtensor, fake_wallet, mocker): netuid=fake_netuid, data_type=f"Raw{len(fake_data)}", data=fake_data.encode(), + period=None ) assert result is mocked_publish_metadata.return_value @@ -1466,7 +1468,7 @@ def test_do_serve_axon_is_success( fake_wait_for_inclusion = True fake_wait_for_finalization = True - subtensor.substrate.submit_extrinsic.return_value.is_success = True + mocker.patch.object(subtensor, "sign_and_send_extrinsic", return_value=(True, "")) # Call result = do_serve_axon( @@ -1484,20 +1486,16 @@ def test_do_serve_axon_is_success( call_params=fake_call_params, ) - subtensor.substrate.create_signed_extrinsic.assert_called_once_with( + subtensor.sign_and_send_extrinsic.assert_called_once_with( call=subtensor.substrate.compose_call.return_value, - keypair=fake_wallet.hotkey, - ) - - subtensor.substrate.submit_extrinsic.assert_called_once_with( - extrinsic=subtensor.substrate.create_signed_extrinsic.return_value, + wallet=fake_wallet, wait_for_inclusion=fake_wait_for_inclusion, wait_for_finalization=fake_wait_for_finalization, + period=None ) - # subtensor.substrate.submit_extrinsic.return_value.process_events.assert_called_once() assert result[0] is True - assert result[1] is None + assert result[1] is "" def test_do_serve_axon_is_not_success(subtensor, fake_wallet, mocker, fake_call_params): @@ -1506,7 +1504,7 @@ def test_do_serve_axon_is_not_success(subtensor, fake_wallet, mocker, fake_call_ fake_wait_for_inclusion = True fake_wait_for_finalization = True - subtensor.substrate.submit_extrinsic.return_value.is_success = None + mocker.patch.object(subtensor, "sign_and_send_extrinsic", return_value=(False, None)) # Call result = do_serve_axon( @@ -1524,21 +1522,15 @@ def test_do_serve_axon_is_not_success(subtensor, fake_wallet, mocker, fake_call_ call_params=fake_call_params, ) - subtensor.substrate.create_signed_extrinsic.assert_called_once_with( + subtensor.sign_and_send_extrinsic.assert_called_once_with( call=subtensor.substrate.compose_call.return_value, - keypair=fake_wallet.hotkey, - ) - - subtensor.substrate.submit_extrinsic.assert_called_once_with( - extrinsic=subtensor.substrate.create_signed_extrinsic.return_value, + wallet=fake_wallet, wait_for_inclusion=fake_wait_for_inclusion, wait_for_finalization=fake_wait_for_finalization, + period=None ) - assert result == ( - False, - subtensor.substrate.submit_extrinsic.return_value.error_message, - ) + assert result == (False, None) def test_do_serve_axon_no_waits(subtensor, fake_wallet, mocker, fake_call_params): @@ -1547,6 +1539,9 @@ def test_do_serve_axon_no_waits(subtensor, fake_wallet, mocker, fake_call_params fake_wait_for_inclusion = False fake_wait_for_finalization = False + mocked_sign_and_send_extrinsic = mocker.Mock(return_value=(True, "")) + mocker.patch.object(subtensor, "sign_and_send_extrinsic", new=mocked_sign_and_send_extrinsic) + # Call result = do_serve_axon( subtensor=subtensor, @@ -1563,17 +1558,14 @@ def test_do_serve_axon_no_waits(subtensor, fake_wallet, mocker, fake_call_params call_params=fake_call_params, ) - subtensor.substrate.create_signed_extrinsic.assert_called_once_with( + mocked_sign_and_send_extrinsic.assert_called_once_with( call=subtensor.substrate.compose_call.return_value, - keypair=fake_wallet.hotkey, - ) - - subtensor.substrate.submit_extrinsic.assert_called_once_with( - extrinsic=subtensor.substrate.create_signed_extrinsic.return_value, + wallet=fake_wallet, wait_for_inclusion=fake_wait_for_inclusion, wait_for_finalization=fake_wait_for_finalization, + period=None ) - assert result == (True, None) + assert result == (True, "") def test_immunity_period(subtensor, mocker): From a36ecb16013521aa9479c3e23a04995addd1093f Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 20:45:24 -0700 Subject: [PATCH 109/251] update `bittensor/core/extrinsics/asyncex/weights.py` --- bittensor/core/extrinsics/asyncex/weights.py | 34 ++++++++++++++------ 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/bittensor/core/extrinsics/asyncex/weights.py b/bittensor/core/extrinsics/asyncex/weights.py index ef5cbfdba9..37693927a1 100644 --- a/bittensor/core/extrinsics/asyncex/weights.py +++ b/bittensor/core/extrinsics/asyncex/weights.py @@ -250,7 +250,7 @@ async def _do_set_weights( version_key: int = version_as_int, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, - period: int = 5, + period: Optional[int] = None, ) -> tuple[bool, str]: # (success, error_message) """ Internal method to send a transaction to the Bittensor blockchain, setting weights for specified neurons. This @@ -265,7 +265,9 @@ async def _do_set_weights( version_key (int, optional): Version key for compatibility with the network. wait_for_inclusion (bool, optional): Waits for the transaction to be included in a block. wait_for_finalization (bool, optional): Waits for the transaction to be finalized on the blockchain. - period (int, optional): The period in seconds to wait for extrinsic inclusion or finalization. Defaults to 5. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: tuple[bool, str]: @@ -285,7 +287,7 @@ async def _do_set_weights( "version_key": version_key, }, ) - return await subtensor.sign_and_send_extrinsic( + success, message = await subtensor.sign_and_send_extrinsic( call, wallet, wait_for_inclusion, @@ -296,6 +298,14 @@ async def _do_set_weights( sign_with="hotkey", ) + # We only wait here if we expect finalization. + if not wait_for_finalization and not wait_for_inclusion: + return True, "Not waiting for finalization or inclusion." + + if success: + return success, "Successfully set weights." + return success, message + async def set_weights_extrinsic( subtensor: "AsyncSubtensor", @@ -306,7 +316,7 @@ async def set_weights_extrinsic( version_key: int = 0, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, - period: int = 5, + period: Optional[int] = 8, ) -> tuple[bool, str]: """Sets the given weights and values on a chain for a wallet hotkey account. @@ -322,7 +332,9 @@ async def set_weights_extrinsic( returns ``False`` if the extrinsic fails to enter the block within the timeout. wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning ``True``, or returns ``False`` if the extrinsic fails to be finalized within the timeout. - period (int, optional): The period in seconds to wait for extrinsic inclusion or finalization. Defaults to 5. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: tuple[bool, str]: @@ -332,10 +344,12 @@ async def set_weights_extrinsic( weight_uids, weight_vals = convert_and_normalize_weights_and_uids(uids, weights) logging.info( - f":satellite: [magenta]Setting weights on [/magenta][blue]{subtensor.network}[/blue] [magenta]...[/magenta]" + f":satellite: [magenta]Setting weights on [/magenta]" + f"[blue]{subtensor.network}[/blue] " + f"[magenta]...[/magenta]" ) try: - success, error_message = await _do_set_weights( + success, message = await _do_set_weights( subtensor=subtensor, wallet=wallet, netuid=netuid, @@ -348,15 +362,15 @@ async def set_weights_extrinsic( ) if not wait_for_finalization and not wait_for_inclusion: - return True, "Not waiting for finalization or inclusion." + return True, message if success is True: message = "Successfully set weights and Finalized." logging.success(f":white_heavy_check_mark: [green]{message}[/green]") return True, message - logging.error(f"[red]Failed[/red] set weights. Error: {error_message}") - return False, error_message + logging.error(f"[red]Failed[/red] set weights. Error: {message}") + return False, message except Exception as error: logging.error(f":cross_mark: [red]Failed[/red] set weights. Error: {error}") From a2418b1a02968dc58709ec9ada3db9f348ac6087 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 20:45:40 -0700 Subject: [PATCH 110/251] update `bittensor/core/extrinsics/set_weights.py` --- bittensor/core/extrinsics/set_weights.py | 59 +++++++++++------------- 1 file changed, 26 insertions(+), 33 deletions(-) diff --git a/bittensor/core/extrinsics/set_weights.py b/bittensor/core/extrinsics/set_weights.py index 90f4cff32d..6b30b78c5d 100644 --- a/bittensor/core/extrinsics/set_weights.py +++ b/bittensor/core/extrinsics/set_weights.py @@ -8,7 +8,7 @@ from bittensor.core.settings import version_as_int from bittensor.utils import format_error_message from bittensor.utils.btlogging import logging -from bittensor.utils.weight_utils import convert_and_normalize_weights_and_uids +from bittensor.utils.weight_utils import convert_and_normalize_weights_and_uids, convert_netuids_and_weights if TYPE_CHECKING: from bittensor.core.subtensor import Subtensor @@ -25,8 +25,8 @@ def _do_set_weights( version_key: int = version_as_int, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, - period: int = 5, -) -> tuple[bool, Optional[str]]: # (success, error_message) + period: Optional[int] = None, +) -> tuple[bool, str]: """ Internal method to send a transaction to the Bittensor blockchain, setting weights for specified neurons. This method constructs and submits the transaction, handling @@ -41,10 +41,12 @@ def _do_set_weights( version_key (int, optional): Version key for compatibility with the network. wait_for_inclusion (bool, optional): Waits for the transaction to be included in a block. wait_for_finalization (bool, optional): Waits for the transaction to be finalized on the blockchain. - period (int, optional): The period in seconds to wait for extrinsic inclusion or finalization. Defaults to 5. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: - Tuple[bool, Optional[str]]: A tuple containing a success flag and an optional error message. + Tuple[bool, str]: A tuple containing a success flag and an optional error message. This method is vital for the dynamic weighting mechanism in Bittensor, where neurons adjust their trust in other neurons based on observed performance and contributions. @@ -60,27 +62,17 @@ def _do_set_weights( "version_key": version_key, }, ) - next_nonce = subtensor.substrate.get_account_next_index(wallet.hotkey.ss58_address) - # Period dictates how long the extrinsic will stay as part of the waiting pool - extrinsic = subtensor.substrate.create_signed_extrinsic( + success, message = subtensor.sign_and_send_extrinsic( call=call, - keypair=wallet.hotkey, - era={"period": period}, - nonce=next_nonce, - ) - response = subtensor.substrate.submit_extrinsic( - extrinsic=extrinsic, + wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, + use_nonce=True, + nonce_key="hotkey", + sign_with="hotkey", ) - # We only wait here if we expect finalization. - if not wait_for_finalization and not wait_for_inclusion: - return True, "Not waiting for finalization or inclusion." - - if response.is_success: - return True, "Successfully set weights." - - return False, format_error_message(response.error_message) + return success, message def set_weights_extrinsic( @@ -92,7 +84,7 @@ def set_weights_extrinsic( version_key: int = 0, wait_for_inclusion: bool = False, wait_for_finalization: bool = False, - period: int = 5, + period: Optional[int] = 8, ) -> tuple[bool, str]: """Sets the given weights and values on a chain for a wallet hotkey account. @@ -108,26 +100,27 @@ def set_weights_extrinsic( returns ``False`` if the extrinsic fails to enter the block within the timeout. wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning ``True``, or returns ``False`` if the extrinsic fails to be finalized within the timeout. - period (int, optional): The period in seconds to wait for extrinsic inclusion or finalization. Defaults to 5. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: success (bool): Flag is ``True`` if extrinsic was finalized or included in the block. If we did not wait for finalization / inclusion, the response is ``True``. """ # Convert types. - if isinstance(uids, list): - uids = np.array(uids, dtype=np.int64) - if isinstance(weights, list): - weights = np.array(weights, dtype=np.float32) + uids, weights = convert_netuids_and_weights(uids, weights) # Reformat and normalize. weight_uids, weight_vals = convert_and_normalize_weights_and_uids(uids, weights) logging.info( - f":satellite: [magenta]Setting weights on [/magenta][blue]{subtensor.network}[/blue] [magenta]...[/magenta]" + f":satellite: [magenta]Setting weights on [/magenta]" + f"[blue]{subtensor.network}[/blue] " + f"[magenta]...[/magenta]" ) try: - success, error_message = _do_set_weights( + success, message = _do_set_weights( subtensor=subtensor, wallet=wallet, netuid=netuid, @@ -140,15 +133,15 @@ def set_weights_extrinsic( ) if not wait_for_finalization and not wait_for_inclusion: - return True, "Not waiting for finalization or inclusion." + return True, message if success is True: message = "Successfully set weights and Finalized." logging.success(f":white_heavy_check_mark: [green]{message}[/green]") return True, message - logging.error(f"[red]Failed[/red] set weights. Error: {error_message}") - return False, error_message + logging.error(f"[red]Failed[/red] set weights. Error: {message}") + return False, message except Exception as error: logging.error(f":cross_mark: [red]Failed[/red] set weights. Error: {error}") From cc5344ca6d49ab4162476447777636335d69596c Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 21:07:23 -0700 Subject: [PATCH 111/251] rename `convert_uids_and_weights` --- bittensor/core/extrinsics/asyncex/root.py | 6 ++--- bittensor/core/extrinsics/root.py | 4 ++-- bittensor/core/extrinsics/set_weights.py | 5 ++--- bittensor/utils/weight_utils.py | 27 +++++++++-------------- 4 files changed, 17 insertions(+), 25 deletions(-) diff --git a/bittensor/core/extrinsics/asyncex/root.py b/bittensor/core/extrinsics/asyncex/root.py index 0170da5236..852f004daf 100644 --- a/bittensor/core/extrinsics/asyncex/root.py +++ b/bittensor/core/extrinsics/asyncex/root.py @@ -11,7 +11,7 @@ from bittensor.utils.weight_utils import ( normalize_max_weight, convert_weights_and_uids_for_emit, - convert_netuids_and_weights + convert_uids_and_weights ) if TYPE_CHECKING: @@ -208,7 +208,7 @@ async def _do_set_root_weights( # We only wait here if we expect finalization. if not wait_for_finalization and not wait_for_inclusion: - return True, "Not waiting for finalization or inclusion." + return True, message if success: return True, "Successfully set weights." @@ -260,7 +260,7 @@ async def set_root_weights_extrinsic( return False # Convert types. - netuids, weights = convert_netuids_and_weights(netuids, weights) + netuids, weights = convert_uids_and_weights(netuids, weights) logging.debug("[magenta]Fetching weight limits ...[/magenta]") min_allowed_weights, max_weight_limit = await _get_limits(subtensor) diff --git a/bittensor/core/extrinsics/root.py b/bittensor/core/extrinsics/root.py index 7272eae296..cc944a7c62 100644 --- a/bittensor/core/extrinsics/root.py +++ b/bittensor/core/extrinsics/root.py @@ -16,7 +16,7 @@ from bittensor.utils.weight_utils import ( normalize_max_weight, convert_weights_and_uids_for_emit, - convert_netuids_and_weights + convert_uids_and_weights ) if TYPE_CHECKING: @@ -261,7 +261,7 @@ def set_root_weights_extrinsic( return False # Convert types. - netuids, weights = convert_netuids_and_weights(netuids, weights) + netuids, weights = convert_uids_and_weights(netuids, weights) logging.debug("[magenta]Fetching weight limits ...[/magenta]") min_allowed_weights, max_weight_limit = _get_limits(subtensor) diff --git a/bittensor/core/extrinsics/set_weights.py b/bittensor/core/extrinsics/set_weights.py index 6b30b78c5d..eea4ddbb0e 100644 --- a/bittensor/core/extrinsics/set_weights.py +++ b/bittensor/core/extrinsics/set_weights.py @@ -6,9 +6,8 @@ from numpy.typing import NDArray from bittensor.core.settings import version_as_int -from bittensor.utils import format_error_message from bittensor.utils.btlogging import logging -from bittensor.utils.weight_utils import convert_and_normalize_weights_and_uids, convert_netuids_and_weights +from bittensor.utils.weight_utils import convert_and_normalize_weights_and_uids, convert_uids_and_weights if TYPE_CHECKING: from bittensor.core.subtensor import Subtensor @@ -109,7 +108,7 @@ def set_weights_extrinsic( finalization / inclusion, the response is ``True``. """ # Convert types. - uids, weights = convert_netuids_and_weights(uids, weights) + uids, weights = convert_uids_and_weights(uids, weights) # Reformat and normalize. weight_uids, weight_vals = convert_and_normalize_weights_and_uids(uids, weights) diff --git a/bittensor/utils/weight_utils.py b/bittensor/utils/weight_utils.py index 4939b7bb4e..3a24f5fcc2 100644 --- a/bittensor/utils/weight_utils.py +++ b/bittensor/utils/weight_utils.py @@ -442,29 +442,29 @@ def generate_weight_hash( return commit_hash -def convert_netuids_and_weights( - netuids: Union[NDArray[np.int64], list], +def convert_uids_and_weights( + uids: Union[NDArray[np.int64], list], weights: Union[NDArray[np.float32], list], ) -> tuple[np.ndarray, np.ndarray]: """Converts netuids and weights to numpy arrays if they are not already. Arguments: - netuids (Union[NDArray[np.int64], list]): The uint64 uids of destination neurons. + uids (Union[NDArray[np.int64], list]): The uint64 uids of destination neurons. weights (Union[NDArray[np.float32], list]): The weights to set. These must be floated. Returns: tuple[ndarray, ndarray]: Bytes converted netuids and weights. """ - if isinstance(netuids, list): - netuids = np.array(netuids, dtype=np.int64) + if isinstance(uids, list): + uids = np.array(uids, dtype=np.int64) if isinstance(weights, list): weights = np.array(weights, dtype=np.float32) - return netuids, weights + return uids, weights def convert_and_normalize_weights_and_uids( - uids: Union[NDArray[np.int64], "torch.LongTensor", list], - weights: Union[NDArray[np.float32], "torch.FloatTensor", list], + uids: Union[NDArray[np.int64], "torch.LongTensor", list], + weights: Union[NDArray[np.float32], "torch.FloatTensor", list], ) -> tuple[list[int], list[int]]: """Converts weights and uids to numpy arrays if they are not already. @@ -476,13 +476,6 @@ def convert_and_normalize_weights_and_uids( Returns: weight_uids, weight_vals: Bytes converted weights and uids """ - if isinstance(uids, list): - uids = np.array(uids, dtype=np.int64) - if isinstance(weights, list): - weights = np.array(weights, dtype=np.float32) - # Reformat and normalize. - weight_uids, weight_vals = convert_weights_and_uids_for_emit( - uids, weights - ) - return weight_uids, weight_vals + # Reformat and normalize and return + return convert_weights_and_uids_for_emit(*convert_uids_and_weights(uids, weights)) From ca92a81f52cda9bb5becb93e27ddaa65d699d105 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 21:07:51 -0700 Subject: [PATCH 112/251] refactoring --- bittensor/core/extrinsics/asyncex/registration.py | 8 ++++---- bittensor/core/extrinsics/commit_reveal.py | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/bittensor/core/extrinsics/asyncex/registration.py b/bittensor/core/extrinsics/asyncex/registration.py index 0d5cff1692..54da2f3a9a 100644 --- a/bittensor/core/extrinsics/asyncex/registration.py +++ b/bittensor/core/extrinsics/asyncex/registration.py @@ -522,7 +522,7 @@ async def set_subnet_identity_extrinsic( }, ) - success, error_message = await subtensor.sign_and_send_extrinsic( + success, message = await subtensor.sign_and_send_extrinsic( call=call, wallet=wallet, wait_for_inclusion=wait_for_inclusion, @@ -531,7 +531,7 @@ async def set_subnet_identity_extrinsic( ) if not wait_for_finalization and not wait_for_inclusion: - return True, f"Identities for subnet {netuid} are sent to the chain." + return True, message if success: logging.success( @@ -540,6 +540,6 @@ async def set_subnet_identity_extrinsic( return True, f"Identities for subnet {netuid} are set." logging.error( - f":cross_mark: Failed to set identity for subnet [blue]{netuid}[/blue]: {error_message}" + f":cross_mark: Failed to set identity for subnet [blue]{netuid}[/blue]: {message}" ) - return False, f"Failed to set identity for subnet {netuid}: {error_message}" + return False, f"Failed to set identity for subnet {netuid}: {message}" diff --git a/bittensor/core/extrinsics/commit_reveal.py b/bittensor/core/extrinsics/commit_reveal.py index ed7e57fc4f..532e1dba83 100644 --- a/bittensor/core/extrinsics/commit_reveal.py +++ b/bittensor/core/extrinsics/commit_reveal.py @@ -25,7 +25,7 @@ def _do_commit_reveal_v3( wait_for_inclusion: bool = False, wait_for_finalization: bool = False, period: Optional[int] = None, -) -> tuple[bool, Optional[str]]: +) -> tuple[bool, str]: """ Executes commit-reveal phase 3 for a given netuid and commit, and optionally waits for extrinsic inclusion or finalization. @@ -43,8 +43,8 @@ def _do_commit_reveal_v3( You can think of it as an expiration date for the transaction. Returns: - A tuple where the first element is a boolean indicating success or failure, and the second element is an - optional string containing an error message if any. + A tuple where the first element is a boolean indicating success or failure, and the second element is a string + containing an error message if any. """ logging.info( f"Committing weights hash [blue]{commit.hex()}[/blue] for subnet #[blue]{netuid}[/blue] with " From 3ee11a6e94f2391666518edb4577cede4c13d997 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 21:08:09 -0700 Subject: [PATCH 113/251] improve `sign_and_send_extrinsic` calls --- bittensor/core/async_subtensor.py | 4 +++- bittensor/core/subtensor.py | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 451706fd1f..e0749df2a5 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -3279,7 +3279,9 @@ async def sign_and_send_extrinsic( ) # We only wait here if we expect finalization. if not wait_for_finalization and not wait_for_inclusion: - return True, "" + message = "Not waiting for finalization or inclusion." + logging.debug(message) + return True, message if await response.is_success: return True, "" diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index ac8e1b0710..8236571bd7 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -2708,7 +2708,9 @@ def sign_and_send_extrinsic( ) # We only wait here if we expect finalization. if not wait_for_finalization and not wait_for_inclusion: - return True, "" + message = f"Not waiting for finalization or inclusion. Extrinsic: {extrinsic}" + logging.debug(message) + return True, message if response.is_success: return True, "" @@ -3276,7 +3278,7 @@ def set_weights( wait_for_finalization: bool = False, max_retries: int = 5, block_time: float = 12.0, - period: int = 16, + period: Optional[int] = 16, ) -> tuple[bool, str]: """ Sets the inter-neuronal weights for the specified neuron. This process involves specifying the influence or From f34befd4f10adff477e397f3d58cae2153f0c1d8 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 21:08:15 -0700 Subject: [PATCH 114/251] fix tests --- .../extrinsics/asyncex/test_commit_reveal.py | 2 +- .../extrinsics/asyncex/test_registration.py | 2 +- .../extrinsics/asyncex/test_weights.py | 22 +++-- .../extrinsics/test_commit_reveal.py | 18 ++-- .../unit_tests/extrinsics/test_set_weights.py | 88 ++++--------------- tests/unit_tests/test_async_subtensor.py | 2 +- 6 files changed, 42 insertions(+), 92 deletions(-) diff --git a/tests/unit_tests/extrinsics/asyncex/test_commit_reveal.py b/tests/unit_tests/extrinsics/asyncex/test_commit_reveal.py index 2b0b1f596d..a81f540f2b 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_commit_reveal.py +++ b/tests/unit_tests/extrinsics/asyncex/test_commit_reveal.py @@ -82,7 +82,7 @@ async def test_do_commit_reveal_v3_success(mocker, subtensor, fake_wallet): wait_for_inclusion=False, wait_for_finalization=False, ) - assert result == (True, "") + assert result == (True, "Not waiting for finalization or inclusion.") @pytest.mark.asyncio diff --git a/tests/unit_tests/extrinsics/asyncex/test_registration.py b/tests/unit_tests/extrinsics/asyncex/test_registration.py index 66d1c667f0..322c132045 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_registration.py +++ b/tests/unit_tests/extrinsics/asyncex/test_registration.py @@ -185,7 +185,7 @@ async def test_do_pow_register_no_waiting(subtensor, fake_wallet, mocker): fake_extrinsic, wait_for_inclusion=False, wait_for_finalization=False ) assert result is True - assert error_message == "" + assert error_message == "Not waiting for finalization or inclusion." @pytest.mark.asyncio diff --git a/tests/unit_tests/extrinsics/asyncex/test_weights.py b/tests/unit_tests/extrinsics/asyncex/test_weights.py index e02f006e9b..ef6900f62f 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_weights.py +++ b/tests/unit_tests/extrinsics/asyncex/test_weights.py @@ -43,7 +43,7 @@ async def fake_is_success(): # Asserts assert result is True - assert message == "" + assert message == "Successfully set weights." @pytest.mark.asyncio @@ -131,7 +131,7 @@ async def test_do_set_weights_no_waiting(subtensor, fake_wallet, mocker): # Asserts assert result is True - assert message == "" + assert message == "Not waiting for finalization or inclusion." @pytest.mark.asyncio @@ -141,12 +141,14 @@ async def test_set_weights_extrinsic_success_with_finalization( """Tests set_weights_extrinsic when weights are successfully set with finalization.""" # Preps fake_netuid = 1 - fake_uids = [1, 2, 3] - fake_weights = [0.1, 0.2, 0.7] + fake_uids = mocker.Mock() + fake_weights = mocker.Mock() mocked_do_set_weights = mocker.patch.object( async_weights, "_do_set_weights", return_value=(True, "") ) + mocker_converter = mocker.patch.object(async_weights, "convert_and_normalize_weights_and_uids") + mocker_converter.return_value = (mocker.Mock(), mocker.Mock()) # Call result, message = await async_weights.set_weights_extrinsic( @@ -160,16 +162,18 @@ async def test_set_weights_extrinsic_success_with_finalization( ) # Asserts + mocker_converter.assert_called_once_with(fake_uids, fake_weights) + mocked_do_set_weights.assert_called_once_with( subtensor=subtensor, wallet=fake_wallet, netuid=fake_netuid, - uids=mocker.ANY, - vals=mocker.ANY, + uids=mocker_converter.return_value[0], + vals=mocker_converter.return_value[1], version_key=0, wait_for_finalization=True, wait_for_inclusion=True, - period=5, + period=8, ) assert result is True assert message == "Successfully set weights and Finalized." @@ -184,7 +188,7 @@ async def test_set_weights_extrinsic_no_waiting(subtensor, fake_wallet, mocker): fake_weights = [0.1, 0.2, 0.7] mocked_do_set_weights = mocker.patch.object( - async_weights, "_do_set_weights", return_value=(True, "") + async_weights, "_do_set_weights", return_value=(True, "Not waiting for finalization or inclusion.") ) # Call @@ -380,7 +384,7 @@ async def test_do_commit_weights_no_waiting(subtensor, fake_wallet, mocker): # Asserts assert result is True - assert message == "" + assert message == "Not waiting for finalization or inclusion." @pytest.mark.asyncio diff --git a/tests/unit_tests/extrinsics/test_commit_reveal.py b/tests/unit_tests/extrinsics/test_commit_reveal.py index e254e88a0f..3891b97dae 100644 --- a/tests/unit_tests/extrinsics/test_commit_reveal.py +++ b/tests/unit_tests/extrinsics/test_commit_reveal.py @@ -48,11 +48,8 @@ def test_do_commit_reveal_v3_success(mocker, subtensor, fake_wallet): fake_reveal_round = 1 mocked_compose_call = mocker.patch.object(subtensor.substrate, "compose_call") - mocked_create_signed_extrinsic = mocker.patch.object( - subtensor.substrate, "create_signed_extrinsic" - ) - mocked_submit_extrinsic = mocker.patch.object( - subtensor.substrate, "submit_extrinsic" + mocked_sign_and_send_extrinsic = mocker.patch.object( + subtensor, "sign_and_send_extrinsic", return_value=(True, "") ) # Call @@ -74,14 +71,15 @@ def test_do_commit_reveal_v3_success(mocker, subtensor, fake_wallet): "reveal_round": fake_reveal_round, }, ) - mocked_create_signed_extrinsic.assert_called_once_with( - call=mocked_compose_call.return_value, keypair=fake_wallet.hotkey - ) - mocked_submit_extrinsic.assert_called_once_with( - mocked_create_signed_extrinsic.return_value, + mocked_sign_and_send_extrinsic.assert_called_once_with( + call=mocked_compose_call.return_value, + wallet=fake_wallet, wait_for_inclusion=False, wait_for_finalization=False, + sign_with="hotkey", + period=None, ) + assert result == (True, "") diff --git a/tests/unit_tests/extrinsics/test_set_weights.py b/tests/unit_tests/extrinsics/test_set_weights.py index d3ebfd71c8..645395ab8a 100644 --- a/tests/unit_tests/extrinsics/test_set_weights.py +++ b/tests/unit_tests/extrinsics/test_set_weights.py @@ -68,16 +68,16 @@ def test_set_weights_extrinsic( expected_success, expected_message, ): - uids_tensor = torch.tensor(uids, dtype=torch.int64) - weights_tensor = torch.tensor(weights, dtype=torch.float32) + # uids_tensor = torch.tensor(uids, dtype=torch.int64) + # weights_tensor = torch.tensor(weights, dtype=torch.float32) with ( patch( "bittensor.utils.weight_utils.convert_weights_and_uids_for_emit", - return_value=(uids_tensor, weights_tensor), + return_value=(uids, weights), ), patch( "bittensor.core.extrinsics.set_weights._do_set_weights", - return_value=(expected_success, "Mock error message"), + return_value=(expected_success, expected_message), ), ): result, message = set_weights_extrinsic( @@ -105,6 +105,7 @@ def test_do_set_weights_is_success(mock_subtensor, fake_wallet, mocker): fake_wait_for_finalization = True mock_subtensor.substrate.submit_extrinsic.return_value.is_success = True + mocker.patch.object(mock_subtensor, "sign_and_send_extrinsic", return_value=(True, "")) # Call result = _do_set_weights( @@ -130,68 +131,17 @@ def test_do_set_weights_is_success(mock_subtensor, fake_wallet, mocker): }, ) - mock_subtensor.substrate.create_signed_extrinsic.assert_called_once() - _, kwargs = mock_subtensor.substrate.create_signed_extrinsic.call_args - assert kwargs["call"] == mock_subtensor.substrate.compose_call.return_value - assert kwargs["keypair"] == fake_wallet.hotkey - assert kwargs["era"] == {"period": 5} - - assert result == (True, "Successfully set weights.") - - -def test_do_set_weights_is_not_success(mock_subtensor, fake_wallet, mocker): - """Unsuccessful _do_set_weights call.""" - # Prep - fake_uids = [1, 2, 3] - fake_vals = [4, 5, 6] - fake_netuid = 1 - fake_wait_for_inclusion = True - fake_wait_for_finalization = True - - mock_subtensor.substrate.submit_extrinsic.return_value.is_success = False - mocked_format_error_message = mocker.MagicMock() - subtensor_module.format_error_message = mocked_format_error_message - - # Call - result = _do_set_weights( - subtensor=mock_subtensor, + mock_subtensor.sign_and_send_extrinsic.assert_called_once_with( + call=mock_subtensor.substrate.compose_call.return_value, wallet=fake_wallet, - uids=fake_uids, - vals=fake_vals, - netuid=fake_netuid, - version_key=version_as_int, - wait_for_inclusion=fake_wait_for_inclusion, - wait_for_finalization=fake_wait_for_finalization, - ) - - # Asserts - mock_subtensor.substrate.compose_call.assert_called_once_with( - call_module="SubtensorModule", - call_function="set_weights", - call_params={ - "dests": fake_uids, - "weights": fake_vals, - "netuid": fake_netuid, - "version_key": version_as_int, - }, - ) - - mock_subtensor.substrate.create_signed_extrinsic.assert_called_once() - _, kwargs = mock_subtensor.substrate.create_signed_extrinsic.call_args - assert kwargs["call"] == mock_subtensor.substrate.compose_call.return_value - assert kwargs["keypair"] == fake_wallet.hotkey - assert kwargs["era"] == {"period": 5} - - mock_subtensor.substrate.submit_extrinsic.assert_called_once_with( - extrinsic=mock_subtensor.substrate.create_signed_extrinsic.return_value, wait_for_inclusion=fake_wait_for_inclusion, wait_for_finalization=fake_wait_for_finalization, + nonce_key="hotkey", + sign_with="hotkey", + use_nonce=True, + period=None ) - - assert result == ( - False, - "Subtensor returned `UnknownError(UnknownType)` error. This means: `Unknown Description`.", - ) + assert result == (True, "") def test_do_set_weights_no_waits(mock_subtensor, fake_wallet, mocker): @@ -203,6 +153,8 @@ def test_do_set_weights_no_waits(mock_subtensor, fake_wallet, mocker): fake_wait_for_inclusion = False fake_wait_for_finalization = False + mocker.patch.object(mock_subtensor, "sign_and_send_extrinsic", return_value=(True, "Not waiting for finalization or inclusion.")) + # Call result = _do_set_weights( subtensor=mock_subtensor, @@ -227,15 +179,11 @@ def test_do_set_weights_no_waits(mock_subtensor, fake_wallet, mocker): }, ) - mock_subtensor.substrate.create_signed_extrinsic.assert_called_once() - _, kwargs = mock_subtensor.substrate.create_signed_extrinsic.call_args - assert kwargs["call"] == mock_subtensor.substrate.compose_call.return_value - assert kwargs["keypair"] == fake_wallet.hotkey - assert kwargs["era"] == {"period": 5} - - mock_subtensor.substrate.submit_extrinsic.assert_called_once_with( - extrinsic=mock_subtensor.substrate.create_signed_extrinsic.return_value, + mock_subtensor.sign_and_send_extrinsic( + call=mock_subtensor.substrate.compose_call.return_value, + wallet=fake_wallet, wait_for_inclusion=fake_wait_for_inclusion, wait_for_finalization=fake_wait_for_finalization, + period=None ) assert result == (True, "Not waiting for finalization or inclusion.") diff --git a/tests/unit_tests/test_async_subtensor.py b/tests/unit_tests/test_async_subtensor.py index ddf5148b8c..1d84ba9470 100644 --- a/tests/unit_tests/test_async_subtensor.py +++ b/tests/unit_tests/test_async_subtensor.py @@ -1821,7 +1821,7 @@ async def test_sign_and_send_extrinsic_success_without_inclusion_finalization( wait_for_inclusion=False, wait_for_finalization=False, ) - assert result == (True, "") + assert result == (True, "Not waiting for finalization or inclusion.") @pytest.mark.asyncio From 42341a16c6014a822370399d2537c28d29a5602d Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 21:14:36 -0700 Subject: [PATCH 115/251] opps with set_weights (fixed) + tests --- bittensor/core/async_subtensor.py | 15 +++++++++------ bittensor/core/subtensor.py | 2 +- tests/unit_tests/test_async_subtensor.py | 4 ++-- tests/unit_tests/test_subtensor.py | 4 ++-- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index e0749df2a5..eb0fab0c5b 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -3994,7 +3994,7 @@ async def set_weights( wait_for_finalization: bool = False, max_retries: int = 5, block_time: float = 12.0, - period: int = 5, + period: Optional[int] = 8, ): """ Sets the inter-neuronal weights for the specified neuron. This process involves specifying the influence or @@ -4015,7 +4015,9 @@ async def set_weights( ``False``. max_retries (int): The number of maximum attempts to set weights. Default is ``5``. block_time (float): The number of seconds for block duration. Default is 12.0 seconds. - period (int, optional): The period in seconds to wait for extrinsic inclusion or finalization. Defaults to 5. + period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Default is 16. Returns: tuple[bool, str]: ``True`` if the setting of weights is successful, False otherwise. And `msg`, a string @@ -4034,6 +4036,7 @@ async def _blocks_weight_limit() -> bool: retries = 0 success = False + message = "No attempt made. Perhaps it is too soon to set weights!" if ( uid := await self.get_uid_for_hotkey_on_subnet( wallet.hotkey.ss58_address, netuid @@ -4046,7 +4049,7 @@ async def _blocks_weight_limit() -> bool: if (await self.commit_reveal_enabled(netuid=netuid)) is True: # go with `commit reveal v3` extrinsic - message = "No attempt made. Perhaps it is too soon to commit weights!" + while ( retries < max_retries and success is False @@ -4065,12 +4068,13 @@ async def _blocks_weight_limit() -> bool: wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, block_time=block_time, + period=period, ) retries += 1 return success, message else: # go with classic `set weights extrinsic` - message = "No attempt made. Perhaps it is too soon to set weights!" + while ( retries < max_retries and success is False @@ -4079,7 +4083,7 @@ async def _blocks_weight_limit() -> bool: try: logging.info( f"Setting weights for subnet #[blue]{netuid}[/blue]. " - f"Attempt [blue]{retries + 1} of {max_retries}[/blue]." + f"Attempt [blue]{retries + 1}[/blue] of [green]{max_retries}[/green]." ) success, message = await set_weights_extrinsic( subtensor=self, @@ -4094,7 +4098,6 @@ async def _blocks_weight_limit() -> bool: ) except Exception as e: logging.error(f"Error setting weights: {e}") - finally: retries += 1 return success, message diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 8236571bd7..dc57fd3059 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -3278,7 +3278,7 @@ def set_weights( wait_for_finalization: bool = False, max_retries: int = 5, block_time: float = 12.0, - period: Optional[int] = 16, + period: Optional[int] = 8, ) -> tuple[bool, str]: """ Sets the inter-neuronal weights for the specified neuron. This process involves specifying the influence or diff --git a/tests/unit_tests/test_async_subtensor.py b/tests/unit_tests/test_async_subtensor.py index 1d84ba9470..e8f4f3817f 100644 --- a/tests/unit_tests/test_async_subtensor.py +++ b/tests/unit_tests/test_async_subtensor.py @@ -2667,7 +2667,7 @@ async def test_set_delegate_take_decrease( @pytest.mark.asyncio async def test_set_weights_success(subtensor, fake_wallet, mocker): - """Tests set_weights with successful weight setting on the first try.""" + """Tests set_weights with the successful weight setting on the first try.""" # Preps fake_netuid = 1 fake_uids = [1, 2, 3] @@ -2716,7 +2716,7 @@ async def test_set_weights_success(subtensor, fake_wallet, mocker): wait_for_finalization=False, wait_for_inclusion=False, weights=fake_weights, - period=5, + period=8, ) mocked_weights_rate_limit.assert_called_once_with(fake_netuid) assert result is True diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index 1eba075158..f5de2b33ff 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -1203,7 +1203,7 @@ def test_set_weights(subtensor, mocker, fake_wallet): version_key=settings.version_as_int, wait_for_inclusion=fake_wait_for_inclusion, wait_for_finalization=fake_wait_for_finalization, - period=16, + period=8, ) assert result == expected_result @@ -3140,7 +3140,7 @@ def test_set_weights_with_commit_reveal_enabled(subtensor, fake_wallet, mocker): wait_for_inclusion=fake_wait_for_inclusion, wait_for_finalization=fake_wait_for_finalization, block_time=12.0, - period=16, + period=8, ) assert result == mocked_commit_reveal_v3_extrinsic.return_value From 45d4bec772d5c03a30ebd69290490c187373e70f Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 21:33:34 -0700 Subject: [PATCH 116/251] refactor --- tests/e2e_tests/test_staking.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/e2e_tests/test_staking.py b/tests/e2e_tests/test_staking.py index dddd9d1af1..95d056b20b 100644 --- a/tests/e2e_tests/test_staking.py +++ b/tests/e2e_tests/test_staking.py @@ -162,8 +162,8 @@ def test_single_operation(subtensor, alice_wallet, bob_wallet): } success = subtensor.unstake( - alice_wallet, - bob_wallet.hotkey.ss58_address, + wallet=alice_wallet, + hotkey_ss58=bob_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid, amount=stake_bob, wait_for_inclusion=True, @@ -173,8 +173,8 @@ def test_single_operation(subtensor, alice_wallet, bob_wallet): assert success is True stake = subtensor.get_stake( - alice_wallet.coldkey.ss58_address, - bob_wallet.hotkey.ss58_address, + coldkey_ss58=alice_wallet.coldkey.ss58_address, + hotkey_ss58=bob_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid, ) From 7d508c6718e4c6d705cf467e79eca451772f5fac Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 21:53:12 -0700 Subject: [PATCH 117/251] update `bittensor/core/extrinsics/asyncex/staking.py` --- bittensor/core/extrinsics/asyncex/staking.py | 35 ++++++++++---------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/bittensor/core/extrinsics/asyncex/staking.py b/bittensor/core/extrinsics/asyncex/staking.py index ddaa1bd240..0dcd9345a7 100644 --- a/bittensor/core/extrinsics/asyncex/staking.py +++ b/bittensor/core/extrinsics/asyncex/staking.py @@ -1,11 +1,11 @@ import asyncio from typing import Optional, Sequence, TYPE_CHECKING - +from async_substrate_interface.errors import SubstrateRequestException from bittensor.core.errors import StakeError, NotRegisteredError +from bittensor.core.extrinsics.utils import get_old_stakes from bittensor.utils import unlock_key from bittensor.utils.balance import Balance from bittensor.utils.btlogging import logging -from bittensor.core.extrinsics.utils import get_old_stakes if TYPE_CHECKING: from bittensor_wallet import Wallet @@ -24,6 +24,7 @@ async def add_stake_extrinsic( safe_staking: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, + period: Optional[int] = None, ) -> bool: """ Adds the specified amount of stake to passed hotkey `uid`. @@ -42,10 +43,16 @@ async def add_stake_extrinsic( safe_staking: If set, uses safe staking logic allow_partial_stake: If set, allows partial stake rate_tolerance: The rate tolerance for safe staking + period: The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: success: Flag is `True` if extrinsic was finalized or included in the block. If we did not wait for finalization/inclusion, the response is `True`. + + Raises: + SubstrateRequestException: Raised if the extrinsic fails to be included in the block within the timeout. """ # Decrypt keys, @@ -147,13 +154,14 @@ async def add_stake_extrinsic( call_params=call_params, ) staking_response, err_msg = await subtensor.sign_and_send_extrinsic( - call, - wallet, - wait_for_inclusion, - wait_for_finalization, + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, nonce_key="coldkeypub", sign_with="coldkey", use_nonce=True, + period=period, ) if staking_response is True: # If we successfully staked. # We only wait here if we expect finalization. @@ -163,8 +171,8 @@ async def add_stake_extrinsic( logging.success(":white_heavy_check_mark: [green]Finalized[/green]") logging.info( - f":satellite: [magenta]Checking Balance on:[/magenta] [blue]{subtensor.network}[/blue] " - "[magenta]...[/magenta]" + f":satellite: [magenta]Checking Balance on:[/magenta] " + f"[blue]{subtensor.network}[/blue] [magenta]...[/magenta]" ) new_block_hash = await subtensor.substrate.get_chain_head() new_balance, new_stake = await asyncio.gather( @@ -195,15 +203,8 @@ async def add_stake_extrinsic( logging.error(f":cross_mark: [red]Failed: {err_msg}.[/red]") return False - except NotRegisteredError: - logging.error( - ":cross_mark: [red]Hotkey: {} is not registered.[/red]".format( - wallet.hotkey_str - ) - ) - return False - except StakeError as e: - logging.error(f":cross_mark: [red]Stake Error: {e}[/red]") + except SubstrateRequestException as e: + logging.error(f":cross_mark: [red]Add Stake Error: {e}[/red]") return False From 3e9db4656ec7841f73093e49acefd17388802d12 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 21:53:37 -0700 Subject: [PATCH 118/251] update `bittensor/core/extrinsics/staking.py` --- bittensor/core/extrinsics/staking.py | 43 +++++++++++++++------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/bittensor/core/extrinsics/staking.py b/bittensor/core/extrinsics/staking.py index 3a011bdf9b..288f4a39e5 100644 --- a/bittensor/core/extrinsics/staking.py +++ b/bittensor/core/extrinsics/staking.py @@ -1,5 +1,7 @@ from typing import Optional, TYPE_CHECKING, Sequence +from async_substrate_interface.errors import SubstrateRequestException + from bittensor.core.errors import StakeError, NotRegisteredError from bittensor.core.extrinsics.utils import get_old_stakes from bittensor.utils import unlock_key @@ -22,6 +24,7 @@ def add_stake_extrinsic( safe_staking: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, + period: Optional[int] = None, ) -> bool: """ Adds the specified amount of stake to passed hotkey `uid`. @@ -39,10 +42,16 @@ def add_stake_extrinsic( safe_staking (bool): If true, enables price safety checks allow_partial_stake (bool): If true, allows partial unstaking if price tolerance exceeded rate_tolerance (float): Maximum allowed price increase percentage (0.005 = 0.5%) + period: The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: success: Flag is `True` if extrinsic was finalized or included in the block. If we did not wait for finalization/inclusion, the response is `True`. + + Raises: + SubstrateRequestException: Raised if the extrinsic fails to be included in the block within the timeout. """ # Decrypt keys, @@ -143,16 +152,17 @@ def add_stake_extrinsic( call_params=call_params, ) - staking_response, err_msg = subtensor.sign_and_send_extrinsic( - call, - wallet, - wait_for_inclusion, - wait_for_finalization, + success, message = subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, use_nonce=True, sign_with="coldkey", nonce_key="coldkeypub", + period=period, ) - if staking_response is True: # If we successfully staked. + if success is True: # If we successfully staked. # We only wait here if we expect finalization. if not wait_for_finalization and not wait_for_inclusion: return True @@ -160,8 +170,8 @@ def add_stake_extrinsic( logging.success(":white_heavy_check_mark: [green]Finalized[/green]") logging.info( - f":satellite: [magenta]Checking Balance on:[/magenta] [blue]{subtensor.network}[/blue] " - "[magenta]...[/magenta]" + f":satellite: [magenta]Checking Balance on:[/magenta] " + f"[blue]{subtensor.network}[/blue] [magenta]...[/magenta]" ) new_block = subtensor.get_current_block() new_balance = subtensor.get_balance( @@ -181,24 +191,16 @@ def add_stake_extrinsic( ) return True else: - if safe_staking and "Custom error: 8" in err_msg: + if safe_staking and "Custom error: 8" in message: logging.error( ":cross_mark: [red]Failed[/red]: Price exceeded tolerance limit. Either increase price tolerance or enable partial staking." ) else: - logging.error(f":cross_mark: [red]Failed: {err_msg}.[/red]") + logging.error(f":cross_mark: [red]Failed: {message}.[/red]") return False - # TODO I don't think these are used. Maybe should just catch SubstrateRequestException? - except NotRegisteredError: - logging.error( - ":cross_mark: [red]Hotkey: {} is not registered.[/red]".format( - wallet.hotkey_str - ) - ) - return False - except StakeError as e: - logging.error(f":cross_mark: [red]Stake Error: {e}[/red]") + except SubstrateRequestException as e: + logging.error(f":cross_mark: [red]Add Stake Error: {e}[/red]") return False @@ -217,6 +219,7 @@ def add_stake_multiple_extrinsic( subtensor: The initialized SubtensorInterface object. wallet: Bittensor wallet object for the coldkey. hotkey_ss58s: List of hotkeys to stake to. + netuids: List of netuids to stake to. amounts: List of amounts to stake. If `None`, stake all to the first hotkey. wait_for_inclusion: If set, waits for the extrinsic to enter a block before returning `True`, or returns `False` if the extrinsic fails to enter the block within the timeout. From 93c051e6c39644bc515aea0ddb0052b7af016180 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 22:05:34 -0700 Subject: [PATCH 119/251] update `bittensor.core.extrinsics.staking.add_stake_multiple_extrinsic` --- bittensor/core/extrinsics/staking.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/bittensor/core/extrinsics/staking.py b/bittensor/core/extrinsics/staking.py index 288f4a39e5..8b99743c81 100644 --- a/bittensor/core/extrinsics/staking.py +++ b/bittensor/core/extrinsics/staking.py @@ -32,7 +32,7 @@ def add_stake_extrinsic( Arguments: subtensor: the Subtensor object to use wallet: Bittensor wallet object. - hotkey_ss58: The `ss58` address of the hotkey account to stake to defaults to the wallet's hotkey. + hotkey_ss58: The `ss58` address of the hotkey account to stake to default to the wallet's hotkey. netuid (Optional[int]): Subnet unique ID. amount: Amount to stake as Bittensor balance, `None` if staking all. wait_for_inclusion: If set, waits for the extrinsic to enter a block before returning `True`, or returns @@ -212,8 +212,9 @@ def add_stake_multiple_extrinsic( amounts: Optional[list[Balance]] = None, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> bool: - """Adds stake to each ``hotkey_ss58`` in the list, using each amount, from a common coldkey. + """Adds a stake to each ``hotkey_ss58`` in the list, using each amount, from a common coldkey. Arguments: subtensor: The initialized SubtensorInterface object. @@ -225,6 +226,9 @@ def add_stake_multiple_extrinsic( if the extrinsic fails to enter the block within the timeout. wait_for_finalization: If set, waits for the extrinsic to be finalized on the chain before returning `True`, or returns `False` if the extrinsic fails to be finalized within the timeout. + period: The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: success: `True` if extrinsic was finalized or included in the block. `True` if any wallet was staked. If we did @@ -330,17 +334,18 @@ def add_stake_multiple_extrinsic( "netuid": netuid, }, ) - staking_response, err_msg = subtensor.sign_and_send_extrinsic( - call, - wallet, - wait_for_inclusion, - wait_for_finalization, + success, message = subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, use_nonce=True, nonce_key="coldkeypub", sign_with="coldkey", + period=period, ) - if staking_response is True: # If we successfully staked. + if success is True: # If we successfully staked. # We only wait here if we expect finalization. if not wait_for_finalization and not wait_for_inclusion: @@ -377,7 +382,7 @@ def add_stake_multiple_extrinsic( break else: - logging.error(f":cross_mark: [red]Failed[/red]: {err_msg}") + logging.error(f":cross_mark: [red]Failed[/red]: {message}") continue except NotRegisteredError: From 76e89078872ce5aed3b97622df9ac09148df9961 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 22:05:47 -0700 Subject: [PATCH 120/251] update `bittensor.core.extrinsics.asyncex.staking.add_stake_multiple_extrinsic` --- bittensor/core/extrinsics/asyncex/staking.py | 21 ++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/bittensor/core/extrinsics/asyncex/staking.py b/bittensor/core/extrinsics/asyncex/staking.py index 0dcd9345a7..f06eb8dced 100644 --- a/bittensor/core/extrinsics/asyncex/staking.py +++ b/bittensor/core/extrinsics/asyncex/staking.py @@ -217,8 +217,9 @@ async def add_stake_multiple_extrinsic( amounts: Optional[list[Balance]] = None, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> bool: - """Adds stake to each ``hotkey_ss58`` in the list, using each amount, from a common coldkey. + """Adds a stake to each ``hotkey_ss58`` in the list, using each amount, from a common coldkey. Arguments: subtensor: The initialized SubtensorInterface object. @@ -231,6 +232,9 @@ async def add_stake_multiple_extrinsic( if the extrinsic fails to enter the block within the timeout. wait_for_finalization: If set, waits for the extrinsic to be finalized on the chain before returning `True`, or returns `False` if the extrinsic fails to be finalized within the timeout. + period: The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: success: `True` if extrinsic was finalized or included in the block. `True` if any wallet was staked. If we did @@ -342,17 +346,18 @@ async def add_stake_multiple_extrinsic( "netuid": netuid, }, ) - staking_response, err_msg = await subtensor.sign_and_send_extrinsic( - call, - wallet, - wait_for_inclusion, - wait_for_finalization, + success, message = await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, nonce_key="coldkeypub", sign_with="coldkey", use_nonce=True, + period=period, ) - if staking_response is True: # If we successfully staked. + if success is True: # If we successfully staked. # We only wait here if we expect finalization. if not wait_for_finalization and not wait_for_inclusion: @@ -392,7 +397,7 @@ async def add_stake_multiple_extrinsic( break else: - logging.error(f":cross_mark: [red]Failed: {err_msg}.[/red]") + logging.error(f":cross_mark: [red]Failed: {message}.[/red]") continue except NotRegisteredError: From 268f95cf519abb32c3639a4894fa5e2689cc5534 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 22:06:08 -0700 Subject: [PATCH 121/251] fix tests --- tests/e2e_tests/test_staking.py | 1 + tests/unit_tests/extrinsics/test_staking.py | 26 +++++++++++---------- tests/unit_tests/test_subtensor.py | 3 +++ 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/tests/e2e_tests/test_staking.py b/tests/e2e_tests/test_staking.py index 95d056b20b..3307956095 100644 --- a/tests/e2e_tests/test_staking.py +++ b/tests/e2e_tests/test_staking.py @@ -55,6 +55,7 @@ def test_single_operation(subtensor, alice_wallet, bob_wallet): amount=Balance.from_tao(1), wait_for_inclusion=True, wait_for_finalization=True, + period=16 ) assert success is True diff --git a/tests/unit_tests/extrinsics/test_staking.py b/tests/unit_tests/extrinsics/test_staking.py index 899054006f..517280d461 100644 --- a/tests/unit_tests/extrinsics/test_staking.py +++ b/tests/unit_tests/extrinsics/test_staking.py @@ -13,7 +13,7 @@ def test_add_stake_extrinsic(mocker): "sign_and_send_extrinsic.return_value": (True, ""), } ) - fake_wallet = mocker.Mock( + fake_wallet_ = mocker.Mock( **{ "coldkeypub.ss58_address": "hotkey_owner", } @@ -27,7 +27,7 @@ def test_add_stake_extrinsic(mocker): # Call result = staking.add_stake_extrinsic( subtensor=fake_subtensor, - wallet=fake_wallet, + wallet=fake_wallet_, hotkey_ss58=hotkey_ss58, netuid=fake_netuid, amount=amount, @@ -44,13 +44,14 @@ def test_add_stake_extrinsic(mocker): call_params={"hotkey": "hotkey", "amount_staked": 9, "netuid": 1}, ) fake_subtensor.sign_and_send_extrinsic.assert_called_once_with( - fake_subtensor.substrate.compose_call.return_value, - fake_wallet, - True, - True, + call=fake_subtensor.substrate.compose_call.return_value, + wallet=fake_wallet_, + wait_for_inclusion=True, + wait_for_finalization=True, nonce_key="coldkeypub", sign_with="coldkey", use_nonce=True, + period=None ) @@ -85,7 +86,7 @@ def test_add_stake_multiple_extrinsic(mocker): mocker.patch.object( staking, "get_old_stakes", return_value=[Balance(1.1), Balance(0.3)] ) - fake_wallet = mocker.Mock( + fake_wallet_ = mocker.Mock( **{ "coldkeypub.ss58_address": "hotkey_owner", } @@ -99,7 +100,7 @@ def test_add_stake_multiple_extrinsic(mocker): # Call result = staking.add_stake_multiple_extrinsic( subtensor=fake_subtensor, - wallet=fake_wallet, + wallet=fake_wallet_, hotkey_ss58s=hotkey_ss58s, netuids=netuids, amounts=amounts, @@ -131,11 +132,12 @@ def test_add_stake_multiple_extrinsic(mocker): }, ) fake_subtensor.sign_and_send_extrinsic.assert_called_with( - fake_subtensor.substrate.compose_call.return_value, - fake_wallet, - True, - True, + call=fake_subtensor.substrate.compose_call.return_value, + wallet=fake_wallet_, + wait_for_inclusion=True, + wait_for_finalization=True, nonce_key="coldkeypub", sign_with="coldkey", use_nonce=True, + period=None ) diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index f5de2b33ff..fbd67014a2 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -2826,6 +2826,7 @@ def test_add_stake_success(mocker, fake_wallet, subtensor): safe_staking=False, allow_partial_stake=False, rate_tolerance=0.005, + period=None, ) assert result == mock_add_stake_extrinsic.return_value @@ -2865,6 +2866,7 @@ def test_add_stake_with_safe_staking(mocker, fake_wallet, subtensor): safe_staking=True, allow_partial_stake=False, rate_tolerance=fake_rate_tolerance, + period=None, ) assert result == mock_add_stake_extrinsic.return_value @@ -2898,6 +2900,7 @@ def test_add_stake_multiple_success(mocker, fake_wallet, subtensor): amounts=fake_amount, wait_for_inclusion=True, wait_for_finalization=False, + period=None, ) assert result == mock_add_stake_multiple_extrinsic.return_value From 6a872d3b64c0e2f2cc8f83147c77ef26429d7d51 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 22:06:27 -0700 Subject: [PATCH 122/251] update subtensor calls --- bittensor/core/async_subtensor.py | 40 +++++++++++++--------- bittensor/core/subtensor.py | 57 +++++++++++++++++++------------ 2 files changed, 59 insertions(+), 38 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index eb0fab0c5b..b9442daa4d 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -661,7 +661,7 @@ async def commit(self, wallet: "Wallet", netuid: int, data: str, period: Optiona wallet (bittensor_wallet.Wallet): The wallet associated with the neuron committing the data. netuid (int): The unique identifier of the subnetwork. data (str): The data to be committed to the network. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -2868,7 +2868,7 @@ async def set_reveal_commitment( blocks_until_reveal (int): The number of blocks from now after which the data will be revealed. Defaults to `360`. The number of blocks in one epoch. block_time (Union[int, float]): The number of seconds between each block. Defaults to `12`. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -3241,7 +3241,7 @@ async def sign_and_send_extrinsic( wait_for_finalization (bool): whether to wait until the extrinsic call is finalized on the chain sign_with: the wallet's keypair to use for the signing. Options are "coldkey", "hotkey", "coldkeypub" use_nonce: unique identifier for the transaction related with hot/coldkey. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. nonce_key: the type on nonce to use. Options are "hotkey" or "coldkey". @@ -3250,6 +3250,9 @@ async def sign_and_send_extrinsic( Returns: (success, error message) + + Raises: + SubstrateRequestException: Substrate request exception. """ possible_keys = ("coldkey", "hotkey", "coldkeypub") if sign_with not in possible_keys: @@ -3310,6 +3313,7 @@ async def add_stake( safe_staking: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, + period: Optional[int] = None, ) -> bool: """ Adds the specified amount of stake to a neuron identified by the hotkey ``SS58`` address. @@ -3330,6 +3334,9 @@ async def add_stake( exceed the threshold. Default is False. rate_tolerance (float): The maximum allowed price change ratio when staking. For example, 0.005 = 0.5% maximum price increase. Only used when safe_staking is True. Default is 0.005. + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's + submitted. If the transaction is not included in a block within that number of blocks, it will expire + and be rejected. You can think of it as an expiration date for the transaction. Returns: bool: ``True`` if the staking is successful, False otherwise. @@ -3350,6 +3357,7 @@ async def add_stake( safe_staking=safe_staking, allow_partial_stake=allow_partial_stake, rate_tolerance=rate_tolerance, + period=period, ) async def add_stake_multiple( @@ -3408,10 +3416,10 @@ async def burned_register( `False`. wait_for_finalization (bool, optional): Waits for the transaction to be finalized on the blockchain. Defaults to `True`. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -3466,7 +3474,7 @@ async def commit_weights( wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. Default is ``False``. max_retries (int): The number of maximum attempts to commit weights. Default is ``5``. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -3541,7 +3549,7 @@ async def move_stake( amount (Balance): Amount of stake to move. wait_for_inclusion (bool): Waits for the transaction to be included in a block. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -3599,7 +3607,7 @@ async def register( num_processes (Optional[int]): The number of processes to use to register. Default to `None`. update_interval (Optional[int]): The number of nonces to solve between updates. Default to `None`. log_verbose (bool): If ``true``, the registration process will log more information. Default to `False`. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -3642,7 +3650,7 @@ async def register_subnet( false if the extrinsic fails to enter the block within the timeout. Default is False. wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning true, or returns false if the extrinsic fails to be finalized within the timeout. Default is True. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -3735,7 +3743,7 @@ async def root_register( wait_for_inclusion (bool): Waits for the transaction to be included in a block. Default is ``False``. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. Default is ``False``. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -3773,7 +3781,7 @@ async def root_set_weights( ``False``. wait_for_finalization (bool, optional): Waits for the transaction to be finalized on the blockchain. Defaults to ``False``. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -3959,7 +3967,7 @@ async def set_subnet_identity( repository, contact, URL, discord, description, and any additional metadata. wait_for_inclusion (bool): Indicates if the function should wait for the transaction to be included in the block. wait_for_finalization (bool): Indicates if the function should wait for the transaction to reach finalization. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -4015,7 +4023,7 @@ async def set_weights( ``False``. max_retries (int): The number of maximum attempts to set weights. Default is ``5``. block_time (float): The number of seconds for block duration. Default is 12.0 seconds. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. Default is 16. @@ -4123,7 +4131,7 @@ async def serve_axon( ``True``. certificate (bittensor.utils.Certificate): Certificate to use for TLS. If ``None``, no TLS will be used. Defaults to ``None``. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -4208,7 +4216,7 @@ async def swap_stake( rate_tolerance (float): The maximum allowed increase in the price ratio between subnets (origin_price/destination_price). For example, 0.005 = 0.5% maximum increase. Only used when safe_staking is True. Default is 0.005. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -4262,7 +4270,7 @@ async def transfer_stake( amount (Balance): Amount to transfer. wait_for_inclusion (bool): If true, waits for inclusion before returning. wait_for_finalization (bool): If true, waits for finalization before returning. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index dc57fd3059..33eef30a03 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -460,9 +460,9 @@ def commit(self, wallet, netuid: int, data: str, period: Optional[int] = None) - wallet (bittensor_wallet.Wallet): The wallet associated with the neuron committing the data. netuid (int): The unique identifier of the subnetwork. data (str): The data to be committed to the network. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If - the transaction is not included in a block within that number of blocks, it will expire and be rejected. - You can think of it as an expiration date for the transaction. + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's + submitted. If the transaction is not included in a block within that number of blocks, it will expire + and be rejected. You can think of it as an expiration date for the transaction. Returns: bool: `True` if the commitment was successful, `False` otherwise. @@ -2384,12 +2384,12 @@ def set_reveal_commitment( wallet (bittensor_wallet.Wallet): The wallet associated with the neuron committing the data. netuid (int): The unique identifier of the subnetwork. data (str): The data to be committed to the network. - blocks_until_reveal (int): The number of blocks from now after which the data will be revealed. Defaults to `360`. - Then number of blocks in one epoch. + blocks_until_reveal (int): The number of blocks from now after which the data will be revealed. Defaults to + `360`. Then number of blocks in one epoch. block_time (Union[int, float]): The number of seconds between each block. Defaults to `12`. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If - the transaction is not included in a block within that number of blocks, it will expire and be rejected. - You can think of it as an expiration date for the transaction. + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's + submitted. If the transaction is not included in a block within that number of blocks, it will expire + and be rejected. You can think of it as an expiration date for the transaction. Returns: bool: `True` if the commitment was successful, `False` otherwise. @@ -2670,7 +2670,7 @@ def sign_and_send_extrinsic( wait_for_finalization (bool): whether to wait until the extrinsic call is finalized on the chain sign_with (str): the wallet's keypair to use for the signing. Options are "coldkey", "hotkey", "coldkeypub" use_nonce (bool): unique identifier for the transaction related with hot/coldkey. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. nonce_key: the type on nonce to use. Options are "hotkey" or "coldkey". @@ -2678,6 +2678,9 @@ def sign_and_send_extrinsic( Returns: (success, error message) + + Raises: + SubstrateRequestException: Substrate request exception. """ possible_keys = ("coldkey", "hotkey", "coldkeypub") if sign_with not in possible_keys: @@ -2739,6 +2742,7 @@ def add_stake( safe_staking: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, + period: Optional[int] = None, ) -> bool: """ Adds the specified amount of stake to a neuron identified by the hotkey ``SS58`` address. @@ -2759,6 +2763,9 @@ def add_stake( exceed the tolerance. Default is False. rate_tolerance (float): The maximum allowed price change ratio when staking. For example, 0.005 = 0.5% maximum price increase. Only used when safe_staking is True. Default is 0.005. + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's + submitted. If the transaction is not included in a block within that number of blocks, it will expire + and be rejected. You can think of it as an expiration date for the transaction. Returns: bool: True if the staking is successful, False otherwise. @@ -2780,6 +2787,7 @@ def add_stake( safe_staking=safe_staking, allow_partial_stake=allow_partial_stake, rate_tolerance=rate_tolerance, + period=period, ) def add_stake_multiple( @@ -2790,6 +2798,7 @@ def add_stake_multiple( amounts: Optional[list[Balance]] = None, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> bool: """ Adds stakes to multiple neurons identified by their hotkey SS58 addresses. @@ -2802,6 +2811,9 @@ def add_stake_multiple( amounts (list[Balance]): Corresponding amounts of TAO to stake for each hotkey. wait_for_inclusion (bool): Waits for the transaction to be included in a block. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: bool: ``True`` if the staking is successful for all specified neurons, False otherwise. @@ -2817,6 +2829,7 @@ def add_stake_multiple( amounts=amounts, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) def burned_register( @@ -2838,7 +2851,7 @@ def burned_register( `False`. wait_for_finalization (bool, optional): Waits for the transaction to be finalized on the blockchain. Defaults to `True`. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -2893,7 +2906,7 @@ def commit_weights( wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. Default is ``False``. max_retries (int): The number of maximum attempts to commit weights. Default is ``5``. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -2968,7 +2981,7 @@ def move_stake( amount (Balance): Amount of stake to move. wait_for_inclusion (bool): Waits for the transaction to be included in a block. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -3026,7 +3039,7 @@ def register( num_processes (Optional[int]): The number of processes to use to register. Default to `None`. update_interval (Optional[int]): The number of nonces to solve between updates. Default to `None`. log_verbose (bool): If ``true``, the registration process will log more information. Default to `False`. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -3069,7 +3082,7 @@ def register_subnet( false if the extrinsic fails to enter the block within the timeout. Default is False. wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning true, or returns false if the extrinsic fails to be finalized within the timeout. Default is True. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -3113,7 +3126,7 @@ def reveal_weights( wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. Default is ``False``. max_retries (int): The number of maximum attempts to reveal weights. Default is ``5``. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -3165,7 +3178,7 @@ def root_register( wait_for_inclusion (bool): Waits for the transaction to be included in a block. Default is ``False``. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. Default is ``False``. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -3203,7 +3216,7 @@ def root_set_weights( ``False``. wait_for_finalization (bool, optional): Waits for the transaction to be finalized on the blockchain. Defaults to ``False``. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -3243,7 +3256,7 @@ def set_subnet_identity( repository, contact, URL, discord, description, and any additional metadata. wait_for_inclusion (bool): Indicates if the function should wait for the transaction to be included in the block. wait_for_finalization (bool): Indicates if the function should wait for the transaction to reach finalization. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -3295,7 +3308,7 @@ def set_weights( wait_for_finalization: Waits for the transaction to be finalized on the blockchain. Default is ``False``. max_retries: The number of maximum attempts to set weights. Default is ``5``. block_time: The number of seconds for block duration. Default is 12.0 seconds. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. Default is 16. @@ -3393,7 +3406,7 @@ def serve_axon( ``True``. certificate (bittensor.utils.Certificate): Certificate to use for TLS. If ``None``, no TLS will be used. Defaults to ``None``. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -3478,7 +3491,7 @@ def swap_stake( rate_tolerance (float): The maximum allowed increase in the price ratio between subnets (origin_price/destination_price). For example, 0.005 = 0.5% maximum increase. Only used when safe_staking is True. Default is 0.005. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -3570,7 +3583,7 @@ def transfer_stake( amount (Union[Balance, float, int]): Amount to transfer. wait_for_inclusion (bool): If true, waits for inclusion before returning. wait_for_finalization (bool): If true, waits for finalization before returning. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. From eff87f2ad1d88c6a9f5659f8a45011b6e9412efb Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 22:06:33 -0700 Subject: [PATCH 123/251] refactoring --- bittensor/core/extrinsics/asyncex/commit_reveal.py | 4 ++-- bittensor/core/extrinsics/asyncex/move_stake.py | 8 ++++---- bittensor/core/extrinsics/asyncex/registration.py | 12 ++++++------ bittensor/core/extrinsics/asyncex/root.py | 6 +++--- bittensor/core/extrinsics/asyncex/serving.py | 8 ++++---- bittensor/core/extrinsics/asyncex/weights.py | 12 ++++++------ bittensor/core/extrinsics/commit_reveal.py | 8 ++++---- bittensor/core/extrinsics/commit_weights.py | 8 ++++---- bittensor/core/extrinsics/move_stake.py | 6 +++--- bittensor/core/extrinsics/registration.py | 12 ++++++------ bittensor/core/extrinsics/root.py | 6 +++--- bittensor/core/extrinsics/serving.py | 8 ++++---- bittensor/core/extrinsics/set_weights.py | 6 +++--- 13 files changed, 52 insertions(+), 52 deletions(-) diff --git a/bittensor/core/extrinsics/asyncex/commit_reveal.py b/bittensor/core/extrinsics/asyncex/commit_reveal.py index 74d2de5c3a..6c4ea6f138 100644 --- a/bittensor/core/extrinsics/asyncex/commit_reveal.py +++ b/bittensor/core/extrinsics/asyncex/commit_reveal.py @@ -37,7 +37,7 @@ async def _do_commit_reveal_v3( reveal_round: int The round number for the reveal phase. wait_for_inclusion: bool, optional Flag indicating whether to wait for the extrinsic to be included in a block. wait_for_finalization: bool, optional Flag indicating whether to wait for the extrinsic to be finalized. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -94,7 +94,7 @@ async def commit_reveal_v3_extrinsic( wait_for_inclusion: Whether to wait for the inclusion of the transaction. Default is False. wait_for_finalization: Whether to wait for the finalization of the transaction. Default is False. block_time (float): The number of seconds for block duration. Default is 12.0 seconds. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. diff --git a/bittensor/core/extrinsics/asyncex/move_stake.py b/bittensor/core/extrinsics/asyncex/move_stake.py index ee21e00aeb..8848e3f4e2 100644 --- a/bittensor/core/extrinsics/asyncex/move_stake.py +++ b/bittensor/core/extrinsics/asyncex/move_stake.py @@ -62,7 +62,7 @@ async def transfer_stake_extrinsic( amount (Balance): The amount of stake to transfer as a `Balance` object. wait_for_inclusion (bool): If True, waits for transaction inclusion in a block. Defaults to `True`. wait_for_finalization (bool): If True, waits for transaction finalization. Defaults to `False`. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -185,8 +185,8 @@ async def swap_stake_extrinsic( wait_for_finalization (bool): If True, waits for transaction finalization. Defaults to False. safe_staking (bool): If true, enables price safety checks to protect against price impact. allow_partial_stake (bool): If true, allows partial stake swaps when the full amount would exceed the price tolerance. - rate_tolerance (float): Maximum allowed increase in price ratio (0.005 = 0.5%). - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + rate_tolerance (float): Maximum allowed increase in a price ratio (0.005 = 0.5%). + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -335,7 +335,7 @@ async def move_stake_extrinsic( amount (Balance): The amount of stake to move as a `Balance` object. wait_for_inclusion (bool): If True, waits for transaction inclusion in a block. Defaults to True. wait_for_finalization (bool): If True, waits for transaction finalization. Defaults to False. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. diff --git a/bittensor/core/extrinsics/asyncex/registration.py b/bittensor/core/extrinsics/asyncex/registration.py index 54da2f3a9a..e3663f8f64 100644 --- a/bittensor/core/extrinsics/asyncex/registration.py +++ b/bittensor/core/extrinsics/asyncex/registration.py @@ -39,7 +39,7 @@ async def _do_burned_register( wallet (bittensor_wallet.Wallet): The wallet to be registered. wait_for_inclusion (bool): Whether to wait for the transaction to be included in a block. Default is False. wait_for_finalization (bool): Whether to wait for the transaction to be finalized. Default is True. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -84,7 +84,7 @@ async def burned_register_extrinsic( returns ``False`` if the extrinsic fails to enter the block within the timeout. wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning ``True``, or returns ``False`` if the extrinsic fails to be finalized within the timeout. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -183,7 +183,7 @@ async def _do_pow_register( pow_result (POWSolution): The PoW result to register. wait_for_inclusion (bool): If ``True``, waits for the extrinsic to be included in a block. Default to `False`. wait_for_finalization (bool): If ``True``, waits for the extrinsic to be finalized. Default to `True`. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -249,7 +249,7 @@ async def register_extrinsic( num_processes: The number of processes to use to register. update_interval: The number of nonces to solve between updates. log_verbose: If `True`, the registration process will log more information. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -416,7 +416,7 @@ async def register_subnet_extrinsic( wallet (Wallet): The wallet to be used for subnet registration. wait_for_inclusion (bool): If set, waits for the extrinsic to enter a block before returning true. wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning true. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -493,7 +493,7 @@ async def set_subnet_identity_extrinsic( additional (str): Any additional metadata or information related to the subnet. wait_for_inclusion (bool): Whether to wait for the extrinsic inclusion in a block (default: False). wait_for_finalization (bool): Whether to wait for the extrinsic finalization in a block (default: True). - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. diff --git a/bittensor/core/extrinsics/asyncex/root.py b/bittensor/core/extrinsics/asyncex/root.py index 852f004daf..74eb15689d 100644 --- a/bittensor/core/extrinsics/asyncex/root.py +++ b/bittensor/core/extrinsics/asyncex/root.py @@ -59,7 +59,7 @@ async def root_register_extrinsic( `False` if the extrinsic fails to enter the block within the timeout. wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning `True`, or returns `False` if the extrinsic fails to be finalized within the timeout. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -177,7 +177,7 @@ async def _do_set_root_weights( False. wait_for_finalization (bool, optional): If True, waits for the extrinsic to be finalized on the chain. Defaults to False. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -239,7 +239,7 @@ async def set_root_weights_extrinsic( `False` if the extrinsic fails to enter the block within the timeout. wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning ` True`, or returns `False` if the extrinsic fails to be finalized within the timeout. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. diff --git a/bittensor/core/extrinsics/asyncex/serving.py b/bittensor/core/extrinsics/asyncex/serving.py index aafde7dd02..007be2a12b 100644 --- a/bittensor/core/extrinsics/asyncex/serving.py +++ b/bittensor/core/extrinsics/asyncex/serving.py @@ -35,7 +35,7 @@ async def do_serve_axon( call_params (bittensor.core.types.AxonServeCallParams): Parameters required for the serve axon call. wait_for_inclusion (bool): Waits for the transaction to be included in a block. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -97,7 +97,7 @@ async def serve_extrinsic( ``True``, or returns ``False`` if the extrinsic fails to be finalized within the timeout. certificate (bittensor.utils.Certificate): Certificate to use for TLS. If ``None``, no TLS will be used. Defaults to ``None``. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -181,7 +181,7 @@ async def serve_axon_extrinsic( ``True``, or returns ``False`` if the extrinsic fails to be finalized within the timeout. certificate (bittensor.utils.Certificate): Certificate to use for TLS. If ``None``, no TLS will be used. Defaults to ``None``. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -252,7 +252,7 @@ async def publish_metadata( block before returning. Defaults to ``False``. wait_for_finalization (bool, optional): If ``True``, the function will wait for the extrinsic to be finalized on the chain before returning. Defaults to ``True``. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. diff --git a/bittensor/core/extrinsics/asyncex/weights.py b/bittensor/core/extrinsics/asyncex/weights.py index 37693927a1..7f04ffd7bd 100644 --- a/bittensor/core/extrinsics/asyncex/weights.py +++ b/bittensor/core/extrinsics/asyncex/weights.py @@ -35,7 +35,7 @@ async def _do_commit_weights( commit_hash (str): The hash of the neuron's weights to be committed. wait_for_inclusion (bool): Waits for the transaction to be included in a block. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -88,7 +88,7 @@ async def commit_weights_extrinsic( commit_hash (str): The hash of the neuron's weights to be committed. wait_for_inclusion (bool): Waits for the transaction to be included in a block. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -146,7 +146,7 @@ async def _do_reveal_weights( version_key (int): Version key for compatibility with the network. wait_for_inclusion (bool): Waits for the transaction to be included in a block. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -207,7 +207,7 @@ async def reveal_weights_extrinsic( version_key (int): Version key for compatibility with the network. wait_for_inclusion (bool): Waits for the transaction to be included in a block. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -265,7 +265,7 @@ async def _do_set_weights( version_key (int, optional): Version key for compatibility with the network. wait_for_inclusion (bool, optional): Waits for the transaction to be included in a block. wait_for_finalization (bool, optional): Waits for the transaction to be finalized on the blockchain. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -332,7 +332,7 @@ async def set_weights_extrinsic( returns ``False`` if the extrinsic fails to enter the block within the timeout. wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning ``True``, or returns ``False`` if the extrinsic fails to be finalized within the timeout. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. diff --git a/bittensor/core/extrinsics/commit_reveal.py b/bittensor/core/extrinsics/commit_reveal.py index 532e1dba83..28f6c112fe 100644 --- a/bittensor/core/extrinsics/commit_reveal.py +++ b/bittensor/core/extrinsics/commit_reveal.py @@ -38,7 +38,7 @@ def _do_commit_reveal_v3( reveal_round: int The round number for the reveal phase. wait_for_inclusion: bool, optional Flag indicating whether to wait for the extrinsic to be included in a block. wait_for_finalization: bool, optional Flag indicating whether to wait for the extrinsic to be finalized. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -95,9 +95,9 @@ def commit_reveal_v3_extrinsic( wait_for_inclusion: Whether to wait for the inclusion of the transaction. Default is False. wait_for_finalization: Whether to wait for the finalization of the transaction. Default is False. block_time (float): The number of seconds for block duration. Default is 12.0 seconds. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If the - transaction is not included in a block within that number of blocks, it will expire and be rejected. You can - think of it as an expiration date for the transaction. + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. + If the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: tuple[bool, str]: A tuple where the first element is a boolean indicating success or failure, and the second diff --git a/bittensor/core/extrinsics/commit_weights.py b/bittensor/core/extrinsics/commit_weights.py index c07878ac16..e139907b65 100644 --- a/bittensor/core/extrinsics/commit_weights.py +++ b/bittensor/core/extrinsics/commit_weights.py @@ -29,7 +29,7 @@ def _do_commit_weights( commit_hash (str): The hash of the neuron's weights to be committed. wait_for_inclusion (bool): Waits for the transaction to be included in a block. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -81,7 +81,7 @@ def commit_weights_extrinsic( commit_hash (str): The hash of the neuron's weights to be committed. wait_for_inclusion (bool): Waits for the transaction to be included in a block. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -139,7 +139,7 @@ def _do_reveal_weights( version_key (int): Version key for compatibility with the network. wait_for_inclusion (bool): Waits for the transaction to be included in a block. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -201,7 +201,7 @@ def reveal_weights_extrinsic( version_key (int): Version key for compatibility with the network. wait_for_inclusion (bool): Waits for the transaction to be included in a block. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. diff --git a/bittensor/core/extrinsics/move_stake.py b/bittensor/core/extrinsics/move_stake.py index a815c14dc0..58e651b3e9 100644 --- a/bittensor/core/extrinsics/move_stake.py +++ b/bittensor/core/extrinsics/move_stake.py @@ -59,7 +59,7 @@ def transfer_stake_extrinsic( amount (Union[Balance, float, int]): Amount to transfer. wait_for_inclusion (bool): If true, waits for inclusion before returning. wait_for_finalization (bool): If true, waits for finalization before returning. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -183,7 +183,7 @@ def swap_stake_extrinsic( safe_staking (bool): If true, enables price safety checks to protect against price impact. allow_partial_stake (bool): If true, allows partial stake swaps when the full amount would exceed the price tolerance. rate_tolerance (float): Maximum allowed increase in a price ratio (0.005 = 0.5%). - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -331,7 +331,7 @@ def move_stake_extrinsic( amount (Union[Balance, float]): Amount to move. wait_for_inclusion (bool): If true, waits for inclusion before returning. wait_for_finalization (bool): If true, waits for finalization before returning. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. diff --git a/bittensor/core/extrinsics/registration.py b/bittensor/core/extrinsics/registration.py index ed53319974..9acd2f0f8d 100644 --- a/bittensor/core/extrinsics/registration.py +++ b/bittensor/core/extrinsics/registration.py @@ -38,7 +38,7 @@ def _do_burned_register( wallet (bittensor_wallet.Wallet): The wallet to be registered. wait_for_inclusion (bool): Whether to wait for the transaction to be included in a block. Default is False. wait_for_finalization (bool): Whether to wait for the transaction to be finalized. Default is True. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -83,7 +83,7 @@ def burned_register_extrinsic( returns ``False`` if the extrinsic fails to enter the block within the timeout. wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning ``True``, or returns ``False`` if the extrinsic fails to be finalized within the timeout. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -175,7 +175,7 @@ def _do_pow_register( pow_result (POWSolution): The PoW result to register. wait_for_inclusion (bool): If ``True``, waits for the extrinsic to be included in a block. Default to `False`. wait_for_finalization (bool): If ``True``, waits for the extrinsic to be finalized. Default to `True`. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -221,7 +221,7 @@ def register_subnet_extrinsic( wallet (Wallet): The wallet to be used for subnet registration. wait_for_inclusion (bool): If set, waits for the extrinsic to enter a block before returning true. wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning true. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -301,7 +301,7 @@ def register_extrinsic( num_processes: The number of processes to use to register. update_interval: The number of nonces to solve between updates. log_verbose: If `True`, the registration process will log more information. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -485,7 +485,7 @@ def set_subnet_identity_extrinsic( additional (str): Any additional metadata or information related to the subnet. wait_for_inclusion (bool): Whether to wait for the extrinsic inclusion in a block (default: False). wait_for_finalization (bool): Whether to wait for the extrinsic finalization in a block (default: True). - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. diff --git a/bittensor/core/extrinsics/root.py b/bittensor/core/extrinsics/root.py index cc944a7c62..3545909493 100644 --- a/bittensor/core/extrinsics/root.py +++ b/bittensor/core/extrinsics/root.py @@ -62,7 +62,7 @@ def root_register_extrinsic( `False` if the extrinsic fails to enter the block within the timeout. wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning `True`, or returns `False` if the extrinsic fails to be finalized within the timeout. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -178,7 +178,7 @@ def _do_set_root_weights( False. wait_for_finalization (bool, optional): If True, waits for the extrinsic to be finalized on the chain. Defaults to False. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -240,7 +240,7 @@ def set_root_weights_extrinsic( `False` if the extrinsic fails to enter the block within the timeout. wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning True`, or returns `False` if the extrinsic fails to be finalized within the timeout. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. diff --git a/bittensor/core/extrinsics/serving.py b/bittensor/core/extrinsics/serving.py index a9309cfe24..d004fd7dba 100644 --- a/bittensor/core/extrinsics/serving.py +++ b/bittensor/core/extrinsics/serving.py @@ -34,7 +34,7 @@ def do_serve_axon( call_params (bittensor.core.types.AxonServeCallParams): Parameters required for the serve axon call. wait_for_inclusion (bool): Waits for the transaction to be included in a block. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -96,7 +96,7 @@ def serve_extrinsic( ``True``, or returns ``False`` if the extrinsic fails to be finalized within the timeout. certificate (bittensor.utils.Certificate): Certificate to use for TLS. If ``None``, no TLS will be used. Defaults to ``None``. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -180,7 +180,7 @@ def serve_axon_extrinsic( ``True``, or returns ``False`` if the extrinsic fails to be finalized within the timeout. certificate (bittensor.utils.Certificate): Certificate to use for TLS. If ``None``, no TLS will be used. Defaults to ``None``. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -249,7 +249,7 @@ def publish_metadata( block before returning. Defaults to ``False``. wait_for_finalization (bool, optional): If ``True``, the function will wait for the extrinsic to be finalized on the chain before returning. Defaults to ``True``. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. diff --git a/bittensor/core/extrinsics/set_weights.py b/bittensor/core/extrinsics/set_weights.py index eea4ddbb0e..bfa912419c 100644 --- a/bittensor/core/extrinsics/set_weights.py +++ b/bittensor/core/extrinsics/set_weights.py @@ -40,7 +40,7 @@ def _do_set_weights( version_key (int, optional): Version key for compatibility with the network. wait_for_inclusion (bool, optional): Waits for the transaction to be included in a block. wait_for_finalization (bool, optional): Waits for the transaction to be finalized on the blockchain. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. @@ -99,8 +99,8 @@ def set_weights_extrinsic( returns ``False`` if the extrinsic fails to enter the block within the timeout. wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning ``True``, or returns ``False`` if the extrinsic fails to be finalized within the timeout. - period (int): The number of blocks during which the transaction will remain valid after it's submitted. If - the transaction is not included in a block within that number of blocks, it will expire and be rejected. + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. + If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. Returns: From 838c9bc7e54bc4b61fa49473f58ed45fa3c34267 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 22:27:19 -0700 Subject: [PATCH 124/251] update start_call extrinsics --- .../core/extrinsics/asyncex/start_call.py | 22 +++++----- bittensor/core/extrinsics/start_call.py | 44 +++++++++---------- 2 files changed, 34 insertions(+), 32 deletions(-) diff --git a/bittensor/core/extrinsics/asyncex/start_call.py b/bittensor/core/extrinsics/asyncex/start_call.py index bc0089ddb9..3790995524 100644 --- a/bittensor/core/extrinsics/asyncex/start_call.py +++ b/bittensor/core/extrinsics/asyncex/start_call.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Optional from bittensor.utils import unlock_key, format_error_message from bittensor.utils.btlogging import logging @@ -14,6 +14,7 @@ async def start_call_extrinsic( netuid: int, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> tuple[bool, str]: """ Submits a start_call extrinsic to the blockchain, to trigger the start call process for a subnet (used to start a @@ -25,6 +26,9 @@ async def start_call_extrinsic( netuid (int): The UID of the target subnet for which the call is being initiated. wait_for_inclusion (bool, optional): Whether to wait for the extrinsic to be included in a block. Defaults to True. wait_for_finalization (bool, optional): Whether to wait for finalization of the extrinsic. Defaults to False. + period: The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: Tuple[bool, str]: @@ -41,21 +45,19 @@ async def start_call_extrinsic( call_function="start_call", call_params={"netuid": netuid}, ) - signed_ext = await substrate.create_signed_extrinsic( - call=start_call, - keypair=wallet.coldkey, - ) - response = await substrate.submit_extrinsic( - extrinsic=signed_ext, + success, message = await subtensor.sign_and_send_extrinsic( + call=start_call, + wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period ) if not wait_for_finalization and not wait_for_inclusion: - return True, "Not waiting for finalization or inclusion." + return True, message - if await response.is_success: + if success: return True, "Success with `start_call` response." - return False, format_error_message(await response.error_message) + return True, message diff --git a/bittensor/core/extrinsics/start_call.py b/bittensor/core/extrinsics/start_call.py index d3a1d423ce..ac6229e802 100644 --- a/bittensor/core/extrinsics/start_call.py +++ b/bittensor/core/extrinsics/start_call.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Optional from bittensor.utils import unlock_key, format_error_message from bittensor.utils.btlogging import logging @@ -14,6 +14,7 @@ def start_call_extrinsic( netuid: int, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> tuple[bool, str]: """ Submits a start_call extrinsic to the blockchain, to trigger the start call process for a subnet (used to start a @@ -25,6 +26,9 @@ def start_call_extrinsic( netuid (int): The UID of the target subnet for which the call is being initiated. wait_for_inclusion (bool, optional): Whether to wait for the extrinsic to be included in a block. Defaults to True. wait_for_finalization (bool, optional): Whether to wait for finalization of the extrinsic. Defaults to False. + period: The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: Tuple[bool, str]: @@ -35,28 +39,24 @@ def start_call_extrinsic( logging.error(unlock.message) return False, unlock.message - with subtensor.substrate as substrate: - start_call = substrate.compose_call( - call_module="SubtensorModule", - call_function="start_call", - call_params={"netuid": netuid}, - ) + start_call = subtensor.substrate.compose_call( + call_module="SubtensorModule", + call_function="start_call", + call_params={"netuid": netuid}, + ) - signed_ext = substrate.create_signed_extrinsic( - call=start_call, - keypair=wallet.coldkey, - ) + success, message = subtensor.sign_and_send_extrinsic( + call=start_call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period + ) - response = substrate.submit_extrinsic( - extrinsic=signed_ext, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - ) + if not wait_for_finalization and not wait_for_inclusion: + return True, message - if not wait_for_finalization and not wait_for_inclusion: - return True, "Not waiting for finalization or inclusion." + if success: + return True, "Success with `start_call` response." - if response.is_success: - return True, "Success with `start_call` response." - - return False, format_error_message(response.error_message) + return True, message From 179b3dd6c5a4fdc60d7a352cfc2cfc3cee222185 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 22:27:33 -0700 Subject: [PATCH 125/251] update subtensors start_call extrinsics --- bittensor/core/async_subtensor.py | 5 +++++ bittensor/core/subtensor.py | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index b9442daa4d..28e6a9a522 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -4157,6 +4157,7 @@ async def start_call( netuid: int, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> tuple[bool, str]: """ Submits a start_call extrinsic to the blockchain, to trigger the start call process for a subnet (used to start a @@ -4167,6 +4168,9 @@ async def start_call( netuid (int): The UID of the target subnet for which the call is being initiated. wait_for_inclusion (bool, optional): Whether to wait for the extrinsic to be included in a block. Defaults to True. wait_for_finalization (bool, optional): Whether to wait for finalization of the extrinsic. Defaults to False. + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: Tuple[bool, str]: @@ -4179,6 +4183,7 @@ async def start_call( netuid=netuid, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) async def swap_stake( diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 33eef30a03..56f930bb6d 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -3432,6 +3432,7 @@ def start_call( netuid: int, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> tuple[bool, str]: """ Submits a start_call extrinsic to the blockchain, to trigger the start call process for a subnet (used to start a @@ -3442,6 +3443,9 @@ def start_call( netuid (int): The UID of the target subnet for which the call is being initiated. wait_for_inclusion (bool, optional): Whether to wait for the extrinsic to be included in a block. Defaults to True. wait_for_finalization (bool, optional): Whether to wait for finalization of the extrinsic. Defaults to False. + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: Tuple[bool, str]: @@ -3454,6 +3458,7 @@ def start_call( netuid=netuid, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) def swap_stake( From 76384c9da53734d31870d73e1e77e41f0838abb8 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 22:27:39 -0700 Subject: [PATCH 126/251] fix tests --- .../extrinsics/asyncex/test_start_call.py | 20 ++++++----------- .../unit_tests/extrinsics/test_start_call.py | 22 +++++++------------ tests/unit_tests/test_async_subtensor.py | 1 + tests/unit_tests/test_subtensor.py | 1 + 4 files changed, 17 insertions(+), 27 deletions(-) diff --git a/tests/unit_tests/extrinsics/asyncex/test_start_call.py b/tests/unit_tests/extrinsics/asyncex/test_start_call.py index 63083cd094..ebb5c327a9 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_start_call.py +++ b/tests/unit_tests/extrinsics/asyncex/test_start_call.py @@ -5,18 +5,15 @@ @pytest.mark.asyncio async def test_start_call_extrinsics(subtensor, mocker, fake_wallet): """Test that start_call_extrinsic correctly constructs and submits the extrinsic.""" - # Preps netuid = 123 wallet = fake_wallet wallet.name = "fake_wallet" wallet.coldkey = "fake_coldkey" - substrate = subtensor.substrate.__aenter__.return_value - substrate.compose_call.return_value = "mock_call" - substrate.create_signed_extrinsic.return_value = "signed_ext" - substrate.submit_extrinsic.return_value = mocker.MagicMock( - is_success=mocker.AsyncMock(return_value=True)(), error_message="" + substrate.compose_call = mocker.AsyncMock() + mocked_sign_and_send_extrinsic = mocker.patch.object( + subtensor, "sign_and_send_extrinsic", return_value=(True, "") ) # Call @@ -33,15 +30,12 @@ async def test_start_call_extrinsics(subtensor, mocker, fake_wallet): call_params={"netuid": netuid}, ) - substrate.create_signed_extrinsic.assert_awaited_once_with( - call="mock_call", - keypair=wallet.coldkey, - ) - - substrate.submit_extrinsic.assert_awaited_once_with( - extrinsic="signed_ext", + mocked_sign_and_send_extrinsic.assert_awaited_once_with( + call=substrate.compose_call.return_value, + wallet=wallet, wait_for_inclusion=True, wait_for_finalization=False, + period=None ) assert success is True diff --git a/tests/unit_tests/extrinsics/test_start_call.py b/tests/unit_tests/extrinsics/test_start_call.py index ccc14581ee..658f0883f7 100644 --- a/tests/unit_tests/extrinsics/test_start_call.py +++ b/tests/unit_tests/extrinsics/test_start_call.py @@ -3,18 +3,15 @@ def test_start_call_extrinsics(subtensor, mocker, fake_wallet): """Test that start_call_extrinsic correctly constructs and submits the extrinsic.""" - # Preps netuid = 123 wallet = fake_wallet wallet.name = "fake_wallet" wallet.coldkey = "fake_coldkey" - substrate = subtensor.substrate.__enter__.return_value - substrate.compose_call.return_value = "mock_call" - substrate.create_signed_extrinsic.return_value = "signed_ext" - substrate.submit_extrinsic.return_value = mocker.MagicMock( - is_success=True, error_message="" + subtensor.substrate.compose_call = mocker.Mock() + mocked_sign_and_send_extrinsic = mocker.patch.object( + subtensor, "sign_and_send_extrinsic", return_value=(True, "") ) # Call @@ -25,21 +22,18 @@ def test_start_call_extrinsics(subtensor, mocker, fake_wallet): ) # Assertions - substrate.compose_call.assert_called_once_with( + subtensor.substrate.compose_call.assert_called_once_with( call_module="SubtensorModule", call_function="start_call", call_params={"netuid": netuid}, ) - substrate.create_signed_extrinsic.assert_called_once_with( - call="mock_call", - keypair=wallet.coldkey, - ) - - substrate.submit_extrinsic.assert_called_once_with( - extrinsic="signed_ext", + mocked_sign_and_send_extrinsic.assert_called_once_with( + call=subtensor.substrate.compose_call.return_value, + wallet=wallet, wait_for_inclusion=True, wait_for_finalization=False, + period=None ) assert success is True diff --git a/tests/unit_tests/test_async_subtensor.py b/tests/unit_tests/test_async_subtensor.py index e8f4f3817f..892836e2d8 100644 --- a/tests/unit_tests/test_async_subtensor.py +++ b/tests/unit_tests/test_async_subtensor.py @@ -3064,6 +3064,7 @@ async def test_start_call(subtensor, mocker): netuid=netuid, wait_for_inclusion=True, wait_for_finalization=False, + period=None, ) assert result == mocked_extrinsic.return_value diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index fbd67014a2..e3588178c5 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -3391,6 +3391,7 @@ def test_start_call(subtensor, mocker): netuid=netuid, wait_for_inclusion=True, wait_for_finalization=False, + period=None, ) assert result == mocked_extrinsic.return_value From 30953e1f3472c60c66fbfd1bb01d74d51bf1434f Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 22:48:58 -0700 Subject: [PATCH 127/251] extrinsics `increase_take_extrinsic` and `decrease_take_extrinsic` --- bittensor/core/extrinsics/asyncex/take.py | 49 ++++++++++++++++++++--- bittensor/core/extrinsics/take.py | 42 ++++++++++++++++++- 2 files changed, 84 insertions(+), 7 deletions(-) diff --git a/bittensor/core/extrinsics/asyncex/take.py b/bittensor/core/extrinsics/asyncex/take.py index 6a51239bc0..543d4e72da 100644 --- a/bittensor/core/extrinsics/asyncex/take.py +++ b/bittensor/core/extrinsics/asyncex/take.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Optional from bittensor_wallet.bittensor_wallet import Wallet @@ -16,7 +16,26 @@ async def increase_take_extrinsic( wait_for_inclusion: bool = True, wait_for_finalization: bool = True, raise_error: bool = False, + period: Optional[int] = None, ) -> tuple[bool, str]: + """Sets the delegate 'take' percentage for a neuron identified by its hotkey. + + Args: + subtensor (Subtensor): Blockchain connection. + wallet (Wallet): The wallet to sign the extrinsic. + hotkey_ss58 (str): SS58 address of the hotkey to set take for. + take (int): The percentage of rewards that the delegate claims from nominators. + wait_for_inclusion (bool, optional): Wait for inclusion before returning. Defaults to True. + wait_for_finalization (bool, optional): Wait for finalization before returning. Defaults to True. + raise_error (bool, optional): Raise error on failure. Defaults to False. + period: The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. + + Returns: + tuple[bool, str]: Success flag and status message. + """ + unlock = unlock_key(wallet, raise_error=raise_error) if not unlock.success: @@ -32,10 +51,11 @@ async def increase_take_extrinsic( ) return await subtensor.sign_and_send_extrinsic( - call, - wallet, + call=call, + wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, raise_error=raise_error, ) @@ -48,7 +68,25 @@ async def decrease_take_extrinsic( wait_for_inclusion: bool = True, wait_for_finalization: bool = True, raise_error: bool = False, + period: Optional[int] = None, ) -> tuple[bool, str]: + """Sets the delegate 'take' percentage for a neuron identified by its hotkey. + + Args: + subtensor (Subtensor): Blockchain connection. + wallet (Wallet): The wallet to sign the extrinsic. + hotkey_ss58 (str): SS58 address of the hotkey to set take for. + take (int): The percentage of rewards that the delegate claims from nominators. + wait_for_inclusion (bool, optional): Wait for inclusion before returning. Defaults to True. + wait_for_finalization (bool, optional): Wait for finalization before returning. Defaults to True. + raise_error (bool, optional): Raise error on failure. Defaults to False. + period: The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. + + Returns: + tuple[bool, str]: Success flag and status message. + """ unlock = unlock_key(wallet, raise_error=raise_error) if not unlock.success: @@ -64,9 +102,10 @@ async def decrease_take_extrinsic( ) return await subtensor.sign_and_send_extrinsic( - call, - wallet, + call=call, + wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, raise_error=raise_error, ) diff --git a/bittensor/core/extrinsics/take.py b/bittensor/core/extrinsics/take.py index 1ac6e96040..4a18ec0748 100644 --- a/bittensor/core/extrinsics/take.py +++ b/bittensor/core/extrinsics/take.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Optional from bittensor_wallet.bittensor_wallet import Wallet @@ -16,7 +16,25 @@ def increase_take_extrinsic( wait_for_inclusion: bool = True, wait_for_finalization: bool = True, raise_error: bool = False, + period: Optional[int] = None, ) -> tuple[bool, str]: + """Sets the delegate 'take' percentage for a neuron identified by its hotkey. + + Args: + subtensor (Subtensor): Blockchain connection. + wallet (Wallet): The wallet to sign the extrinsic. + hotkey_ss58 (str): SS58 address of the hotkey to set take for. + take (int): The percentage of rewards that the delegate claims from nominators. + wait_for_inclusion (bool, optional): Wait for inclusion before returning. Defaults to True. + wait_for_finalization (bool, optional): Wait for finalization before returning. Defaults to True. + raise_error (bool, optional): Raise error on failure. Defaults to False. + period: The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. + + Returns: + tuple[bool, str]: Success flag and status message. + """ unlock = unlock_key(wallet, raise_error=raise_error) if not unlock.success: @@ -36,6 +54,7 @@ def increase_take_extrinsic( wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, raise_error=raise_error, ) @@ -48,7 +67,25 @@ def decrease_take_extrinsic( wait_for_inclusion: bool = True, wait_for_finalization: bool = True, raise_error: bool = False, + period: Optional[int] = None, ) -> tuple[bool, str]: + """Sets the delegate 'take' percentage for a neuron identified by its hotkey. + + Args: + subtensor (Subtensor): Blockchain connection. + wallet (Wallet): The wallet to sign the extrinsic. + hotkey_ss58 (str): SS58 address of the hotkey to set take for. + take (int): The percentage of rewards that the delegate claims from nominators. + wait_for_inclusion (bool, optional): Wait for inclusion before returning. Defaults to True. + wait_for_finalization (bool, optional): Wait for finalization before returning. Defaults to True. + raise_error (bool, optional): Raise error on failure. Defaults to False. + period: The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. + + Returns: + tuple[bool, str]: Success flag and status message. + """ unlock = unlock_key(wallet, raise_error=raise_error) if not unlock.success: @@ -67,6 +104,7 @@ def decrease_take_extrinsic( call, wallet, wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, raise_error=raise_error, + wait_for_finalization=wait_for_finalization, + period=period, ) From 9563c0402b5a2ac5379fddb442d996c9bf2a8146 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 22:49:13 -0700 Subject: [PATCH 128/251] related subtensors calls --- bittensor/core/async_subtensor.py | 12 +- bittensor/core/subtensor.py | 296 +++++++++++++++--------------- 2 files changed, 160 insertions(+), 148 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 28e6a9a522..c10be33b69 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -3880,6 +3880,7 @@ async def set_delegate_take( wait_for_inclusion: bool = True, wait_for_finalization: bool = True, raise_error: bool = False, + period: Optional[int] = None, ) -> tuple[bool, str]: """ Sets the delegate 'take' percentage for a neuron identified by its hotkey. @@ -3891,7 +3892,10 @@ async def set_delegate_take( take (float): Percentage reward for the delegate. wait_for_inclusion (bool): Waits for the transaction to be included in a block. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. - raise_error: Raises relevant exception rather than returning `False` if unsuccessful. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: tuple[bool, str]: A tuple where the first element is a boolean indicating success or failure of the @@ -3901,8 +3905,8 @@ async def set_delegate_take( DelegateTakeTooHigh: Delegate take is too high. DelegateTakeTooLow: Delegate take is too low. DelegateTxRateLimitExceeded: A transactor exceeded the rate limit for delegate transaction. - HotKeyAccountNotExists: The hotkey does not exists. - NonAssociatedColdKey: Request to stake, unstake or subscribe is made by a coldkey that is not associated with the hotkey account. + HotKeyAccountNotExists: The hotkey does not exist. + NonAssociatedColdKey: Request to stake, unstake, or subscribe is made by a coldkey that is not associated with the hotkey account. bittensor_wallet.errors.PasswordError: Decryption failed or wrong password for decryption provided. bittensor_wallet.errors.KeyFileError: Failed to decode keyfile data. @@ -3931,6 +3935,7 @@ async def set_delegate_take( wait_for_finalization=wait_for_finalization, wait_for_inclusion=wait_for_inclusion, raise_error=raise_error, + period=period ) else: success, error = await decrease_take_extrinsic( @@ -3941,6 +3946,7 @@ async def set_delegate_take( wait_for_finalization=wait_for_finalization, wait_for_inclusion=wait_for_inclusion, raise_error=raise_error, + period=period ) if success: diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 56f930bb6d..685b715ed8 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -1931,151 +1931,6 @@ def immunity_period( ) return None if call is None else int(call) - def set_children( - self, - wallet: "Wallet", - hotkey: str, - netuid: int, - children: list[tuple[float, str]], - wait_for_inclusion: bool = True, - wait_for_finalization: bool = True, - raise_error: bool = False, - ) -> tuple[bool, str]: - """ - Allows a coldkey to set children keys. - - Arguments: - wallet (bittensor_wallet.Wallet): bittensor wallet instance. - hotkey (str): The ``SS58`` address of the neuron's hotkey. - netuid (int): The netuid value. - children (list[tuple[float, str]]): A list of children with their proportions. - wait_for_inclusion (bool): Waits for the transaction to be included in a block. - wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. - raise_error: Raises relevant exception rather than returning `False` if unsuccessful. - - Returns: - tuple[bool, str]: A tuple where the first element is a boolean indicating success or failure of the - operation, and the second element is a message providing additional information. - - Raises: - DuplicateChild: There are duplicates in the list of children. - InvalidChild: Child is the hotkey. - NonAssociatedColdKey: The coldkey does not own the hotkey or the child is the same as the hotkey. - NotEnoughStakeToSetChildkeys: Parent key doesn't have minimum own stake. - ProportionOverflow: The sum of the proportions does exceed uint64. - RegistrationNotPermittedOnRootSubnet: Attempting to register a child on the root network. - SubNetworkDoesNotExist: Attempting to register to a non-existent network. - TooManyChildren: Too many children in request. - TxRateLimitExceeded: Hotkey hit the rate limit. - bittensor_wallet.errors.KeyFileError: Failed to decode keyfile data. - bittensor_wallet.errors.PasswordError: Decryption failed or wrong password for decryption provided. - """ - - unlock = unlock_key(wallet, raise_error=raise_error) - - if not unlock.success: - return False, unlock.message - - call = self.substrate.compose_call( - call_module="SubtensorModule", - call_function="set_children", - call_params={ - "children": [ - ( - float_to_u64(proportion), - child_hotkey, - ) - for proportion, child_hotkey in children - ], - "hotkey": hotkey, - "netuid": netuid, - }, - ) - - return self.sign_and_send_extrinsic( - call, - wallet, - wait_for_inclusion, - wait_for_finalization, - raise_error=raise_error, - ) - - def set_delegate_take( - self, - wallet: "Wallet", - hotkey_ss58: str, - take: float, - wait_for_inclusion: bool = True, - wait_for_finalization: bool = True, - raise_error: bool = False, - ) -> tuple[bool, str]: - """ - Sets the delegate 'take' percentage for a nueron identified by its hotkey. - The 'take' represents the percentage of rewards that the delegate claims from its nominators' stakes. - - Arguments: - wallet (bittensor_wallet.Wallet): bittensor wallet instance. - hotkey_ss58 (str): The ``SS58`` address of the neuron's hotkey. - take (float): Percentage reward for the delegate. - wait_for_inclusion (bool): Waits for the transaction to be included in a block. - wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. - raise_error: Raises relevant exception rather than returning `False` if unsuccessful. - - Returns: - tuple[bool, str]: A tuple where the first element is a boolean indicating success or failure of the - operation, and the second element is a message providing additional information. - - Raises: - DelegateTakeTooHigh: Delegate take is too high. - DelegateTakeTooLow: Delegate take is too low. - DelegateTxRateLimitExceeded: A transactor exceeded the rate limit for delegate transaction. - HotKeyAccountNotExists: The hotkey does not exists. - NonAssociatedColdKey: Request to stake, unstake or subscribe is made by a coldkey that is not associated with the hotkey account. - bittensor_wallet.errors.PasswordError: Decryption failed or wrong password for decryption provided. - bittensor_wallet.errors.KeyFileError: Failed to decode keyfile data. - - The delegate take is a critical parameter in the network's incentive structure, influencing the distribution of - rewards among neurons and their nominators. - """ - - # u16 representation of the take - take_u16 = int(take * 0xFFFF) - - current_take = self.get_delegate_take(hotkey_ss58) - current_take_u16 = int(current_take * 0xFFFF) - - if current_take_u16 == take_u16: - logging.info(":white_heavy_check_mark: [green]Already Set[/green]") - return True, "" - - logging.info(f"Updating {hotkey_ss58} take: current={current_take} new={take}") - - if current_take_u16 < take_u16: - success, error = increase_take_extrinsic( - self, - wallet, - hotkey_ss58, - take_u16, - wait_for_finalization=wait_for_finalization, - wait_for_inclusion=wait_for_inclusion, - raise_error=raise_error, - ) - else: - success, error = decrease_take_extrinsic( - self, - wallet, - hotkey_ss58, - take_u16, - wait_for_finalization=wait_for_finalization, - wait_for_inclusion=wait_for_inclusion, - raise_error=raise_error, - ) - - if success: - logging.info(":white_heavy_check_mark: [green]Take Updated[/green]") - - return success, error - def is_hotkey_delegate(self, hotkey_ss58: str, block: Optional[int] = None) -> bool: """ Determines whether a given hotkey (public key) is a delegate on the Bittensor network. This function checks if @@ -3237,6 +3092,157 @@ def root_set_weights( period=period, ) + def set_children( + self, + wallet: "Wallet", + hotkey: str, + netuid: int, + children: list[tuple[float, str]], + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, + raise_error: bool = False, + ) -> tuple[bool, str]: + """ + Allows a coldkey to set children keys. + + Arguments: + wallet (bittensor_wallet.Wallet): bittensor wallet instance. + hotkey (str): The ``SS58`` address of the neuron's hotkey. + netuid (int): The netuid value. + children (list[tuple[float, str]]): A list of children with their proportions. + wait_for_inclusion (bool): Waits for the transaction to be included in a block. + wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. + raise_error: Raises relevant exception rather than returning `False` if unsuccessful. + + Returns: + tuple[bool, str]: A tuple where the first element is a boolean indicating success or failure of the + operation, and the second element is a message providing additional information. + + Raises: + DuplicateChild: There are duplicates in the list of children. + InvalidChild: Child is the hotkey. + NonAssociatedColdKey: The coldkey does not own the hotkey or the child is the same as the hotkey. + NotEnoughStakeToSetChildkeys: Parent key doesn't have minimum own stake. + ProportionOverflow: The sum of the proportions does exceed uint64. + RegistrationNotPermittedOnRootSubnet: Attempting to register a child on the root network. + SubNetworkDoesNotExist: Attempting to register to a non-existent network. + TooManyChildren: Too many children in request. + TxRateLimitExceeded: Hotkey hit the rate limit. + bittensor_wallet.errors.KeyFileError: Failed to decode keyfile data. + bittensor_wallet.errors.PasswordError: Decryption failed or wrong password for decryption provided. + """ + + unlock = unlock_key(wallet, raise_error=raise_error) + + if not unlock.success: + return False, unlock.message + + call = self.substrate.compose_call( + call_module="SubtensorModule", + call_function="set_children", + call_params={ + "children": [ + ( + float_to_u64(proportion), + child_hotkey, + ) + for proportion, child_hotkey in children + ], + "hotkey": hotkey, + "netuid": netuid, + }, + ) + + return self.sign_and_send_extrinsic( + call, + wallet, + wait_for_inclusion, + wait_for_finalization, + raise_error=raise_error, + ) + + def set_delegate_take( + self, + wallet: "Wallet", + hotkey_ss58: str, + take: float, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = True, + raise_error: bool = False, + period: Optional[int] = None, + ) -> tuple[bool, str]: + """ + Sets the delegate 'take' percentage for a neuron identified by its hotkey. + The 'take' represents the percentage of rewards that the delegate claims from its nominators' stakes. + + Arguments: + wallet (bittensor_wallet.Wallet): bittensor wallet instance. + hotkey_ss58 (str): The ``SS58`` address of the neuron's hotkey. + take (float): Percentage reward for the delegate. + wait_for_inclusion (bool): Waits for the transaction to be included in a block. + wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. + + Returns: + tuple[bool, str]: A tuple where the first element is a boolean indicating success or failure of the + operation, and the second element is a message providing additional information. + + Raises: + DelegateTakeTooHigh: Delegate take is too high. + DelegateTakeTooLow: Delegate take is too low. + DelegateTxRateLimitExceeded: A transactor exceeded the rate limit for delegate transaction. + HotKeyAccountNotExists: The hotkey does not exist. + NonAssociatedColdKey: Request to stake, unstake, or subscribe is made by a coldkey that is not associated with the hotkey account. + bittensor_wallet.errors.PasswordError: Decryption failed or wrong password for decryption provided. + bittensor_wallet.errors.KeyFileError: Failed to decode keyfile data. + + The delegate take is a critical parameter in the network's incentive structure, influencing the distribution of + rewards among neurons and their nominators. + """ + + # u16 representation of the take + take_u16 = int(take * 0xFFFF) + + current_take = self.get_delegate_take(hotkey_ss58) + current_take_u16 = int(current_take * 0xFFFF) + + if current_take_u16 == take_u16: + logging.info(":white_heavy_check_mark: [green]Already Set[/green]") + return True, "" + + logging.info(f"Updating {hotkey_ss58} take: current={current_take} new={take}") + + if current_take_u16 < take_u16: + success, error = increase_take_extrinsic( + self, + wallet, + hotkey_ss58, + take_u16, + wait_for_finalization=wait_for_finalization, + wait_for_inclusion=wait_for_inclusion, + raise_error=raise_error, + period=period + ) + else: + success, error = decrease_take_extrinsic( + self, + wallet, + hotkey_ss58, + take_u16, + wait_for_finalization=wait_for_finalization, + wait_for_inclusion=wait_for_inclusion, + raise_error=raise_error, + period=period + ) + + if success: + logging.info(":white_heavy_check_mark: [green]Take Updated[/green]") + + return success, error + def set_subnet_identity( self, wallet: "Wallet", From 363ba28d2d5d78066e3b423f02b25453cc1d7bee Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 23:18:03 -0700 Subject: [PATCH 129/251] refactoring --- bittensor/core/extrinsics/take.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bittensor/core/extrinsics/take.py b/bittensor/core/extrinsics/take.py index 4a18ec0748..20f4de2f8f 100644 --- a/bittensor/core/extrinsics/take.py +++ b/bittensor/core/extrinsics/take.py @@ -79,8 +79,8 @@ def decrease_take_extrinsic( wait_for_inclusion (bool, optional): Wait for inclusion before returning. Defaults to True. wait_for_finalization (bool, optional): Wait for finalization before returning. Defaults to True. raise_error (bool, optional): Raise error on failure. Defaults to False. - period: The number of blocks during which the transaction will remain valid after it's submitted. If - the transaction is not included in a block within that number of blocks, it will expire and be rejected. + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. + If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. Returns: From d1f0a9079219af03a9b64d1c85f8d29d098e3044 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 23:18:21 -0700 Subject: [PATCH 130/251] transfer extrinsics --- bittensor/core/extrinsics/asyncex/transfer.py | 42 +++++++++------ bittensor/core/extrinsics/transfer.py | 53 +++++++++++-------- 2 files changed, 57 insertions(+), 38 deletions(-) diff --git a/bittensor/core/extrinsics/asyncex/transfer.py b/bittensor/core/extrinsics/asyncex/transfer.py index 1347c44260..a1c781310c 100644 --- a/bittensor/core/extrinsics/asyncex/transfer.py +++ b/bittensor/core/extrinsics/asyncex/transfer.py @@ -1,9 +1,8 @@ import asyncio -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Optional from bittensor.core.settings import NETWORK_EXPLORER_MAP from bittensor.utils import ( - format_error_message, get_explorer_url_for_network, is_valid_bittensor_address_or_public_key, unlock_key, @@ -23,6 +22,7 @@ async def _do_transfer( amount: "Balance", wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> tuple[bool, str, str]: """ Makes transfer from wallet to destination public key address. @@ -34,8 +34,11 @@ async def _do_transfer( amount (bittensor.utils.balance.Balance): Amount to stake as Bittensor balance. wait_for_inclusion (bool): If set, waits for the extrinsic to enter a block before returning `True`, or returns `False` if the extrinsic fails to enter the block within the timeout. - wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning + wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning `True`, or returns `False` if the extrinsic fails to be finalized within the timeout. + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. + If the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: success, block hash, formatted error message @@ -45,24 +48,25 @@ async def _do_transfer( call_function="transfer_allow_death", call_params={"dest": destination, "value": amount.rao}, ) - extrinsic = await subtensor.substrate.create_signed_extrinsic( - call=call, keypair=wallet.coldkey - ) - response = await subtensor.substrate.submit_extrinsic( - extrinsic=extrinsic, + + success, message = await subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) + # We only wait here if we expect finalization. if not wait_for_finalization and not wait_for_inclusion: - return True, "", "Success, extrinsic submitted without waiting." + return True, "", message # Otherwise continue with finalization. - if await response.is_success: - block_hash_ = response.block_hash + if success: + block_hash_ = await subtensor.get_block_hash() return True, block_hash_, "Success with response." - return False, "", format_error_message(await response.error_message) + return False, "", message async def transfer_extrinsic( @@ -74,6 +78,7 @@ async def transfer_extrinsic( wait_for_inclusion: bool = True, wait_for_finalization: bool = False, keep_alive: bool = True, + period: Optional[int] = None, ) -> bool: """Transfers funds from this wallet to the destination public key address. @@ -85,9 +90,12 @@ async def transfer_extrinsic( transfer_all (bool): Whether to transfer all funds from this wallet to the destination address. wait_for_inclusion (bool): If set, waits for the extrinsic to enter a block before returning `True`, or returns `False` if the extrinsic fails to enter the block within the timeout. - wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning + wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning `True`, or returns `False` if the extrinsic fails to be finalized within the timeout. keep_alive (bool): If set, keeps the account alive by keeping the balance above the existential deposit. + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. + If the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: success (bool): Flag is `True` if extrinsic was finalized or included in the block. If we did not wait for @@ -100,6 +108,7 @@ async def transfer_extrinsic( f":cross_mark: [red]Invalid destination SS58 address[/red]: {destination}" ) return False + logging.info(f"Initiating transfer on network: {subtensor.network}") # Unlock wallet coldkey. if not (unlock := unlock_key(wallet)).success: @@ -148,6 +157,7 @@ async def transfer_extrinsic( amount=amount, wait_for_finalization=wait_for_finalization, wait_for_inclusion=wait_for_inclusion, + period=period, ) if success: @@ -173,6 +183,6 @@ async def transfer_extrinsic( f"Balance: [blue]{account_balance}[/blue] :arrow_right: [green]{new_balance}[/green]" ) return True - else: - logging.error(f":cross_mark: [red]Failed[/red]: {err_msg}") - return False + + logging.error(f":cross_mark: [red]Failed[/red]: {err_msg}") + return False diff --git a/bittensor/core/extrinsics/transfer.py b/bittensor/core/extrinsics/transfer.py index badc4d7cf9..03624097d0 100644 --- a/bittensor/core/extrinsics/transfer.py +++ b/bittensor/core/extrinsics/transfer.py @@ -1,13 +1,12 @@ -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Optional from bittensor.core.settings import NETWORK_EXPLORER_MAP -from bittensor.utils.balance import Balance from bittensor.utils import ( is_valid_bittensor_address_or_public_key, unlock_key, get_explorer_url_for_network, - format_error_message, ) +from bittensor.utils.balance import Balance from bittensor.utils.btlogging import logging if TYPE_CHECKING: @@ -22,6 +21,7 @@ def _do_transfer( amount: Balance, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> tuple[bool, str, str]: """ Makes transfer from wallet to destination public key address. @@ -33,8 +33,11 @@ def _do_transfer( amount (bittensor.utils.balance.Balance): Amount to stake as Bittensor balance. wait_for_inclusion (bool): If set, waits for the extrinsic to enter a block before returning `True`, or returns `False` if the extrinsic fails to enter the block within the timeout. - wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning + wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning `True`, or returns `False` if the extrinsic fails to be finalized within the timeout. + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. + If the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: success, block hash, formatted error message @@ -44,24 +47,25 @@ def _do_transfer( call_function="transfer_allow_death", call_params={"dest": destination, "value": amount.rao}, ) - extrinsic = subtensor.substrate.create_signed_extrinsic( - call=call, keypair=wallet.coldkey - ) - response = subtensor.substrate.submit_extrinsic( - extrinsic=extrinsic, + + success, message = subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) + # We only wait here if we expect finalization. if not wait_for_finalization and not wait_for_inclusion: - return True, "", "Success, extrinsic submitted without waiting." + return True, "", message # Otherwise continue with finalization. - if response.is_success: - block_hash_ = response.block_hash + if success: + block_hash_ = subtensor.get_block_hash() return True, block_hash_, "Success with response." - return False, "", format_error_message(response.error_message) + return False, "", message def transfer_extrinsic( @@ -73,6 +77,7 @@ def transfer_extrinsic( wait_for_inclusion: bool = True, wait_for_finalization: bool = False, keep_alive: bool = True, + period: Optional[int] = None, ) -> bool: """Transfers funds from this wallet to the destination public key address. @@ -84,21 +89,24 @@ def transfer_extrinsic( transfer_all (bool): Whether to transfer all funds from this wallet to the destination address. wait_for_inclusion (bool): If set, waits for the extrinsic to enter a block before returning `True`, or returns `False` if the extrinsic fails to enter the block within the timeout. - wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning + wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning `True`, or returns `False` if the extrinsic fails to be finalized within the timeout. keep_alive (bool): If set, keeps the account alive by keeping the balance above the existential deposit. + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. + If the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: success (bool): Flag is `True` if extrinsic was finalized or included in the block. If we did not wait for finalization / inclusion, the response is `True`, regardless of its inclusion. """ - destination = dest # Validate destination address. - if not is_valid_bittensor_address_or_public_key(destination): + if not is_valid_bittensor_address_or_public_key(dest): logging.error( - f":cross_mark: [red]Invalid destination SS58 address[/red]: {destination}" + f":cross_mark: [red]Invalid destination SS58 address[/red]: {dest}" ) return False + logging.info(f"Initiating transfer on network: {subtensor.network}") # Unlock wallet coldkey. if not (unlock := unlock_key(wallet)).success: @@ -119,7 +127,7 @@ def transfer_extrinsic( else: existential_deposit = subtensor.get_existential_deposit(block=block) - fee = subtensor.get_transfer_fee(wallet=wallet, dest=destination, value=amount) + fee = subtensor.get_transfer_fee(wallet=wallet, dest=dest, value=amount) # Check if we have enough balance. if transfer_all is True: @@ -139,10 +147,11 @@ def transfer_extrinsic( success, block_hash, err_msg = _do_transfer( subtensor=subtensor, wallet=wallet, - destination=destination, + destination=dest, amount=amount, wait_for_finalization=wait_for_finalization, wait_for_inclusion=wait_for_inclusion, + period=period, ) if success: @@ -168,6 +177,6 @@ def transfer_extrinsic( f"Balance: [blue]{account_balance}[/blue] :arrow_right: [green]{new_balance}[/green]" ) return True - else: - logging.error(f":cross_mark: [red]Failed[/red]: {err_msg}") - return False + + logging.error(f":cross_mark: [red]Failed[/red]: {err_msg}") + return False From ac9685c087232737e9621bc745cd60fbb7aeb9a4 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 23:19:50 -0700 Subject: [PATCH 131/251] transfer extrinsics calls in subtensors --- bittensor/core/async_subtensor.py | 10 +++++++--- bittensor/core/subtensor.py | 11 ++++++++--- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index c10be33b69..2b11192dcf 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -3283,7 +3283,7 @@ async def sign_and_send_extrinsic( # We only wait here if we expect finalization. if not wait_for_finalization and not wait_for_inclusion: message = "Not waiting for finalization or inclusion." - logging.debug(message) + logging.debug(f"{message}. Extrinsic: {extrinsic}") return True, message if await response.is_success: @@ -4311,6 +4311,7 @@ async def transfer( wait_for_inclusion: bool = True, wait_for_finalization: bool = False, keep_alive: bool = True, + period: Optional[int] = None, ) -> bool: """ Transfer token of amount to destination. @@ -4318,13 +4319,15 @@ async def transfer( Arguments: wallet (bittensor_wallet.Wallet): Source wallet for the transfer. dest (str): Destination address for the transfer. - amount (float): Amount of tokens to transfer. + amount (float): Number of tokens to transfer. transfer_all (bool): Flag to transfer all tokens. Default is ``False``. wait_for_inclusion (bool): Waits for the transaction to be included in a block. Default is ``True``. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. Default is ``False``. keep_alive (bool): Flag to keep the connection alive. Default is ``True``. - + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: `True` if the transferring was successful, otherwise `False`. """ @@ -4338,6 +4341,7 @@ async def transfer( wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, keep_alive=keep_alive, + period=period, ) async def unstake( diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 685b715ed8..460fdfd6b8 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -2566,8 +2566,8 @@ def sign_and_send_extrinsic( ) # We only wait here if we expect finalization. if not wait_for_finalization and not wait_for_inclusion: - message = f"Not waiting for finalization or inclusion. Extrinsic: {extrinsic}" - logging.debug(message) + message = f"Not waiting for finalization or inclusion." + logging.debug(f"{message}. Extrinsic: {extrinsic}") return True, message if response.is_success: @@ -3541,6 +3541,7 @@ def transfer( wait_for_finalization: bool = False, transfer_all: bool = False, keep_alive: bool = True, + period: Optional[int] = None, ) -> bool: """ Transfer token of amount to destination. @@ -3548,12 +3549,15 @@ def transfer( Arguments: wallet (bittensor_wallet.Wallet): Source wallet for the transfer. dest (str): Destination address for the transfer. - amount (float): Amount of tokens to transfer. + amount (float): Number of tokens to transfer. transfer_all (bool): Flag to transfer all tokens. Default is ``False``. wait_for_inclusion (bool): Waits for the transaction to be included in a block. Default is ``True``. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. Default is ``False``. keep_alive (bool): Flag to keep the connection alive. Default is ``True``. + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: `True` if the transferring was successful, otherwise `False`. @@ -3568,6 +3572,7 @@ def transfer( wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, keep_alive=keep_alive, + period=period, ) def transfer_stake( From 770a50acb1e92984d2a5e30a86492f21e38cf078 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 23:19:57 -0700 Subject: [PATCH 132/251] fixed tests --- .../extrinsics/asyncex/test_transfer.py | 91 ++++++------------- tests/unit_tests/extrinsics/test_transfer.py | 15 +-- 2 files changed, 33 insertions(+), 73 deletions(-) diff --git a/tests/unit_tests/extrinsics/asyncex/test_transfer.py b/tests/unit_tests/extrinsics/asyncex/test_transfer.py index 0fa70a8e75..1abd2fe15c 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_transfer.py +++ b/tests/unit_tests/extrinsics/asyncex/test_transfer.py @@ -9,22 +9,13 @@ async def test_do_transfer_success(subtensor, fake_wallet, mocker): # Preps fake_destination = "destination_address" fake_amount = mocker.Mock(autospec=Balance, rao=1000) + fake_block_hash = "fake_block_hash" - fake_call = mocker.AsyncMock() - fake_extrinsic = mocker.AsyncMock() - fake_response = mocker.Mock() - - fake_response.is_success = mocker.AsyncMock(return_value=True)() - fake_response.process_events = mocker.AsyncMock() - fake_response.block_hash = "fake_block_hash" - - mocker.patch.object(subtensor.substrate, "compose_call", return_value=fake_call) - mocker.patch.object( - subtensor.substrate, "create_signed_extrinsic", return_value=fake_extrinsic - ) + mocker.patch.object(subtensor.substrate, "compose_call") mocker.patch.object( - subtensor.substrate, "submit_extrinsic", return_value=fake_response + subtensor, "sign_and_send_extrinsic", new=mocker.AsyncMock(return_value=(True, "")) ) + mocker.patch.object(subtensor, "get_block_hash", return_value=fake_block_hash) # Call success, block_hash, error_message = await async_transfer._do_transfer( @@ -37,18 +28,18 @@ async def test_do_transfer_success(subtensor, fake_wallet, mocker): ) # Asserts - subtensor.substrate.compose_call.assert_called_once_with( + subtensor.substrate.compose_call.assert_awaited_once_with( call_module="Balances", call_function="transfer_allow_death", call_params={"dest": fake_destination, "value": fake_amount.rao}, ) - subtensor.substrate.create_signed_extrinsic.assert_called_once_with( - call=subtensor.substrate.compose_call.return_value, keypair=fake_wallet.coldkey - ) - subtensor.substrate.submit_extrinsic.assert_called_once_with( - extrinsic=subtensor.substrate.create_signed_extrinsic.return_value, + + subtensor.sign_and_send_extrinsic.assert_awaited_once_with( + call=subtensor.substrate.compose_call.return_value, + wallet=fake_wallet, wait_for_inclusion=True, wait_for_finalization=True, + period=None, ) assert success is True assert block_hash == "fake_block_hash" @@ -61,28 +52,13 @@ async def test_do_transfer_failure(subtensor, fake_wallet, mocker): # Preps fake_destination = "destination_address" fake_amount = mocker.Mock(autospec=Balance, rao=1000) + fake_block_hash = "fake_block_hash" - fake_call = mocker.AsyncMock() - fake_extrinsic = mocker.AsyncMock() - fake_response = mocker.Mock() - - fake_response.is_success = mocker.AsyncMock(return_value=False)() - fake_response.process_events = mocker.AsyncMock() - fake_response.error_message = mocker.AsyncMock(return_value="Fake error message")() - - mocker.patch.object(subtensor.substrate, "compose_call", return_value=fake_call) + mocker.patch.object(subtensor.substrate, "compose_call") mocker.patch.object( - subtensor.substrate, "create_signed_extrinsic", return_value=fake_extrinsic - ) - mocker.patch.object( - subtensor.substrate, "submit_extrinsic", return_value=fake_response - ) - - mocked_format_error_message = mocker.patch.object( - async_transfer, - "format_error_message", - return_value="Formatted error message", + subtensor, "sign_and_send_extrinsic", new=mocker.AsyncMock(return_value=(False, "Formatted error message")) ) + mocker.patch.object(subtensor, "get_block_hash", return_value=fake_block_hash) # Call success, block_hash, error_message = await async_transfer._do_transfer( @@ -95,22 +71,21 @@ async def test_do_transfer_failure(subtensor, fake_wallet, mocker): ) # Asserts - subtensor.substrate.compose_call.assert_called_once_with( + subtensor.substrate.compose_call.assert_awaited_once_with( call_module="Balances", call_function="transfer_allow_death", call_params={"dest": fake_destination, "value": fake_amount.rao}, ) - subtensor.substrate.create_signed_extrinsic.assert_called_once_with( - call=subtensor.substrate.compose_call.return_value, keypair=fake_wallet.coldkey - ) - subtensor.substrate.submit_extrinsic.assert_called_once_with( - extrinsic=subtensor.substrate.create_signed_extrinsic.return_value, + + subtensor.sign_and_send_extrinsic.assert_awaited_once_with( + call=subtensor.substrate.compose_call.return_value, + wallet=fake_wallet, wait_for_inclusion=True, wait_for_finalization=True, + period=None, ) assert success is False assert block_hash == "" - mocked_format_error_message.assert_called_once_with("Fake error message") assert error_message == "Formatted error message" @@ -120,19 +95,13 @@ async def test_do_transfer_no_waiting(subtensor, fake_wallet, mocker): # Preps fake_destination = "destination_address" fake_amount = mocker.Mock(autospec=Balance, rao=1000) + fake_block_hash = "fake_block_hash" - fake_call = mocker.AsyncMock() - fake_extrinsic = mocker.AsyncMock() - - mocker.patch.object(subtensor.substrate, "compose_call", return_value=fake_call) + mocker.patch.object(subtensor.substrate, "compose_call") mocker.patch.object( - subtensor.substrate, "create_signed_extrinsic", return_value=fake_extrinsic - ) - mocker.patch.object( - subtensor.substrate, - "submit_extrinsic", - return_value=mocker.Mock(), + subtensor, "sign_and_send_extrinsic", new=mocker.AsyncMock(return_value=(False, "Success, extrinsic submitted without waiting.")) ) + mocker.patch.object(subtensor, "get_block_hash", return_value=fake_block_hash) # Call success, block_hash, error_message = await async_transfer._do_transfer( @@ -145,18 +114,18 @@ async def test_do_transfer_no_waiting(subtensor, fake_wallet, mocker): ) # Asserts - subtensor.substrate.compose_call.assert_called_once_with( + subtensor.substrate.compose_call.assert_awaited_once_with( call_module="Balances", call_function="transfer_allow_death", call_params={"dest": fake_destination, "value": fake_amount.rao}, ) - subtensor.substrate.create_signed_extrinsic.assert_called_once_with( - call=subtensor.substrate.compose_call.return_value, keypair=fake_wallet.coldkey - ) - subtensor.substrate.submit_extrinsic.assert_called_once_with( - extrinsic=subtensor.substrate.create_signed_extrinsic.return_value, + + subtensor.sign_and_send_extrinsic.assert_awaited_once_with( + call=subtensor.substrate.compose_call.return_value, + wallet=fake_wallet, wait_for_inclusion=False, wait_for_finalization=False, + period=None, ) assert success is True assert block_hash == "" diff --git a/tests/unit_tests/extrinsics/test_transfer.py b/tests/unit_tests/extrinsics/test_transfer.py index 9424352a55..097156ba49 100644 --- a/tests/unit_tests/extrinsics/test_transfer.py +++ b/tests/unit_tests/extrinsics/test_transfer.py @@ -39,7 +39,7 @@ def test_do_transfer_is_success_true(subtensor, fake_wallet, mocker): # subtensor.substrate.submit_extrinsic.return_value.process_events.assert_called_once() assert result == ( True, - subtensor.substrate.submit_extrinsic.return_value.block_hash, + subtensor.substrate.get_chain_head.return_value, "Success with response.", ) @@ -54,12 +54,6 @@ def test_do_transfer_is_success_false(subtensor, fake_wallet, mocker): subtensor.substrate.submit_extrinsic.return_value.is_success = False - mocked_format_error_message = mocker.Mock() - mocker.patch( - "bittensor.core.extrinsics.transfer.format_error_message", - mocked_format_error_message, - ) - # Call result = _do_transfer( subtensor, @@ -84,14 +78,11 @@ def test_do_transfer_is_success_false(subtensor, fake_wallet, mocker): wait_for_inclusion=fake_wait_for_inclusion, wait_for_finalization=fake_wait_for_finalization, ) - mocked_format_error_message.assert_called_once_with( - subtensor.substrate.submit_extrinsic.return_value.error_message - ) assert result == ( False, "", - mocked_format_error_message.return_value, + "Subtensor returned `UnknownError(UnknownType)` error. This means: `Unknown Description`.", ) @@ -127,4 +118,4 @@ def test_do_transfer_no_waits(subtensor, fake_wallet, mocker): wait_for_inclusion=fake_wait_for_inclusion, wait_for_finalization=fake_wait_for_finalization, ) - assert result == (True, "", "Success, extrinsic submitted without waiting.") + assert result == (True, "", "Not waiting for finalization or inclusion.") From 889670202913f093d48d2f1d377cf8c0556ad645 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 23:35:46 -0700 Subject: [PATCH 133/251] replace error + formatting error for staking --- bittensor/core/extrinsics/asyncex/staking.py | 15 +++++---------- bittensor/core/extrinsics/staking.py | 15 +++++---------- 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/bittensor/core/extrinsics/asyncex/staking.py b/bittensor/core/extrinsics/asyncex/staking.py index f06eb8dced..e0e33e2e9c 100644 --- a/bittensor/core/extrinsics/asyncex/staking.py +++ b/bittensor/core/extrinsics/asyncex/staking.py @@ -3,7 +3,7 @@ from async_substrate_interface.errors import SubstrateRequestException from bittensor.core.errors import StakeError, NotRegisteredError from bittensor.core.extrinsics.utils import get_old_stakes -from bittensor.utils import unlock_key +from bittensor.utils import unlock_key, format_error_message from bittensor.utils.balance import Balance from bittensor.utils.btlogging import logging @@ -203,8 +203,8 @@ async def add_stake_extrinsic( logging.error(f":cross_mark: [red]Failed: {err_msg}.[/red]") return False - except SubstrateRequestException as e: - logging.error(f":cross_mark: [red]Add Stake Error: {e}[/red]") + except SubstrateRequestException as error: + logging.error(f":cross_mark: [red]Add Stake Error: {format_error_message(error)}[/red]") return False @@ -400,13 +400,8 @@ async def add_stake_multiple_extrinsic( logging.error(f":cross_mark: [red]Failed: {message}.[/red]") continue - except NotRegisteredError: - logging.error( - f":cross_mark: [red]Hotkey: {hotkey_ss58} is not registered.[/red]" - ) - continue - except StakeError as e: - logging.error(f":cross_mark: [red]Stake Error: {e}[/red]") + except SubstrateRequestException as error: + logging.error(f":cross_mark: [red]Add Stake Multiple error: {format_error_message(error)}[/red]") continue if successful_stakes != 0: diff --git a/bittensor/core/extrinsics/staking.py b/bittensor/core/extrinsics/staking.py index 8b99743c81..ae883a72ee 100644 --- a/bittensor/core/extrinsics/staking.py +++ b/bittensor/core/extrinsics/staking.py @@ -4,7 +4,7 @@ from bittensor.core.errors import StakeError, NotRegisteredError from bittensor.core.extrinsics.utils import get_old_stakes -from bittensor.utils import unlock_key +from bittensor.utils import unlock_key, format_error_message from bittensor.utils.balance import Balance from bittensor.utils.btlogging import logging @@ -199,8 +199,8 @@ def add_stake_extrinsic( logging.error(f":cross_mark: [red]Failed: {message}.[/red]") return False - except SubstrateRequestException as e: - logging.error(f":cross_mark: [red]Add Stake Error: {e}[/red]") + except SubstrateRequestException as error: + logging.error(f":cross_mark: [red]Add Stake Error: {format_error_message((error))}[/red]") return False @@ -385,13 +385,8 @@ def add_stake_multiple_extrinsic( logging.error(f":cross_mark: [red]Failed[/red]: {message}") continue - except NotRegisteredError: - logging.error( - f":cross_mark: [red]Hotkey: {hotkey_ss58} is not registered.[/red]" - ) - continue - except StakeError as e: - logging.error(f":cross_mark: [red]Stake Error: {e}[/red]") + except SubstrateRequestException as error: + logging.error(f":cross_mark: [red]Add Stake Multiple error: {format_error_message(error)}[/red]") continue if successful_stakes != 0: From de076c999ea577129dbfc5a0c0840846f45c1d1e Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 23:46:22 -0700 Subject: [PATCH 134/251] update unstaking extrinsics --- .../core/extrinsics/asyncex/unstaking.py | 45 +++++++------- bittensor/core/extrinsics/unstaking.py | 60 ++++++++++--------- 2 files changed, 54 insertions(+), 51 deletions(-) diff --git a/bittensor/core/extrinsics/asyncex/unstaking.py b/bittensor/core/extrinsics/asyncex/unstaking.py index 54fb43c79d..8e322e4c05 100644 --- a/bittensor/core/extrinsics/asyncex/unstaking.py +++ b/bittensor/core/extrinsics/asyncex/unstaking.py @@ -1,11 +1,12 @@ import asyncio from typing import Optional, TYPE_CHECKING -from bittensor.core.errors import StakeError, NotRegisteredError -from bittensor.utils import unlock_key +from async_substrate_interface.errors import SubstrateRequestException + +from bittensor.core.extrinsics.utils import get_old_stakes +from bittensor.utils import unlock_key, format_error_message from bittensor.utils.balance import Balance from bittensor.utils.btlogging import logging -from bittensor.core.extrinsics.utils import get_old_stakes if TYPE_CHECKING: from bittensor_wallet import Wallet @@ -23,6 +24,7 @@ async def unstake_extrinsic( safe_staking: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, + period: Optional[int] = None, ) -> bool: """Removes stake into the wallet coldkey from the specified hotkey ``uid``. @@ -40,6 +42,9 @@ async def unstake_extrinsic( safe_staking: If true, enables price safety checks allow_partial_stake: If true, allows partial unstaking if price tolerance exceeded rate_tolerance: Maximum allowed price decrease percentage (0.005 = 0.5%) + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. + If the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: success (bool): Flag is ``True`` if extrinsic was finalized or included in the block. If we did not wait for @@ -133,7 +138,7 @@ async def unstake_extrinsic( call_function=call_function, call_params=call_params, ) - staking_response, err_msg = await subtensor.sign_and_send_extrinsic( + success, message = await subtensor.sign_and_send_extrinsic( call, wallet, wait_for_inclusion, @@ -141,9 +146,10 @@ async def unstake_extrinsic( nonce_key="coldkeypub", sign_with="coldkey", use_nonce=True, + period=period, ) - if staking_response is True: # If we successfully unstaked. + if success is True: # If we successfully unstaked. # We only wait here if we expect finalization. if not wait_for_finalization and not wait_for_inclusion: return True @@ -174,21 +180,16 @@ async def unstake_extrinsic( ) return True else: - if safe_staking and "Custom error: 8" in err_msg: + if safe_staking and "Custom error: 8" in message: logging.error( ":cross_mark: [red]Failed[/red]: Price exceeded tolerance limit. Either increase price tolerance or enable partial staking." ) else: - logging.error(f":cross_mark: [red]Failed: {err_msg}.[/red]") + logging.error(f":cross_mark: [red]Failed: {message}.[/red]") return False - except NotRegisteredError: - logging.error( - f":cross_mark: [red]Hotkey: {wallet.hotkey_str} is not registered.[/red]" - ) - return False - except StakeError as e: - logging.error(f":cross_mark: [red]Stake Error: {e}[/red]") + except SubstrateRequestException as error: + logging.error(f":cross_mark: [red]Unstake filed with error: {format_error_message(error)}[/red]") return False @@ -200,6 +201,7 @@ async def unstake_multiple_extrinsic( amounts: Optional[list[Balance]] = None, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> bool: """Removes stake from each ``hotkey_ss58`` in the list, using each amount, to a common coldkey. @@ -213,6 +215,9 @@ async def unstake_multiple_extrinsic( returns ``False`` if the extrinsic fails to enter the block within the timeout. wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning ``True``, or returns ``False`` if the extrinsic fails to be finalized within the timeout. + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. + If the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: success (bool): Flag is ``True`` if extrinsic was finalized or included in the block. Flag is ``True`` if any @@ -317,6 +322,7 @@ async def unstake_multiple_extrinsic( nonce_key="coldkeypub", sign_with="coldkey", use_nonce=True, + period=period, ) if staking_response is True: # If we successfully unstaked. @@ -347,14 +353,9 @@ async def unstake_multiple_extrinsic( logging.error(f":cross_mark: [red]Failed: {err_msg}.[/red]") continue - except NotRegisteredError: - logging.error( - f":cross_mark: [red]Hotkey[/red] [blue]{hotkey_ss58}[/blue] [red]is not registered.[/red]" - ) - continue - except StakeError as e: - logging.error(f":cross_mark: [red]Stake Error: {e}[/red]") - continue + except SubstrateRequestException as error: + logging.error(f":cross_mark: [red]Multiple unstake filed with error: {format_error_message(error)}[/red]") + return False if successful_unstakes != 0: logging.info( diff --git a/bittensor/core/extrinsics/unstaking.py b/bittensor/core/extrinsics/unstaking.py index f49362a4db..f4bb46f425 100644 --- a/bittensor/core/extrinsics/unstaking.py +++ b/bittensor/core/extrinsics/unstaking.py @@ -1,8 +1,9 @@ from typing import Optional, TYPE_CHECKING -from bittensor.core.errors import StakeError, NotRegisteredError +from async_substrate_interface.errors import SubstrateRequestException + from bittensor.core.extrinsics.utils import get_old_stakes -from bittensor.utils import unlock_key +from bittensor.utils import unlock_key, format_error_message from bittensor.utils.balance import Balance from bittensor.utils.btlogging import logging @@ -22,6 +23,7 @@ def unstake_extrinsic( safe_staking: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, + period: Optional[int] = None, ) -> bool: """Removes stake into the wallet coldkey from the specified hotkey ``uid``. @@ -39,6 +41,9 @@ def unstake_extrinsic( safe_staking: If true, enables price safety checks allow_partial_stake: If true, allows partial unstaking if price tolerance exceeded rate_tolerance: Maximum allowed price decrease percentage (0.005 = 0.5%) + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. + If the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: success (bool): Flag is ``True`` if extrinsic was finalized or included in the block. If we did not wait for @@ -64,6 +69,7 @@ def unstake_extrinsic( block=block, ) + # Covert to bittensor.Balance if amount is None: # Unstake it all. logging.warning( @@ -131,17 +137,18 @@ def unstake_extrinsic( call_params=call_params, ) - staking_response, err_msg = subtensor.sign_and_send_extrinsic( - call, - wallet, - wait_for_inclusion, - wait_for_finalization, + success, message = subtensor.sign_and_send_extrinsic( + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, nonce_key="coldkeypub", sign_with="coldkey", use_nonce=True, + period=period, ) - if staking_response is True: # If we successfully unstaked. + if success is True: # If we successfully unstaked. # We only wait here if we expect finalization. if not wait_for_finalization and not wait_for_inclusion: return True @@ -170,21 +177,16 @@ def unstake_extrinsic( ) return True else: - if safe_staking and "Custom error: 8" in err_msg: + if safe_staking and "Custom error: 8" in message: logging.error( ":cross_mark: [red]Failed[/red]: Price exceeded tolerance limit. Either increase price tolerance or enable partial staking." ) else: - logging.error(f":cross_mark: [red]Failed: {err_msg}.[/red]") + logging.error(f":cross_mark: [red]Failed: {message}.[/red]") return False - except NotRegisteredError: - logging.error( - f":cross_mark: [red]Hotkey: {wallet.hotkey_str} is not registered.[/red]" - ) - return False - except StakeError as e: - logging.error(f":cross_mark: [red]Stake Error: {e}[/red]") + except SubstrateRequestException as error: + logging.error(f":cross_mark: [red]Unstake filed with error: {format_error_message(error)}[/red]") return False @@ -196,6 +198,7 @@ def unstake_multiple_extrinsic( amounts: Optional[list[Balance]] = None, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> bool: """Removes stake from each ``hotkey_ss58`` in the list, using each amount, to a common coldkey. @@ -209,6 +212,9 @@ def unstake_multiple_extrinsic( returns ``False`` if the extrinsic fails to enter the block within the timeout. wait_for_finalization (bool): If set, waits for the extrinsic to be finalized on the chain before returning ``True``, or returns ``False`` if the extrinsic fails to be finalized within the timeout. + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. + If the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: success (bool): Flag is ``True`` if extrinsic was finalized or included in the block. Flag is ``True`` if any @@ -298,13 +304,14 @@ def unstake_multiple_extrinsic( }, ) staking_response, err_msg = subtensor.sign_and_send_extrinsic( - call, - wallet, - wait_for_inclusion, - wait_for_finalization, + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, nonce_key="coldkeypub", sign_with="coldkey", use_nonce=True, + period=period, ) if staking_response is True: # If we successfully unstaked. @@ -334,14 +341,9 @@ def unstake_multiple_extrinsic( logging.error(f":cross_mark: [red]Failed: {err_msg}.[/red]") continue - except NotRegisteredError: - logging.error( - f":cross_mark: [red]Hotkey[/red] [blue]{hotkey_ss58}[/blue] [red]is not registered.[/red]" - ) - continue - except StakeError as e: - logging.error(":cross_mark: [red]Stake Error: {}[/red]".format(e)) - continue + except SubstrateRequestException as error: + logging.error(f":cross_mark: [red]Multiple unstake filed with error: {format_error_message(error)}[/red]") + return False if successful_unstakes != 0: logging.info( From 1bd2e4e2f0da93e74c6fe4b2680af5ec6399cb09 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 23:46:46 -0700 Subject: [PATCH 135/251] update subtensor unstaking calls --- bittensor/core/async_subtensor.py | 10 ++++++++++ bittensor/core/subtensor.py | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 2b11192dcf..c93007aa50 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -4355,6 +4355,7 @@ async def unstake( safe_staking: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, + period: Optional[int] = None, ) -> bool: """ Removes a specified amount of stake from a single hotkey account. This function is critical for adjusting @@ -4375,6 +4376,9 @@ async def unstake( exceed the threshold. Default is False. rate_tolerance (float): The maximum allowed price change ratio when unstaking. For example, 0.005 = 0.5% maximum price decrease. Only used when safe_staking is True. Default is 0.005. + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: bool: ``True`` if the unstaking process is successful, False otherwise. @@ -4394,6 +4398,7 @@ async def unstake( safe_staking=safe_staking, allow_partial_stake=allow_partial_stake, rate_tolerance=rate_tolerance, + period=period, ) async def unstake_multiple( @@ -4404,6 +4409,7 @@ async def unstake_multiple( amounts: Optional[list[Balance]] = None, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> bool: """ Performs batch unstaking from multiple hotkey accounts, allowing a neuron to reduce its staked amounts @@ -4418,6 +4424,9 @@ async def unstake_multiple( unstakes all available stakes. wait_for_inclusion (bool): Waits for the transaction to be included in a block. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: bool: ``True`` if the batch unstaking is successful, False otherwise. @@ -4433,6 +4442,7 @@ async def unstake_multiple( amounts=amounts, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 460fdfd6b8..c6cbd34373 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -3631,6 +3631,7 @@ def unstake( safe_staking: bool = False, allow_partial_stake: bool = False, rate_tolerance: float = 0.005, + period: Optional[int] = None, ) -> bool: """ Removes a specified amount of stake from a single hotkey account. This function is critical for adjusting @@ -3651,6 +3652,9 @@ def unstake( exceed the tolerance. Default is False. rate_tolerance (float): The maximum allowed price change ratio when unstaking. For example, 0.005 = 0.5% maximum price decrease. Only used when safe_staking is True. Default is 0.005. + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: bool: ``True`` if the unstaking process is successful, False otherwise. @@ -3671,6 +3675,7 @@ def unstake( safe_staking=safe_staking, allow_partial_stake=allow_partial_stake, rate_tolerance=rate_tolerance, + period=period, ) def unstake_multiple( @@ -3681,6 +3686,7 @@ def unstake_multiple( amounts: Optional[list[Balance]] = None, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + period: Optional[int] = None, ) -> bool: """ Performs batch unstaking from multiple hotkey accounts, allowing a neuron to reduce its staked amounts @@ -3695,6 +3701,9 @@ def unstake_multiple( unstakes all available stakes. wait_for_inclusion (bool): Waits for the transaction to be included in a block. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: bool: ``True`` if the batch unstaking is successful, False otherwise. @@ -3710,4 +3719,5 @@ def unstake_multiple( amounts=amounts, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) From ef80b8b66c07276d4387e9ee9ca9b6bbf8ff2c7a Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 23:46:54 -0700 Subject: [PATCH 136/251] fix related tests --- tests/unit_tests/extrinsics/test_unstaking.py | 18 ++++++++++-------- tests/unit_tests/test_async_subtensor.py | 1 + tests/unit_tests/test_subtensor.py | 4 ++++ 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/tests/unit_tests/extrinsics/test_unstaking.py b/tests/unit_tests/extrinsics/test_unstaking.py index 224fe4b640..2fdf0cbe47 100644 --- a/tests/unit_tests/extrinsics/test_unstaking.py +++ b/tests/unit_tests/extrinsics/test_unstaking.py @@ -43,13 +43,14 @@ def test_unstake_extrinsic(fake_wallet, mocker): }, ) fake_subtensor.sign_and_send_extrinsic.assert_called_once_with( - fake_subtensor.substrate.compose_call.return_value, - fake_wallet, - True, - True, + call=fake_subtensor.substrate.compose_call.return_value, + wallet=fake_wallet, + wait_for_inclusion=True, + wait_for_finalization=True, sign_with="coldkey", nonce_key="coldkeypub", use_nonce=True, + period=None, ) @@ -109,11 +110,12 @@ def test_unstake_multiple_extrinsic(fake_wallet, mocker): }, ) fake_subtensor.sign_and_send_extrinsic.assert_called_with( - fake_subtensor.substrate.compose_call.return_value, - fake_wallet, - True, - True, + call=fake_subtensor.substrate.compose_call.return_value, + wallet=fake_wallet, + wait_for_inclusion=True, + wait_for_finalization=True, sign_with="coldkey", nonce_key="coldkeypub", use_nonce=True, + period=None, ) diff --git a/tests/unit_tests/test_async_subtensor.py b/tests/unit_tests/test_async_subtensor.py index 892836e2d8..b61df6dfaa 100644 --- a/tests/unit_tests/test_async_subtensor.py +++ b/tests/unit_tests/test_async_subtensor.py @@ -2518,6 +2518,7 @@ async def test_transfer_success(subtensor, fake_wallet, mocker): wait_for_inclusion=True, wait_for_finalization=False, keep_alive=True, + period=None, ) assert result == mocked_transfer_extrinsic.return_value diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index e3588178c5..03b45a35fe 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -1327,6 +1327,7 @@ def test_transfer(subtensor, fake_wallet, mocker): wait_for_inclusion=fake_wait_for_inclusion, wait_for_finalization=fake_wait_for_finalization, keep_alive=True, + period=None, ) assert result == mocked_transfer_extrinsic.return_value @@ -2937,6 +2938,7 @@ def test_unstake_success(mocker, subtensor, fake_wallet): safe_staking=False, allow_partial_stake=False, rate_tolerance=0.005, + period=None, ) assert result == mock_unstake_extrinsic.return_value @@ -2973,6 +2975,7 @@ def test_unstake_with_safe_staking(mocker, subtensor, fake_wallet): safe_staking=True, allow_partial_stake=True, rate_tolerance=fake_rate_tolerance, + period=None, ) assert result == mock_unstake_extrinsic.return_value @@ -3095,6 +3098,7 @@ def test_unstake_multiple_success(mocker, subtensor, fake_wallet): amounts=fake_amounts, wait_for_inclusion=True, wait_for_finalization=False, + period=None, ) assert result == mock_unstake_multiple_extrinsic.return_value From bf7c9bbf24ce39e56c9660bcb5c1f78158379afa Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 May 2025 23:47:18 -0700 Subject: [PATCH 137/251] del unused imports --- bittensor/core/extrinsics/asyncex/staking.py | 3 ++- bittensor/core/extrinsics/staking.py | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bittensor/core/extrinsics/asyncex/staking.py b/bittensor/core/extrinsics/asyncex/staking.py index e0e33e2e9c..c148fa86f9 100644 --- a/bittensor/core/extrinsics/asyncex/staking.py +++ b/bittensor/core/extrinsics/asyncex/staking.py @@ -1,7 +1,8 @@ import asyncio from typing import Optional, Sequence, TYPE_CHECKING + from async_substrate_interface.errors import SubstrateRequestException -from bittensor.core.errors import StakeError, NotRegisteredError + from bittensor.core.extrinsics.utils import get_old_stakes from bittensor.utils import unlock_key, format_error_message from bittensor.utils.balance import Balance diff --git a/bittensor/core/extrinsics/staking.py b/bittensor/core/extrinsics/staking.py index ae883a72ee..86014c5b07 100644 --- a/bittensor/core/extrinsics/staking.py +++ b/bittensor/core/extrinsics/staking.py @@ -2,7 +2,6 @@ from async_substrate_interface.errors import SubstrateRequestException -from bittensor.core.errors import StakeError, NotRegisteredError from bittensor.core.extrinsics.utils import get_old_stakes from bittensor.utils import unlock_key, format_error_message from bittensor.utils.balance import Balance From d7a6ef17c3cce04d4427fc00428deca96d48d9d3 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 2 May 2025 00:44:36 -0700 Subject: [PATCH 138/251] fix a bunch of unit test's inner calls --- .../core/extrinsics/asyncex/move_stake.py | 10 +- bittensor/core/extrinsics/asyncex/root.py | 2 +- bittensor/core/extrinsics/asyncex/serving.py | 2 +- .../core/extrinsics/asyncex/unstaking.py | 16 ++-- bittensor/core/extrinsics/asyncex/weights.py | 24 ++--- bittensor/core/extrinsics/commit_weights.py | 16 ++-- bittensor/core/extrinsics/move_stake.py | 8 +- bittensor/core/extrinsics/root.py | 4 +- bittensor/core/extrinsics/take.py | 8 +- bittensor/core/extrinsics/utils.py | 7 +- .../extrinsics/asyncex/test_registration.py | 93 ++++++------------- .../extrinsics/test_commit_weights.py | 56 ++++------- tests/unit_tests/extrinsics/test_root.py | 4 +- tests/unit_tests/extrinsics/test_transfer.py | 49 +++++----- 14 files changed, 115 insertions(+), 184 deletions(-) diff --git a/bittensor/core/extrinsics/asyncex/move_stake.py b/bittensor/core/extrinsics/asyncex/move_stake.py index 8848e3f4e2..8cfd5a2604 100644 --- a/bittensor/core/extrinsics/asyncex/move_stake.py +++ b/bittensor/core/extrinsics/asyncex/move_stake.py @@ -82,7 +82,7 @@ async def transfer_stake_extrinsic( # Check sufficient stake stake_in_origin, stake_in_destination = await _get_stake_in_origin_and_dest( - subtensor, + subtensor=subtensor, origin_hotkey_ss58=hotkey_ss58, destination_hotkey_ss58=hotkey_ss58, origin_coldkey_ss58=wallet.coldkeypub.ss58_address, @@ -132,7 +132,7 @@ async def transfer_stake_extrinsic( # Get updated stakes origin_stake, dest_stake = await _get_stake_in_origin_and_dest( - subtensor, + subtensor=subtensor, origin_hotkey_ss58=hotkey_ss58, destination_hotkey_ss58=hotkey_ss58, origin_coldkey_ss58=wallet.coldkeypub.ss58_address, @@ -205,7 +205,7 @@ async def swap_stake_extrinsic( # Check sufficient stake stake_in_origin, stake_in_destination = await _get_stake_in_origin_and_dest( - subtensor, + subtensor=subtensor, origin_hotkey_ss58=hotkey_ss58, destination_hotkey_ss58=hotkey_ss58, origin_coldkey_ss58=wallet.coldkeypub.ss58_address, @@ -346,7 +346,7 @@ async def move_stake_extrinsic( # Check sufficient stake stake_in_origin, stake_in_destination = await _get_stake_in_origin_and_dest( - subtensor, + subtensor=subtensor, origin_hotkey_ss58=origin_hotkey, destination_hotkey_ss58=destination_hotkey, origin_coldkey_ss58=wallet.coldkeypub.ss58_address, @@ -395,7 +395,7 @@ async def move_stake_extrinsic( # Get updated stakes origin_stake, dest_stake = await _get_stake_in_origin_and_dest( - subtensor, + subtensor=subtensor, origin_hotkey_ss58=origin_hotkey, destination_hotkey_ss58=destination_hotkey, origin_coldkey_ss58=wallet.coldkeypub.ss58_address, diff --git a/bittensor/core/extrinsics/asyncex/root.py b/bittensor/core/extrinsics/asyncex/root.py index 74eb15689d..4641676e36 100644 --- a/bittensor/core/extrinsics/asyncex/root.py +++ b/bittensor/core/extrinsics/asyncex/root.py @@ -118,7 +118,7 @@ async def root_register_extrinsic( call_params={"hotkey": wallet.hotkey.ss58_address}, ) success, message = await subtensor.sign_and_send_extrinsic( - call, + call=call, wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, diff --git a/bittensor/core/extrinsics/asyncex/serving.py b/bittensor/core/extrinsics/asyncex/serving.py index 007be2a12b..c86a681b08 100644 --- a/bittensor/core/extrinsics/asyncex/serving.py +++ b/bittensor/core/extrinsics/asyncex/serving.py @@ -57,7 +57,7 @@ async def do_serve_axon( call_params=call_params.dict(), ) success, message = await subtensor.sign_and_send_extrinsic( - call, + call=call, wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, diff --git a/bittensor/core/extrinsics/asyncex/unstaking.py b/bittensor/core/extrinsics/asyncex/unstaking.py index 8e322e4c05..b114dc7e23 100644 --- a/bittensor/core/extrinsics/asyncex/unstaking.py +++ b/bittensor/core/extrinsics/asyncex/unstaking.py @@ -139,10 +139,10 @@ async def unstake_extrinsic( call_params=call_params, ) success, message = await subtensor.sign_and_send_extrinsic( - call, - wallet, - wait_for_inclusion, - wait_for_finalization, + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, nonce_key="coldkeypub", sign_with="coldkey", use_nonce=True, @@ -315,10 +315,10 @@ async def unstake_multiple_extrinsic( ) staking_response, err_msg = await subtensor.sign_and_send_extrinsic( - call, - wallet, - wait_for_inclusion, - wait_for_finalization, + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, nonce_key="coldkeypub", sign_with="coldkey", use_nonce=True, diff --git a/bittensor/core/extrinsics/asyncex/weights.py b/bittensor/core/extrinsics/asyncex/weights.py index 7f04ffd7bd..4b3d5cd56d 100644 --- a/bittensor/core/extrinsics/asyncex/weights.py +++ b/bittensor/core/extrinsics/asyncex/weights.py @@ -56,10 +56,10 @@ async def _do_commit_weights( }, ) return await subtensor.sign_and_send_extrinsic( - call, - wallet, - wait_for_inclusion, - wait_for_finalization, + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, use_nonce=True, period=period, nonce_key="hotkey", @@ -170,10 +170,10 @@ async def _do_reveal_weights( }, ) return await subtensor.sign_and_send_extrinsic( - call, - wallet, - wait_for_inclusion, - wait_for_finalization, + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, sign_with="hotkey", period=period, nonce_key="hotkey", @@ -288,10 +288,10 @@ async def _do_set_weights( }, ) success, message = await subtensor.sign_and_send_extrinsic( - call, - wallet, - wait_for_inclusion, - wait_for_finalization, + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, period=period, use_nonce=True, nonce_key="hotkey", diff --git a/bittensor/core/extrinsics/commit_weights.py b/bittensor/core/extrinsics/commit_weights.py index e139907b65..511d1364ef 100644 --- a/bittensor/core/extrinsics/commit_weights.py +++ b/bittensor/core/extrinsics/commit_weights.py @@ -50,10 +50,10 @@ def _do_commit_weights( }, ) return subtensor.sign_and_send_extrinsic( - call, - wallet, - wait_for_inclusion, - wait_for_finalization, + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, use_nonce=True, period=period, sign_with="hotkey", @@ -164,10 +164,10 @@ def _do_reveal_weights( }, ) return subtensor.sign_and_send_extrinsic( - call, - wallet, - wait_for_inclusion, - wait_for_finalization, + call=call, + wallet=wallet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, use_nonce=True, period=period, sign_with="hotkey", diff --git a/bittensor/core/extrinsics/move_stake.py b/bittensor/core/extrinsics/move_stake.py index 58e651b3e9..3e7e49eefd 100644 --- a/bittensor/core/extrinsics/move_stake.py +++ b/bittensor/core/extrinsics/move_stake.py @@ -203,7 +203,7 @@ def swap_stake_extrinsic( # Check sufficient stake stake_in_origin, stake_in_destination = _get_stake_in_origin_and_dest( - subtensor, + subtensor=subtensor, origin_hotkey_ss58=hotkey_ss58, destination_hotkey_ss58=hotkey_ss58, origin_netuid=origin_netuid, @@ -276,7 +276,7 @@ def swap_stake_extrinsic( # Get updated stakes origin_stake, dest_stake = _get_stake_in_origin_and_dest( - subtensor, + subtensor=subtensor, origin_hotkey_ss58=hotkey_ss58, destination_hotkey_ss58=hotkey_ss58, origin_netuid=origin_netuid, @@ -343,7 +343,7 @@ def move_stake_extrinsic( # Check sufficient stake stake_in_origin, stake_in_destination = _get_stake_in_origin_and_dest( - subtensor, + subtensor=subtensor, origin_hotkey_ss58=origin_hotkey, destination_hotkey_ss58=destination_hotkey, origin_netuid=origin_netuid, @@ -390,7 +390,7 @@ def move_stake_extrinsic( # Get updated stakes origin_stake, dest_stake = _get_stake_in_origin_and_dest( - subtensor, + subtensor=subtensor, origin_hotkey_ss58=origin_hotkey, destination_hotkey_ss58=destination_hotkey, origin_netuid=origin_netuid, diff --git a/bittensor/core/extrinsics/root.py b/bittensor/core/extrinsics/root.py index 3545909493..36d3a92a57 100644 --- a/bittensor/core/extrinsics/root.py +++ b/bittensor/core/extrinsics/root.py @@ -83,7 +83,7 @@ def root_register_extrinsic( block=block, ) balance = subtensor.get_balance( - wallet.coldkeypub.ss58_address, + address=wallet.coldkeypub.ss58_address, block=block, ) @@ -119,7 +119,7 @@ def root_register_extrinsic( call_params={"hotkey": wallet.hotkey.ss58_address}, ) success, err_msg = subtensor.sign_and_send_extrinsic( - call, + call=call, wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, diff --git a/bittensor/core/extrinsics/take.py b/bittensor/core/extrinsics/take.py index 20f4de2f8f..fb47bdc770 100644 --- a/bittensor/core/extrinsics/take.py +++ b/bittensor/core/extrinsics/take.py @@ -50,8 +50,8 @@ def increase_take_extrinsic( ) return subtensor.sign_and_send_extrinsic( - call, - wallet, + call=call, + wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, period=period, @@ -101,8 +101,8 @@ def decrease_take_extrinsic( ) return subtensor.sign_and_send_extrinsic( - call, - wallet, + call=call, + wallet=wallet, wait_for_inclusion=wait_for_inclusion, raise_error=raise_error, wait_for_finalization=wait_for_finalization, diff --git a/bittensor/core/extrinsics/utils.py b/bittensor/core/extrinsics/utils.py index a23126d1c8..7566aa9bc2 100644 --- a/bittensor/core/extrinsics/utils.py +++ b/bittensor/core/extrinsics/utils.py @@ -1,17 +1,12 @@ """Module with helper functions for extrinsics.""" -from typing import TYPE_CHECKING, Union - -import numpy as np -from numpy.typing import NDArray +from typing import TYPE_CHECKING from bittensor.utils.balance import Balance -from bittensor.utils.weight_utils import convert_weights_and_uids_for_emit if TYPE_CHECKING: from bittensor_wallet import Wallet from bittensor.core.chain_data import StakeInfo - from bittensor.utils.registration import torch def get_old_stakes( diff --git a/tests/unit_tests/extrinsics/asyncex/test_registration.py b/tests/unit_tests/extrinsics/asyncex/test_registration.py index 322c132045..fa1e66d67b 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_registration.py +++ b/tests/unit_tests/extrinsics/asyncex/test_registration.py @@ -16,19 +16,8 @@ async def test_do_pow_register_success(subtensor, fake_wallet, mocker): seal=b"fake_seal", ) - fake_call = mocker.AsyncMock() - fake_extrinsic = mocker.AsyncMock() - fake_response = mocker.Mock() - fake_response.is_success = mocker.AsyncMock(return_value=True)() - fake_response.process_events = mocker.AsyncMock() - - mocker.patch.object(subtensor.substrate, "compose_call", return_value=fake_call) - mocker.patch.object( - subtensor.substrate, "create_signed_extrinsic", return_value=fake_extrinsic - ) - mocker.patch.object( - subtensor.substrate, "submit_extrinsic", return_value=fake_response - ) + mocker.patch.object(subtensor.substrate, "compose_call") + mocker.patch.object(subtensor, "sign_and_send_extrinsic", new=mocker.AsyncMock(return_value=(True, ""))) # Call result, error_message = await async_registration._do_pow_register( @@ -53,12 +42,12 @@ async def test_do_pow_register_success(subtensor, fake_wallet, mocker): "coldkey": "coldkey_ss58", }, ) - subtensor.substrate.create_signed_extrinsic.assert_awaited_once_with( - call=fake_call, - keypair=fake_wallet.coldkey, - ) - subtensor.substrate.submit_extrinsic.assert_awaited_once_with( - fake_extrinsic, wait_for_inclusion=True, wait_for_finalization=True + subtensor.sign_and_send_extrinsic.assert_awaited_once_with( + call=subtensor.substrate.compose_call.return_value, + wallet=fake_wallet, + wait_for_inclusion=True, + wait_for_finalization=True, + period=None ) assert result is True assert error_message == "" @@ -75,25 +64,9 @@ async def test_do_pow_register_failure(subtensor, fake_wallet, mocker): nonce=67890, seal=b"fake_seal", ) - fake_err_message = mocker.Mock(autospec=str) - fake_call = mocker.AsyncMock() - fake_extrinsic = mocker.AsyncMock() - fake_response = mocker.Mock() - fake_response.is_success = mocker.AsyncMock(return_value=False)() - fake_response.process_events = mocker.AsyncMock() - fake_response.error_message = mocker.AsyncMock(return_value=fake_err_message)() - - mocker.patch.object(subtensor.substrate, "compose_call", return_value=fake_call) - mocker.patch.object( - subtensor.substrate, "create_signed_extrinsic", return_value=fake_extrinsic - ) - mocker.patch.object( - subtensor.substrate, "submit_extrinsic", return_value=fake_response - ) - mocked_format_error_message = mocker.patch.object( - async_subtensor, "format_error_message" - ) + mocker.patch.object(subtensor.substrate, "compose_call") + mocker.patch.object(subtensor, "sign_and_send_extrinsic") # Call result_error_message = await async_registration._do_pow_register( @@ -118,16 +91,15 @@ async def test_do_pow_register_failure(subtensor, fake_wallet, mocker): "coldkey": "coldkey_ss58", }, ) - subtensor.substrate.create_signed_extrinsic.assert_awaited_once_with( - call=fake_call, - keypair=fake_wallet.coldkey, - ) - subtensor.substrate.submit_extrinsic.assert_awaited_once_with( - fake_extrinsic, wait_for_inclusion=True, wait_for_finalization=True + subtensor.sign_and_send_extrinsic.assert_awaited_once_with( + call=subtensor.substrate.compose_call.return_value, + wallet=fake_wallet, + wait_for_inclusion=True, + wait_for_finalization=True, + period=None ) - mocked_format_error_message.assert_called_once_with(fake_err_message) - assert result_error_message == (False, mocked_format_error_message.return_value) + assert result_error_message == subtensor.sign_and_send_extrinsic.return_value @pytest.mark.asyncio @@ -142,20 +114,11 @@ async def test_do_pow_register_no_waiting(subtensor, fake_wallet, mocker): seal=b"fake_seal", ) - fake_call = mocker.AsyncMock() - fake_extrinsic = mocker.AsyncMock() - fake_response = mocker.Mock() - - mocker.patch.object(subtensor.substrate, "compose_call", return_value=fake_call) - mocker.patch.object( - subtensor.substrate, "create_signed_extrinsic", return_value=fake_extrinsic - ) - mocker.patch.object( - subtensor.substrate, "submit_extrinsic", return_value=fake_response - ) + mocker.patch.object(subtensor.substrate, "compose_call") + mocker.patch.object(subtensor, "sign_and_send_extrinsic") # Call - result, error_message = await async_registration._do_pow_register( + result = await async_registration._do_pow_register( subtensor=subtensor, netuid=1, wallet=fake_wallet, @@ -177,15 +140,15 @@ async def test_do_pow_register_no_waiting(subtensor, fake_wallet, mocker): "coldkey": "coldkey_ss58", }, ) - subtensor.substrate.create_signed_extrinsic.assert_awaited_once_with( - call=fake_call, - keypair=fake_wallet.coldkey, - ) - subtensor.substrate.submit_extrinsic.assert_awaited_once_with( - fake_extrinsic, wait_for_inclusion=False, wait_for_finalization=False + subtensor.sign_and_send_extrinsic.assert_awaited_once_with( + call=subtensor.substrate.compose_call.return_value, + wallet=fake_wallet, + wait_for_inclusion=False, + wait_for_finalization=False, + period=None ) - assert result is True - assert error_message == "Not waiting for finalization or inclusion." + + assert result == subtensor.sign_and_send_extrinsic.return_value @pytest.mark.asyncio diff --git a/tests/unit_tests/extrinsics/test_commit_weights.py b/tests/unit_tests/extrinsics/test_commit_weights.py index 71771d2247..e27547528b 100644 --- a/tests/unit_tests/extrinsics/test_commit_weights.py +++ b/tests/unit_tests/extrinsics/test_commit_weights.py @@ -13,7 +13,8 @@ def test_do_commit_weights(subtensor, fake_wallet, mocker): wait_for_inclusion = True wait_for_finalization = True - subtensor.substrate.submit_extrinsic.return_value.is_success = None + mocker.patch.object(subtensor, "sign_and_send_extrinsic", return_value=(False, "")) + mocker.patch.object(subtensor, "get_block_hash", return_value=1) mocked_format_error_message = mocker.Mock() mocker.patch( @@ -41,25 +42,18 @@ def test_do_commit_weights(subtensor, fake_wallet, mocker): }, ) - subtensor.substrate.create_signed_extrinsic.assert_called_once() - _, kwargs = subtensor.substrate.create_signed_extrinsic.call_args - assert kwargs["call"] == subtensor.substrate.compose_call.return_value - assert kwargs["keypair"] == fake_wallet.hotkey - - subtensor.substrate.submit_extrinsic.assert_called_once_with( - subtensor.substrate.create_signed_extrinsic.return_value, + subtensor.sign_and_send_extrinsic.assert_called_once_with( + call=subtensor.substrate.compose_call.return_value, + wallet=fake_wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=None, + nonce_key="hotkey", + sign_with="hotkey", + use_nonce=True, ) - mocked_format_error_message.assert_called_once_with( - subtensor.substrate.submit_extrinsic.return_value.error_message, - ) - - assert result == ( - False, - mocked_format_error_message.return_value, - ) + assert result == (False, "") def test_do_reveal_weights(subtensor, fake_wallet, mocker): @@ -74,13 +68,7 @@ def test_do_reveal_weights(subtensor, fake_wallet, mocker): wait_for_inclusion = True wait_for_finalization = True - subtensor.substrate.submit_extrinsic.return_value.is_success = None - - mocked_format_error_message = mocker.Mock() - mocker.patch( - "bittensor.core.subtensor.format_error_message", - mocked_format_error_message, - ) + mocker.patch.object(subtensor, "sign_and_send_extrinsic") # Call result = _do_reveal_weights( @@ -108,23 +96,15 @@ def test_do_reveal_weights(subtensor, fake_wallet, mocker): }, ) - subtensor.substrate.create_signed_extrinsic.assert_called_once_with( + subtensor.sign_and_send_extrinsic( call=subtensor.substrate.compose_call.return_value, - keypair=fake_wallet.hotkey, - nonce=subtensor.substrate.get_account_next_index.return_value, - ) - - subtensor.substrate.submit_extrinsic.assert_called_once_with( - subtensor.substrate.create_signed_extrinsic.return_value, + wallet=fake_wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=None, + nonce_key="hotkey", + sign_with="hotkey", + use_nonce=True, ) - mocked_format_error_message.assert_called_once_with( - subtensor.substrate.submit_extrinsic.return_value.error_message, - ) - - assert result == ( - False, - mocked_format_error_message.return_value, - ) + assert result == subtensor.sign_and_send_extrinsic.return_value diff --git a/tests/unit_tests/extrinsics/test_root.py b/tests/unit_tests/extrinsics/test_root.py index 9324613fe8..0ebaca5be7 100644 --- a/tests/unit_tests/extrinsics/test_root.py +++ b/tests/unit_tests/extrinsics/test_root.py @@ -99,7 +99,7 @@ def test_root_register_extrinsic( call_params={"hotkey": "fake_hotkey_address"}, ) mocked_sign_and_send_extrinsic.assert_called_once_with( - mock_subtensor.substrate.compose_call.return_value, + call=mock_subtensor.substrate.compose_call.return_value, wallet=mock_wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, @@ -126,7 +126,7 @@ def test_root_register_extrinsic_insufficient_balance( assert success is False mock_subtensor.get_balance.assert_called_once_with( - mock_wallet.coldkeypub.ss58_address, + address=mock_wallet.coldkeypub.ss58_address, block=mock_subtensor.get_current_block.return_value, ) mock_subtensor.substrate.submit_extrinsic.assert_not_called() diff --git a/tests/unit_tests/extrinsics/test_transfer.py b/tests/unit_tests/extrinsics/test_transfer.py index 097156ba49..06567e2737 100644 --- a/tests/unit_tests/extrinsics/test_transfer.py +++ b/tests/unit_tests/extrinsics/test_transfer.py @@ -10,7 +10,8 @@ def test_do_transfer_is_success_true(subtensor, fake_wallet, mocker): fake_wait_for_inclusion = True fake_wait_for_finalization = True - subtensor.substrate.submit_extrinsic.return_value.is_success = True + mocker.patch.object(subtensor, "sign_and_send_extrinsic", return_value=(True, "")) + mocker.patch.object(subtensor, "get_block_hash", return_value=1) # Call result = _do_transfer( @@ -28,20 +29,15 @@ def test_do_transfer_is_success_true(subtensor, fake_wallet, mocker): call_function="transfer_allow_death", call_params={"dest": fake_dest, "value": fake_transfer_balance.rao}, ) - subtensor.substrate.create_signed_extrinsic.assert_called_once_with( - call=subtensor.substrate.compose_call.return_value, keypair=fake_wallet.coldkey - ) - subtensor.substrate.submit_extrinsic.assert_called_once_with( - extrinsic=subtensor.substrate.create_signed_extrinsic.return_value, + subtensor.sign_and_send_extrinsic.assert_called_once_with( + call=subtensor.substrate.compose_call.return_value, + wallet=fake_wallet, wait_for_inclusion=fake_wait_for_inclusion, wait_for_finalization=fake_wait_for_finalization, + period=None ) # subtensor.substrate.submit_extrinsic.return_value.process_events.assert_called_once() - assert result == ( - True, - subtensor.substrate.get_chain_head.return_value, - "Success with response.", - ) + assert result == (True, 1, "Success with response.") def test_do_transfer_is_success_false(subtensor, fake_wallet, mocker): @@ -52,7 +48,8 @@ def test_do_transfer_is_success_false(subtensor, fake_wallet, mocker): fake_wait_for_inclusion = True fake_wait_for_finalization = True - subtensor.substrate.submit_extrinsic.return_value.is_success = False + mocker.patch.object(subtensor, "sign_and_send_extrinsic", return_value=(False, "")) + mocker.patch.object(subtensor, "get_block_hash", return_value=1) # Call result = _do_transfer( @@ -70,20 +67,15 @@ def test_do_transfer_is_success_false(subtensor, fake_wallet, mocker): call_function="transfer_allow_death", call_params={"dest": fake_dest, "value": fake_transfer_balance.rao}, ) - subtensor.substrate.create_signed_extrinsic.assert_called_once_with( - call=subtensor.substrate.compose_call.return_value, keypair=fake_wallet.coldkey - ) - subtensor.substrate.submit_extrinsic.assert_called_once_with( - extrinsic=subtensor.substrate.create_signed_extrinsic.return_value, + subtensor.sign_and_send_extrinsic.assert_called_once_with( + call=subtensor.substrate.compose_call.return_value, + wallet=fake_wallet, wait_for_inclusion=fake_wait_for_inclusion, wait_for_finalization=fake_wait_for_finalization, + period=None ) - assert result == ( - False, - "", - "Subtensor returned `UnknownError(UnknownType)` error. This means: `Unknown Description`.", - ) + assert result == (False, "", "") def test_do_transfer_no_waits(subtensor, fake_wallet, mocker): @@ -94,6 +86,8 @@ def test_do_transfer_no_waits(subtensor, fake_wallet, mocker): fake_wait_for_inclusion = False fake_wait_for_finalization = False + mocker.patch.object(subtensor, "sign_and_send_extrinsic", return_value=(True, "msg")) + # Call result = _do_transfer( subtensor, @@ -110,12 +104,11 @@ def test_do_transfer_no_waits(subtensor, fake_wallet, mocker): call_function="transfer_allow_death", call_params={"dest": fake_dest, "value": fake_transfer_balance.rao}, ) - subtensor.substrate.create_signed_extrinsic.assert_called_once_with( - call=subtensor.substrate.compose_call.return_value, keypair=fake_wallet.coldkey - ) - subtensor.substrate.submit_extrinsic.assert_called_once_with( - extrinsic=subtensor.substrate.create_signed_extrinsic.return_value, + subtensor.sign_and_send_extrinsic.assert_called_once_with( + call=subtensor.substrate.compose_call.return_value, + wallet=fake_wallet, wait_for_inclusion=fake_wait_for_inclusion, wait_for_finalization=fake_wait_for_finalization, + period=None ) - assert result == (True, "", "Not waiting for finalization or inclusion.") + assert result == (True, "", "msg") From e915a3f231b28894ae78adb2dfdadbe11b5959f3 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 2 May 2025 00:45:11 -0700 Subject: [PATCH 139/251] oops with `set_children` -> fixed --- bittensor/core/async_subtensor.py | 99 +++++++++++++++++-------------- bittensor/core/subtensor.py | 9 ++- 2 files changed, 61 insertions(+), 47 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index c93007aa50..f9cee8c153 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -3677,6 +3677,7 @@ async def reveal_weights( wait_for_inclusion: bool = False, wait_for_finalization: bool = False, max_retries: int = 5, + period: Optional[int] = None, ) -> tuple[bool, str]: """ Reveals the weights for a specific subnet on the Bittensor blockchain using the provided wallet. @@ -3689,11 +3690,14 @@ async def reveal_weights( weights (np.ndarray): NumPy array of weight values corresponding to each UID. salt (np.ndarray): NumPy array of salt values corresponding to the hash function. version_key (int): Version key for compatibility with the network. Default is ``int representation of - Bittensor version``. + a Bittensor version``. wait_for_inclusion (bool): Waits for the transaction to be included in a block. Default is ``False``. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. Default is ``False``. max_retries (int): The number of maximum attempts to reveal weights. Default is ``5``. + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: tuple[bool, str]: ``True`` if the weight revelation is successful, False otherwise. And `msg`, a string @@ -3718,12 +3722,12 @@ async def reveal_weights( version_key=version_key, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + period=period, ) if success: break except Exception as e: logging.error(f"Error revealing weights: {e}") - finally: retries += 1 return success, message @@ -3812,9 +3816,10 @@ async def set_children( wait_for_inclusion: bool = True, wait_for_finalization: bool = True, raise_error: bool = False, + period: Optional[int] = None, ) -> tuple[bool, str]: """ - Allows a coldkey to set children keys. + Allows a coldkey to set children-keys. Arguments: wallet (bittensor_wallet.Wallet): bittensor wallet instance. @@ -3823,7 +3828,10 @@ async def set_children( children (list[tuple[float, str]]): A list of children with their proportions. wait_for_inclusion (bool): Waits for the transaction to be included in a block. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. - raise_error: Raises relevant exception rather than returning `False` if unsuccessful. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: tuple[bool, str]: A tuple where the first element is a boolean indicating success or failure of the @@ -3870,6 +3878,7 @@ async def set_children( wait_for_inclusion, wait_for_finalization, raise_error=raise_error, + period=period, ) async def set_delegate_take( @@ -4257,90 +4266,90 @@ async def swap_stake( period=period ) - async def transfer_stake( + async def transfer( self, wallet: "Wallet", - destination_coldkey_ss58: str, - hotkey_ss58: str, - origin_netuid: int, - destination_netuid: int, + dest: str, amount: Balance, + transfer_all: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, + keep_alive: bool = True, period: Optional[int] = None, ) -> bool: """ - Transfers stake from one subnet to another while changing the coldkey owner. + Transfer token of amount to destination. - Args: - wallet (bittensor.wallet): The wallet to transfer stake from. - destination_coldkey_ss58 (str): The destination coldkey SS58 address. - hotkey_ss58 (str): The hotkey SS58 address associated with the stake. - origin_netuid (int): The source subnet UID. - destination_netuid (int): The destination subnet UID. - amount (Balance): Amount to transfer. - wait_for_inclusion (bool): If true, waits for inclusion before returning. - wait_for_finalization (bool): If true, waits for finalization before returning. + Arguments: + wallet (bittensor_wallet.Wallet): Source wallet for the transfer. + dest (str): Destination address for the transfer. + amount (float): Number of tokens to transfer. + transfer_all (bool): Flag to transfer all tokens. Default is ``False``. + wait_for_inclusion (bool): Waits for the transaction to be included in a block. Default is ``True``. + wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. Default is + ``False``. + keep_alive (bool): Flag to keep the connection alive. Default is ``True``. period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. - Returns: - success (bool): True if the transfer was successful. + `True` if the transferring was successful, otherwise `False`. """ amount = check_and_convert_to_balance(amount) - return await transfer_stake_extrinsic( + return await transfer_extrinsic( subtensor=self, wallet=wallet, - destination_coldkey_ss58=destination_coldkey_ss58, - hotkey_ss58=hotkey_ss58, - origin_netuid=origin_netuid, - destination_netuid=destination_netuid, + dest=dest, amount=amount, + transfer_all=transfer_all, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, + keep_alive=keep_alive, period=period, ) - async def transfer( + async def transfer_stake( self, wallet: "Wallet", - dest: str, + destination_coldkey_ss58: str, + hotkey_ss58: str, + origin_netuid: int, + destination_netuid: int, amount: Balance, - transfer_all: bool = False, wait_for_inclusion: bool = True, wait_for_finalization: bool = False, - keep_alive: bool = True, period: Optional[int] = None, ) -> bool: """ - Transfer token of amount to destination. + Transfers stake from one subnet to another while changing the coldkey owner. - Arguments: - wallet (bittensor_wallet.Wallet): Source wallet for the transfer. - dest (str): Destination address for the transfer. - amount (float): Number of tokens to transfer. - transfer_all (bool): Flag to transfer all tokens. Default is ``False``. - wait_for_inclusion (bool): Waits for the transaction to be included in a block. Default is ``True``. - wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. Default is - ``False``. - keep_alive (bool): Flag to keep the connection alive. Default is ``True``. + Args: + wallet (bittensor.wallet): The wallet to transfer stake from. + destination_coldkey_ss58 (str): The destination coldkey SS58 address. + hotkey_ss58 (str): The hotkey SS58 address associated with the stake. + origin_netuid (int): The source subnet UID. + destination_netuid (int): The destination subnet UID. + amount (Balance): Amount to transfer. + wait_for_inclusion (bool): If true, waits for inclusion before returning. + wait_for_finalization (bool): If true, waits for finalization before returning. period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. + Returns: - `True` if the transferring was successful, otherwise `False`. + success (bool): True if the transfer was successful. """ amount = check_and_convert_to_balance(amount) - return await transfer_extrinsic( + return await transfer_stake_extrinsic( subtensor=self, wallet=wallet, - dest=dest, + destination_coldkey_ss58=destination_coldkey_ss58, + hotkey_ss58=hotkey_ss58, + origin_netuid=origin_netuid, + destination_netuid=destination_netuid, amount=amount, - transfer_all=transfer_all, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - keep_alive=keep_alive, period=period, ) diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index c6cbd34373..442c21841b 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -3101,9 +3101,10 @@ def set_children( wait_for_inclusion: bool = True, wait_for_finalization: bool = True, raise_error: bool = False, + period: Optional[int] = None, ) -> tuple[bool, str]: """ - Allows a coldkey to set children keys. + Allows a coldkey to set children-keys. Arguments: wallet (bittensor_wallet.Wallet): bittensor wallet instance. @@ -3112,7 +3113,10 @@ def set_children( children (list[tuple[float, str]]): A list of children with their proportions. wait_for_inclusion (bool): Waits for the transaction to be included in a block. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. - raise_error: Raises relevant exception rather than returning `False` if unsuccessful. + raise_error: Raises a relevant exception rather than returning `False` if unsuccessful. + period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If + the transaction is not included in a block within that number of blocks, it will expire and be rejected. + You can think of it as an expiration date for the transaction. Returns: tuple[bool, str]: A tuple where the first element is a boolean indicating success or failure of the @@ -3159,6 +3163,7 @@ def set_children( wait_for_inclusion, wait_for_finalization, raise_error=raise_error, + period=period, ) def set_delegate_take( From 7fef30ff338bf55cdb77eb76ca8725dfcbab6952 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 2 May 2025 00:46:28 -0700 Subject: [PATCH 140/251] ruff + mypy + flake8 --- bittensor/core/async_subtensor.py | 16 ++++++++------- .../core/extrinsics/asyncex/commit_reveal.py | 2 +- .../core/extrinsics/asyncex/registration.py | 2 +- bittensor/core/extrinsics/asyncex/root.py | 2 +- bittensor/core/extrinsics/asyncex/serving.py | 2 +- bittensor/core/extrinsics/asyncex/staking.py | 8 ++++++-- .../core/extrinsics/asyncex/start_call.py | 4 ++-- .../core/extrinsics/asyncex/unstaking.py | 8 ++++++-- bittensor/core/extrinsics/commit_reveal.py | 2 +- bittensor/core/extrinsics/root.py | 2 +- bittensor/core/extrinsics/set_weights.py | 5 ++++- bittensor/core/extrinsics/staking.py | 8 ++++++-- bittensor/core/extrinsics/start_call.py | 4 ++-- bittensor/core/extrinsics/take.py | 4 ++-- bittensor/core/extrinsics/unstaking.py | 8 ++++++-- bittensor/core/subtensor.py | 8 +++++--- tests/e2e_tests/test_staking.py | 2 +- .../extrinsics/asyncex/test_commit_reveal.py | 4 ++-- .../extrinsics/asyncex/test_registration.py | 12 +++++++---- .../extrinsics/asyncex/test_root.py | 8 ++++++-- .../extrinsics/asyncex/test_start_call.py | 2 +- .../extrinsics/asyncex/test_transfer.py | 14 ++++++++++--- .../extrinsics/asyncex/test_weights.py | 8 ++++++-- tests/unit_tests/extrinsics/test_root.py | 2 +- tests/unit_tests/extrinsics/test_serving.py | 4 +++- .../unit_tests/extrinsics/test_set_weights.py | 14 +++++++++---- tests/unit_tests/extrinsics/test_staking.py | 4 ++-- .../unit_tests/extrinsics/test_start_call.py | 2 +- tests/unit_tests/extrinsics/test_transfer.py | 10 ++++++---- tests/unit_tests/test_subtensor.py | 20 +++++++++++-------- 30 files changed, 124 insertions(+), 67 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index f9cee8c153..4645a1ffda 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -653,7 +653,9 @@ async def bonds( return b_map - async def commit(self, wallet: "Wallet", netuid: int, data: str, period: Optional[int] = None) -> bool: + async def commit( + self, wallet: "Wallet", netuid: int, data: str, period: Optional[int] = None + ) -> bool: """ Commits arbitrary data to the Bittensor network by publishing metadata. @@ -2856,7 +2858,7 @@ async def set_reveal_commitment( data: str, blocks_until_reveal: int = 360, block_time: Union[int, float] = 12, - period: Optional[int] = None + period: Optional[int] = None, ) -> tuple[bool, int]: """ Commits arbitrary data to the Bittensor network by publishing metadata. @@ -2891,7 +2893,7 @@ async def set_reveal_commitment( netuid=netuid, data_type=f"TimelockEncrypted", data=data_, - period=period + period=period, ), reveal_round async def subnet( @@ -3442,7 +3444,7 @@ async def burned_register( netuid=netuid, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - period=period + period=period, ) async def commit_weights( @@ -3944,7 +3946,7 @@ async def set_delegate_take( wait_for_finalization=wait_for_finalization, wait_for_inclusion=wait_for_inclusion, raise_error=raise_error, - period=period + period=period, ) else: success, error = await decrease_take_extrinsic( @@ -3955,7 +3957,7 @@ async def set_delegate_take( wait_for_finalization=wait_for_finalization, wait_for_inclusion=wait_for_inclusion, raise_error=raise_error, - period=period + period=period, ) if success: @@ -4263,7 +4265,7 @@ async def swap_stake( safe_staking=safe_staking, allow_partial_stake=allow_partial_stake, rate_tolerance=rate_tolerance, - period=period + period=period, ) async def transfer( diff --git a/bittensor/core/extrinsics/asyncex/commit_reveal.py b/bittensor/core/extrinsics/asyncex/commit_reveal.py index 6c4ea6f138..9c65ae8ea1 100644 --- a/bittensor/core/extrinsics/asyncex/commit_reveal.py +++ b/bittensor/core/extrinsics/asyncex/commit_reveal.py @@ -65,7 +65,7 @@ async def _do_commit_reveal_v3( wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, sign_with="hotkey", - period=period + period=period, ) diff --git a/bittensor/core/extrinsics/asyncex/registration.py b/bittensor/core/extrinsics/asyncex/registration.py index e3663f8f64..8758169869 100644 --- a/bittensor/core/extrinsics/asyncex/registration.py +++ b/bittensor/core/extrinsics/asyncex/registration.py @@ -10,7 +10,7 @@ import asyncio from typing import Optional, Union, TYPE_CHECKING -from bittensor.utils import unlock_key, format_error_message +from bittensor.utils import unlock_key from bittensor.utils.btlogging import logging from bittensor.utils.registration import log_no_torch_error, create_pow_async, torch diff --git a/bittensor/core/extrinsics/asyncex/root.py b/bittensor/core/extrinsics/asyncex/root.py index 4641676e36..79c5417e4f 100644 --- a/bittensor/core/extrinsics/asyncex/root.py +++ b/bittensor/core/extrinsics/asyncex/root.py @@ -11,7 +11,7 @@ from bittensor.utils.weight_utils import ( normalize_max_weight, convert_weights_and_uids_for_emit, - convert_uids_and_weights + convert_uids_and_weights, ) if TYPE_CHECKING: diff --git a/bittensor/core/extrinsics/asyncex/serving.py b/bittensor/core/extrinsics/asyncex/serving.py index c86a681b08..c37c612191 100644 --- a/bittensor/core/extrinsics/asyncex/serving.py +++ b/bittensor/core/extrinsics/asyncex/serving.py @@ -221,7 +221,7 @@ async def serve_axon_extrinsic( wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, certificate=certificate, - period=period + period=period, ) return serve_success diff --git a/bittensor/core/extrinsics/asyncex/staking.py b/bittensor/core/extrinsics/asyncex/staking.py index c148fa86f9..41076f0178 100644 --- a/bittensor/core/extrinsics/asyncex/staking.py +++ b/bittensor/core/extrinsics/asyncex/staking.py @@ -205,7 +205,9 @@ async def add_stake_extrinsic( return False except SubstrateRequestException as error: - logging.error(f":cross_mark: [red]Add Stake Error: {format_error_message(error)}[/red]") + logging.error( + f":cross_mark: [red]Add Stake Error: {format_error_message(error)}[/red]" + ) return False @@ -402,7 +404,9 @@ async def add_stake_multiple_extrinsic( continue except SubstrateRequestException as error: - logging.error(f":cross_mark: [red]Add Stake Multiple error: {format_error_message(error)}[/red]") + logging.error( + f":cross_mark: [red]Add Stake Multiple error: {format_error_message(error)}[/red]" + ) continue if successful_stakes != 0: diff --git a/bittensor/core/extrinsics/asyncex/start_call.py b/bittensor/core/extrinsics/asyncex/start_call.py index 3790995524..63f6fbc3c1 100644 --- a/bittensor/core/extrinsics/asyncex/start_call.py +++ b/bittensor/core/extrinsics/asyncex/start_call.py @@ -1,6 +1,6 @@ from typing import TYPE_CHECKING, Optional -from bittensor.utils import unlock_key, format_error_message +from bittensor.utils import unlock_key from bittensor.utils.btlogging import logging if TYPE_CHECKING: @@ -51,7 +51,7 @@ async def start_call_extrinsic( wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - period=period + period=period, ) if not wait_for_finalization and not wait_for_inclusion: diff --git a/bittensor/core/extrinsics/asyncex/unstaking.py b/bittensor/core/extrinsics/asyncex/unstaking.py index b114dc7e23..8125b603e9 100644 --- a/bittensor/core/extrinsics/asyncex/unstaking.py +++ b/bittensor/core/extrinsics/asyncex/unstaking.py @@ -189,7 +189,9 @@ async def unstake_extrinsic( return False except SubstrateRequestException as error: - logging.error(f":cross_mark: [red]Unstake filed with error: {format_error_message(error)}[/red]") + logging.error( + f":cross_mark: [red]Unstake filed with error: {format_error_message(error)}[/red]" + ) return False @@ -354,7 +356,9 @@ async def unstake_multiple_extrinsic( continue except SubstrateRequestException as error: - logging.error(f":cross_mark: [red]Multiple unstake filed with error: {format_error_message(error)}[/red]") + logging.error( + f":cross_mark: [red]Multiple unstake filed with error: {format_error_message(error)}[/red]" + ) return False if successful_unstakes != 0: diff --git a/bittensor/core/extrinsics/commit_reveal.py b/bittensor/core/extrinsics/commit_reveal.py index 28f6c112fe..c531986254 100644 --- a/bittensor/core/extrinsics/commit_reveal.py +++ b/bittensor/core/extrinsics/commit_reveal.py @@ -66,7 +66,7 @@ def _do_commit_reveal_v3( wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, sign_with="hotkey", - period=period + period=period, ) diff --git a/bittensor/core/extrinsics/root.py b/bittensor/core/extrinsics/root.py index 36d3a92a57..4a6dfbc0ee 100644 --- a/bittensor/core/extrinsics/root.py +++ b/bittensor/core/extrinsics/root.py @@ -16,7 +16,7 @@ from bittensor.utils.weight_utils import ( normalize_max_weight, convert_weights_and_uids_for_emit, - convert_uids_and_weights + convert_uids_and_weights, ) if TYPE_CHECKING: diff --git a/bittensor/core/extrinsics/set_weights.py b/bittensor/core/extrinsics/set_weights.py index bfa912419c..039dbdf837 100644 --- a/bittensor/core/extrinsics/set_weights.py +++ b/bittensor/core/extrinsics/set_weights.py @@ -7,7 +7,10 @@ from bittensor.core.settings import version_as_int from bittensor.utils.btlogging import logging -from bittensor.utils.weight_utils import convert_and_normalize_weights_and_uids, convert_uids_and_weights +from bittensor.utils.weight_utils import ( + convert_and_normalize_weights_and_uids, + convert_uids_and_weights, +) if TYPE_CHECKING: from bittensor.core.subtensor import Subtensor diff --git a/bittensor/core/extrinsics/staking.py b/bittensor/core/extrinsics/staking.py index 86014c5b07..173fee2db1 100644 --- a/bittensor/core/extrinsics/staking.py +++ b/bittensor/core/extrinsics/staking.py @@ -199,7 +199,9 @@ def add_stake_extrinsic( return False except SubstrateRequestException as error: - logging.error(f":cross_mark: [red]Add Stake Error: {format_error_message((error))}[/red]") + logging.error( + f":cross_mark: [red]Add Stake Error: {format_error_message((error))}[/red]" + ) return False @@ -385,7 +387,9 @@ def add_stake_multiple_extrinsic( continue except SubstrateRequestException as error: - logging.error(f":cross_mark: [red]Add Stake Multiple error: {format_error_message(error)}[/red]") + logging.error( + f":cross_mark: [red]Add Stake Multiple error: {format_error_message(error)}[/red]" + ) continue if successful_stakes != 0: diff --git a/bittensor/core/extrinsics/start_call.py b/bittensor/core/extrinsics/start_call.py index ac6229e802..2788bb88f1 100644 --- a/bittensor/core/extrinsics/start_call.py +++ b/bittensor/core/extrinsics/start_call.py @@ -1,6 +1,6 @@ from typing import TYPE_CHECKING, Optional -from bittensor.utils import unlock_key, format_error_message +from bittensor.utils import unlock_key from bittensor.utils.btlogging import logging if TYPE_CHECKING: @@ -50,7 +50,7 @@ def start_call_extrinsic( wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - period=period + period=period, ) if not wait_for_finalization and not wait_for_inclusion: diff --git a/bittensor/core/extrinsics/take.py b/bittensor/core/extrinsics/take.py index fb47bdc770..d07e628aab 100644 --- a/bittensor/core/extrinsics/take.py +++ b/bittensor/core/extrinsics/take.py @@ -54,7 +54,7 @@ def increase_take_extrinsic( wallet=wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - period=period, + period=period, raise_error=raise_error, ) @@ -70,7 +70,7 @@ def decrease_take_extrinsic( period: Optional[int] = None, ) -> tuple[bool, str]: """Sets the delegate 'take' percentage for a neuron identified by its hotkey. - + Args: subtensor (Subtensor): Blockchain connection. wallet (Wallet): The wallet to sign the extrinsic. diff --git a/bittensor/core/extrinsics/unstaking.py b/bittensor/core/extrinsics/unstaking.py index f4bb46f425..34fe47d7ac 100644 --- a/bittensor/core/extrinsics/unstaking.py +++ b/bittensor/core/extrinsics/unstaking.py @@ -186,7 +186,9 @@ def unstake_extrinsic( return False except SubstrateRequestException as error: - logging.error(f":cross_mark: [red]Unstake filed with error: {format_error_message(error)}[/red]") + logging.error( + f":cross_mark: [red]Unstake filed with error: {format_error_message(error)}[/red]" + ) return False @@ -342,7 +344,9 @@ def unstake_multiple_extrinsic( continue except SubstrateRequestException as error: - logging.error(f":cross_mark: [red]Multiple unstake filed with error: {format_error_message(error)}[/red]") + logging.error( + f":cross_mark: [red]Multiple unstake filed with error: {format_error_message(error)}[/red]" + ) return False if successful_unstakes != 0: diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 442c21841b..7f7a9eaa81 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -452,7 +452,9 @@ def bonds( return b_map - def commit(self, wallet, netuid: int, data: str, period: Optional[int] = None) -> bool: + def commit( + self, wallet, netuid: int, data: str, period: Optional[int] = None + ) -> bool: """ Commits arbitrary data to the Bittensor network by publishing metadata. @@ -3229,7 +3231,7 @@ def set_delegate_take( wait_for_finalization=wait_for_finalization, wait_for_inclusion=wait_for_inclusion, raise_error=raise_error, - period=period + period=period, ) else: success, error = decrease_take_extrinsic( @@ -3240,7 +3242,7 @@ def set_delegate_take( wait_for_finalization=wait_for_finalization, wait_for_inclusion=wait_for_inclusion, raise_error=raise_error, - period=period + period=period, ) if success: diff --git a/tests/e2e_tests/test_staking.py b/tests/e2e_tests/test_staking.py index 3307956095..477c61054e 100644 --- a/tests/e2e_tests/test_staking.py +++ b/tests/e2e_tests/test_staking.py @@ -55,7 +55,7 @@ def test_single_operation(subtensor, alice_wallet, bob_wallet): amount=Balance.from_tao(1), wait_for_inclusion=True, wait_for_finalization=True, - period=16 + period=16, ) assert success is True diff --git a/tests/unit_tests/extrinsics/asyncex/test_commit_reveal.py b/tests/unit_tests/extrinsics/asyncex/test_commit_reveal.py index a81f540f2b..66873eba00 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_commit_reveal.py +++ b/tests/unit_tests/extrinsics/asyncex/test_commit_reveal.py @@ -223,7 +223,7 @@ async def test_commit_reveal_v3_extrinsic_success_with_torch( reveal_round=fake_reveal_round, wait_for_inclusion=True, wait_for_finalization=True, - period=None + period=None, ) @@ -329,7 +329,7 @@ async def test_commit_reveal_v3_extrinsic_response_false( reveal_round=fake_reveal_round, wait_for_inclusion=True, wait_for_finalization=True, - period=None + period=None, ) diff --git a/tests/unit_tests/extrinsics/asyncex/test_registration.py b/tests/unit_tests/extrinsics/asyncex/test_registration.py index fa1e66d67b..a9b3ac511f 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_registration.py +++ b/tests/unit_tests/extrinsics/asyncex/test_registration.py @@ -17,7 +17,11 @@ async def test_do_pow_register_success(subtensor, fake_wallet, mocker): ) mocker.patch.object(subtensor.substrate, "compose_call") - mocker.patch.object(subtensor, "sign_and_send_extrinsic", new=mocker.AsyncMock(return_value=(True, ""))) + mocker.patch.object( + subtensor, + "sign_and_send_extrinsic", + new=mocker.AsyncMock(return_value=(True, "")), + ) # Call result, error_message = await async_registration._do_pow_register( @@ -47,7 +51,7 @@ async def test_do_pow_register_success(subtensor, fake_wallet, mocker): wallet=fake_wallet, wait_for_inclusion=True, wait_for_finalization=True, - period=None + period=None, ) assert result is True assert error_message == "" @@ -96,7 +100,7 @@ async def test_do_pow_register_failure(subtensor, fake_wallet, mocker): wallet=fake_wallet, wait_for_inclusion=True, wait_for_finalization=True, - period=None + period=None, ) assert result_error_message == subtensor.sign_and_send_extrinsic.return_value @@ -145,7 +149,7 @@ async def test_do_pow_register_no_waiting(subtensor, fake_wallet, mocker): wallet=fake_wallet, wait_for_inclusion=False, wait_for_finalization=False, - period=None + period=None, ) assert result == subtensor.sign_and_send_extrinsic.return_value diff --git a/tests/unit_tests/extrinsics/asyncex/test_root.py b/tests/unit_tests/extrinsics/asyncex/test_root.py index ca221b2cd4..baa134ee92 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_root.py +++ b/tests/unit_tests/extrinsics/asyncex/test_root.py @@ -389,7 +389,9 @@ async def test_do_set_root_weights_failure(subtensor, fake_wallet, mocker): fake_call = mocker.AsyncMock() mocker.patch.object(subtensor.substrate, "compose_call", return_value=fake_call) - mocker.patch.object(subtensor, "sign_and_send_extrinsic", return_value=(False, "Transaction failed")) + mocker.patch.object( + subtensor, "sign_and_send_extrinsic", return_value=(False, "Transaction failed") + ) # Call result, message = await async_root._do_set_root_weights( @@ -419,7 +421,9 @@ async def test_do_set_root_weights_no_waiting(subtensor, fake_wallet, mocker): mocker.patch.object(subtensor.substrate, "compose_call", return_value=fake_call) mocker.patch.object( - subtensor, "sign_and_send_extrinsic", return_value=(True, "Not waiting for finalization or inclusion.") + subtensor, + "sign_and_send_extrinsic", + return_value=(True, "Not waiting for finalization or inclusion."), ) # Call diff --git a/tests/unit_tests/extrinsics/asyncex/test_start_call.py b/tests/unit_tests/extrinsics/asyncex/test_start_call.py index ebb5c327a9..d99295195e 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_start_call.py +++ b/tests/unit_tests/extrinsics/asyncex/test_start_call.py @@ -35,7 +35,7 @@ async def test_start_call_extrinsics(subtensor, mocker, fake_wallet): wallet=wallet, wait_for_inclusion=True, wait_for_finalization=False, - period=None + period=None, ) assert success is True diff --git a/tests/unit_tests/extrinsics/asyncex/test_transfer.py b/tests/unit_tests/extrinsics/asyncex/test_transfer.py index 1abd2fe15c..77fcd72f9f 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_transfer.py +++ b/tests/unit_tests/extrinsics/asyncex/test_transfer.py @@ -13,7 +13,9 @@ async def test_do_transfer_success(subtensor, fake_wallet, mocker): mocker.patch.object(subtensor.substrate, "compose_call") mocker.patch.object( - subtensor, "sign_and_send_extrinsic", new=mocker.AsyncMock(return_value=(True, "")) + subtensor, + "sign_and_send_extrinsic", + new=mocker.AsyncMock(return_value=(True, "")), ) mocker.patch.object(subtensor, "get_block_hash", return_value=fake_block_hash) @@ -56,7 +58,9 @@ async def test_do_transfer_failure(subtensor, fake_wallet, mocker): mocker.patch.object(subtensor.substrate, "compose_call") mocker.patch.object( - subtensor, "sign_and_send_extrinsic", new=mocker.AsyncMock(return_value=(False, "Formatted error message")) + subtensor, + "sign_and_send_extrinsic", + new=mocker.AsyncMock(return_value=(False, "Formatted error message")), ) mocker.patch.object(subtensor, "get_block_hash", return_value=fake_block_hash) @@ -99,7 +103,11 @@ async def test_do_transfer_no_waiting(subtensor, fake_wallet, mocker): mocker.patch.object(subtensor.substrate, "compose_call") mocker.patch.object( - subtensor, "sign_and_send_extrinsic", new=mocker.AsyncMock(return_value=(False, "Success, extrinsic submitted without waiting.")) + subtensor, + "sign_and_send_extrinsic", + new=mocker.AsyncMock( + return_value=(False, "Success, extrinsic submitted without waiting.") + ), ) mocker.patch.object(subtensor, "get_block_hash", return_value=fake_block_hash) diff --git a/tests/unit_tests/extrinsics/asyncex/test_weights.py b/tests/unit_tests/extrinsics/asyncex/test_weights.py index ef6900f62f..226292b43c 100644 --- a/tests/unit_tests/extrinsics/asyncex/test_weights.py +++ b/tests/unit_tests/extrinsics/asyncex/test_weights.py @@ -147,7 +147,9 @@ async def test_set_weights_extrinsic_success_with_finalization( mocked_do_set_weights = mocker.patch.object( async_weights, "_do_set_weights", return_value=(True, "") ) - mocker_converter = mocker.patch.object(async_weights, "convert_and_normalize_weights_and_uids") + mocker_converter = mocker.patch.object( + async_weights, "convert_and_normalize_weights_and_uids" + ) mocker_converter.return_value = (mocker.Mock(), mocker.Mock()) # Call @@ -188,7 +190,9 @@ async def test_set_weights_extrinsic_no_waiting(subtensor, fake_wallet, mocker): fake_weights = [0.1, 0.2, 0.7] mocked_do_set_weights = mocker.patch.object( - async_weights, "_do_set_weights", return_value=(True, "Not waiting for finalization or inclusion.") + async_weights, + "_do_set_weights", + return_value=(True, "Not waiting for finalization or inclusion."), ) # Call diff --git a/tests/unit_tests/extrinsics/test_root.py b/tests/unit_tests/extrinsics/test_root.py index 0ebaca5be7..8737b3f32c 100644 --- a/tests/unit_tests/extrinsics/test_root.py +++ b/tests/unit_tests/extrinsics/test_root.py @@ -103,7 +103,7 @@ def test_root_register_extrinsic( wallet=mock_wallet, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - period=None + period=None, ) diff --git a/tests/unit_tests/extrinsics/test_serving.py b/tests/unit_tests/extrinsics/test_serving.py index 027199ca81..c1688fd3d4 100644 --- a/tests/unit_tests/extrinsics/test_serving.py +++ b/tests/unit_tests/extrinsics/test_serving.py @@ -353,7 +353,9 @@ def test_publish_metadata( # Arrange with ( patch.object(mock_subtensor.substrate, "compose_call"), - patch.object(mock_subtensor, "sign_and_send_extrinsic", return_value=response_success), + patch.object( + mock_subtensor, "sign_and_send_extrinsic", return_value=response_success + ), ): # Act result = serving.publish_metadata( diff --git a/tests/unit_tests/extrinsics/test_set_weights.py b/tests/unit_tests/extrinsics/test_set_weights.py index 645395ab8a..1046385291 100644 --- a/tests/unit_tests/extrinsics/test_set_weights.py +++ b/tests/unit_tests/extrinsics/test_set_weights.py @@ -105,7 +105,9 @@ def test_do_set_weights_is_success(mock_subtensor, fake_wallet, mocker): fake_wait_for_finalization = True mock_subtensor.substrate.submit_extrinsic.return_value.is_success = True - mocker.patch.object(mock_subtensor, "sign_and_send_extrinsic", return_value=(True, "")) + mocker.patch.object( + mock_subtensor, "sign_and_send_extrinsic", return_value=(True, "") + ) # Call result = _do_set_weights( @@ -139,7 +141,7 @@ def test_do_set_weights_is_success(mock_subtensor, fake_wallet, mocker): nonce_key="hotkey", sign_with="hotkey", use_nonce=True, - period=None + period=None, ) assert result == (True, "") @@ -153,7 +155,11 @@ def test_do_set_weights_no_waits(mock_subtensor, fake_wallet, mocker): fake_wait_for_inclusion = False fake_wait_for_finalization = False - mocker.patch.object(mock_subtensor, "sign_and_send_extrinsic", return_value=(True, "Not waiting for finalization or inclusion.")) + mocker.patch.object( + mock_subtensor, + "sign_and_send_extrinsic", + return_value=(True, "Not waiting for finalization or inclusion."), + ) # Call result = _do_set_weights( @@ -184,6 +190,6 @@ def test_do_set_weights_no_waits(mock_subtensor, fake_wallet, mocker): wallet=fake_wallet, wait_for_inclusion=fake_wait_for_inclusion, wait_for_finalization=fake_wait_for_finalization, - period=None + period=None, ) assert result == (True, "Not waiting for finalization or inclusion.") diff --git a/tests/unit_tests/extrinsics/test_staking.py b/tests/unit_tests/extrinsics/test_staking.py index 517280d461..80ecb5c240 100644 --- a/tests/unit_tests/extrinsics/test_staking.py +++ b/tests/unit_tests/extrinsics/test_staking.py @@ -51,7 +51,7 @@ def test_add_stake_extrinsic(mocker): nonce_key="coldkeypub", sign_with="coldkey", use_nonce=True, - period=None + period=None, ) @@ -139,5 +139,5 @@ def test_add_stake_multiple_extrinsic(mocker): nonce_key="coldkeypub", sign_with="coldkey", use_nonce=True, - period=None + period=None, ) diff --git a/tests/unit_tests/extrinsics/test_start_call.py b/tests/unit_tests/extrinsics/test_start_call.py index 658f0883f7..ece0e6cf42 100644 --- a/tests/unit_tests/extrinsics/test_start_call.py +++ b/tests/unit_tests/extrinsics/test_start_call.py @@ -33,7 +33,7 @@ def test_start_call_extrinsics(subtensor, mocker, fake_wallet): wallet=wallet, wait_for_inclusion=True, wait_for_finalization=False, - period=None + period=None, ) assert success is True diff --git a/tests/unit_tests/extrinsics/test_transfer.py b/tests/unit_tests/extrinsics/test_transfer.py index 06567e2737..fcd532e2d0 100644 --- a/tests/unit_tests/extrinsics/test_transfer.py +++ b/tests/unit_tests/extrinsics/test_transfer.py @@ -34,7 +34,7 @@ def test_do_transfer_is_success_true(subtensor, fake_wallet, mocker): wallet=fake_wallet, wait_for_inclusion=fake_wait_for_inclusion, wait_for_finalization=fake_wait_for_finalization, - period=None + period=None, ) # subtensor.substrate.submit_extrinsic.return_value.process_events.assert_called_once() assert result == (True, 1, "Success with response.") @@ -72,7 +72,7 @@ def test_do_transfer_is_success_false(subtensor, fake_wallet, mocker): wallet=fake_wallet, wait_for_inclusion=fake_wait_for_inclusion, wait_for_finalization=fake_wait_for_finalization, - period=None + period=None, ) assert result == (False, "", "") @@ -86,7 +86,9 @@ def test_do_transfer_no_waits(subtensor, fake_wallet, mocker): fake_wait_for_inclusion = False fake_wait_for_finalization = False - mocker.patch.object(subtensor, "sign_and_send_extrinsic", return_value=(True, "msg")) + mocker.patch.object( + subtensor, "sign_and_send_extrinsic", return_value=(True, "msg") + ) # Call result = _do_transfer( @@ -109,6 +111,6 @@ def test_do_transfer_no_waits(subtensor, fake_wallet, mocker): wallet=fake_wallet, wait_for_inclusion=fake_wait_for_inclusion, wait_for_finalization=fake_wait_for_finalization, - period=None + period=None, ) assert result == (True, "", "msg") diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index 03b45a35fe..17c858c8a5 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -1269,7 +1269,7 @@ def test_commit(subtensor, fake_wallet, mocker): netuid=fake_netuid, data_type=f"Raw{len(fake_data)}", data=fake_data.encode(), - period=None + period=None, ) assert result is mocked_publish_metadata.return_value @@ -1492,7 +1492,7 @@ def test_do_serve_axon_is_success( wallet=fake_wallet, wait_for_inclusion=fake_wait_for_inclusion, wait_for_finalization=fake_wait_for_finalization, - period=None + period=None, ) assert result[0] is True @@ -1505,7 +1505,9 @@ def test_do_serve_axon_is_not_success(subtensor, fake_wallet, mocker, fake_call_ fake_wait_for_inclusion = True fake_wait_for_finalization = True - mocker.patch.object(subtensor, "sign_and_send_extrinsic", return_value=(False, None)) + mocker.patch.object( + subtensor, "sign_and_send_extrinsic", return_value=(False, None) + ) # Call result = do_serve_axon( @@ -1528,7 +1530,7 @@ def test_do_serve_axon_is_not_success(subtensor, fake_wallet, mocker, fake_call_ wallet=fake_wallet, wait_for_inclusion=fake_wait_for_inclusion, wait_for_finalization=fake_wait_for_finalization, - period=None + period=None, ) assert result == (False, None) @@ -1541,7 +1543,9 @@ def test_do_serve_axon_no_waits(subtensor, fake_wallet, mocker, fake_call_params fake_wait_for_finalization = False mocked_sign_and_send_extrinsic = mocker.Mock(return_value=(True, "")) - mocker.patch.object(subtensor, "sign_and_send_extrinsic", new=mocked_sign_and_send_extrinsic) + mocker.patch.object( + subtensor, "sign_and_send_extrinsic", new=mocked_sign_and_send_extrinsic + ) # Call result = do_serve_axon( @@ -1564,7 +1568,7 @@ def test_do_serve_axon_no_waits(subtensor, fake_wallet, mocker, fake_call_params wallet=fake_wallet, wait_for_inclusion=fake_wait_for_inclusion, wait_for_finalization=fake_wait_for_finalization, - period=None + period=None, ) assert result == (True, "") @@ -1956,7 +1960,7 @@ def test_commit_weights(subtensor, fake_wallet, mocker): commit_hash=mocked_generate_weight_hash.return_value, wait_for_inclusion=wait_for_inclusion, wait_for_finalization=wait_for_finalization, - period=16 + period=16, ) assert result == expected_result @@ -1996,7 +2000,7 @@ def test_reveal_weights(subtensor, fake_wallet, mocker): salt=salt, wait_for_inclusion=False, wait_for_finalization=False, - period=16 + period=16, ) From caf8c7080faa98658d4daa6b60e0c8a060a2baa9 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 2 May 2025 00:53:23 -0700 Subject: [PATCH 141/251] optimization + period for test --- bittensor/core/async_subtensor.py | 5 ++--- bittensor/core/subtensor.py | 5 ++--- tests/e2e_tests/test_staking.py | 1 + 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 4645a1ffda..d3c04c42a5 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -98,7 +98,7 @@ check_and_convert_to_balance, ) from bittensor.utils.btlogging import logging -from bittensor.utils.weight_utils import generate_weight_hash +from bittensor.utils.weight_utils import generate_weight_hash, convert_uids_and_weights if TYPE_CHECKING: from async_substrate_interface.types import ScaleObj @@ -3794,8 +3794,7 @@ async def root_set_weights( Returns: `True` if the setting of weights is successful, `False` otherwise. """ - netuids_ = np.array(netuids, dtype=np.int64) - weights_ = np.array(weights, dtype=np.float32) + netuids_, weights_ = convert_uids_and_weights(netuids, weights) logging.info(f"Setting weights in network: [blue]{self.network}[/blue]") # Run the set weights operation. return await set_root_weights_extrinsic( diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 7f7a9eaa81..a01323e050 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -101,7 +101,7 @@ check_and_convert_to_balance, ) from bittensor.utils.btlogging import logging -from bittensor.utils.weight_utils import generate_weight_hash +from bittensor.utils.weight_utils import generate_weight_hash, convert_uids_and_weights if TYPE_CHECKING: from bittensor_wallet import Wallet @@ -3080,8 +3080,7 @@ def root_set_weights( Returns: `True` if the setting of weights is successful, `False` otherwise. """ - netuids_ = np.array(netuids, dtype=np.int64) - weights_ = np.array(weights, dtype=np.float32) + netuids_, weights_ = convert_uids_and_weights(netuids, weights) logging.info(f"Setting weights in network: [blue]{self.network}[/blue]") return set_root_weights_extrinsic( subtensor=self, diff --git a/tests/e2e_tests/test_staking.py b/tests/e2e_tests/test_staking.py index 477c61054e..9ab91351db 100644 --- a/tests/e2e_tests/test_staking.py +++ b/tests/e2e_tests/test_staking.py @@ -169,6 +169,7 @@ def test_single_operation(subtensor, alice_wallet, bob_wallet): amount=stake_bob, wait_for_inclusion=True, wait_for_finalization=True, + period=16, ) assert success is True From a752e50a0b3dee679e3a627ff8b1b35745da72d0 Mon Sep 17 00:00:00 2001 From: BD Himes <37844818+thewhaleking@users.noreply.github.com> Date: Fri, 2 May 2025 17:17:47 +0200 Subject: [PATCH 142/251] Update bittensor/core/async_subtensor.py --- bittensor/core/async_subtensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index d3c04c42a5..5149640df5 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -3036,7 +3036,7 @@ async def tx_rate_limit( Optional[int]: The transaction rate limit of the network, None if not available. The transaction rate limit is an essential parameter for ensuring the stability and scalability of the Bittensor - network. It helps in managing a network load and preventing congestion, thereby maintaining efficient and + network. It helps in managing network load and preventing congestion, thereby maintaining efficient and timely transaction processing. """ block_hash = await self.determine_block_hash(block, block_hash, reuse_block) From 6bb89542996670350e4d470f38a1abc00020434a Mon Sep 17 00:00:00 2001 From: BD Himes <37844818+thewhaleking@users.noreply.github.com> Date: Fri, 2 May 2025 17:17:55 +0200 Subject: [PATCH 143/251] Update bittensor/core/async_subtensor.py --- bittensor/core/async_subtensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 5149640df5..9d95c898c1 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -3247,7 +3247,7 @@ async def sign_and_send_extrinsic( the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. nonce_key: the type on nonce to use. Options are "hotkey" or "coldkey". - nonce_key: the type on nonce to use. Options are "hotkey" or "coldkey". + nonce_key: the type on nonce to use. Options are "hotkey", "coldkey", or "coldkeypub". raise_error: raises a relevant exception rather than returning `False` if unsuccessful. Returns: From 1fc275a261472f49559926d981fe6338d293a686 Mon Sep 17 00:00:00 2001 From: BD Himes <37844818+thewhaleking@users.noreply.github.com> Date: Fri, 2 May 2025 17:18:05 +0200 Subject: [PATCH 144/251] Update bittensor/core/async_subtensor.py --- bittensor/core/async_subtensor.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 9d95c898c1..c342cddf4d 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -3421,9 +3421,6 @@ async def burned_register( period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. - period (Optional[int]): The number of blocks during which the transaction will remain valid after it's submitted. If - the transaction is not included in a block within that number of blocks, it will expire and be rejected. - You can think of it as an expiration date for the transaction. Returns: bool: ``True`` if the registration is successful, False otherwise. From 12323dcfde88eb8f6d2f426736e4aecbe63fb135 Mon Sep 17 00:00:00 2001 From: BD Himes <37844818+thewhaleking@users.noreply.github.com> Date: Fri, 2 May 2025 17:18:14 +0200 Subject: [PATCH 145/251] Update bittensor/core/async_subtensor.py --- bittensor/core/async_subtensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index c342cddf4d..8fc95fdc0e 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -3689,7 +3689,7 @@ async def reveal_weights( weights (np.ndarray): NumPy array of weight values corresponding to each UID. salt (np.ndarray): NumPy array of salt values corresponding to the hash function. version_key (int): Version key for compatibility with the network. Default is ``int representation of - a Bittensor version``. + the Bittensor version``. wait_for_inclusion (bool): Waits for the transaction to be included in a block. Default is ``False``. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. Default is ``False``. From 00f0b69d58e9e1f15e9cadbae5d2b9253240d045 Mon Sep 17 00:00:00 2001 From: BD Himes <37844818+thewhaleking@users.noreply.github.com> Date: Fri, 2 May 2025 17:18:22 +0200 Subject: [PATCH 146/251] Update bittensor/utils/weight_utils.py --- bittensor/utils/weight_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bittensor/utils/weight_utils.py b/bittensor/utils/weight_utils.py index 3a24f5fcc2..0afa8269ad 100644 --- a/bittensor/utils/weight_utils.py +++ b/bittensor/utils/weight_utils.py @@ -190,7 +190,7 @@ def convert_weights_and_uids_for_emit( f"Passed weights and uids must have the same length, got {len(uids)} and {len(weights)}" ) if sum(weights) == 0: - return [], [] # Nothing to set on a chain. + return [], [] # Nothing to set on chain. else: max_weight = float(max(weights)) weights = [ From 2235197baa0ffb5beb1a876c7059b963c966298d Mon Sep 17 00:00:00 2001 From: BD Himes <37844818+thewhaleking@users.noreply.github.com> Date: Fri, 2 May 2025 17:18:35 +0200 Subject: [PATCH 147/251] Update bittensor/utils/weight_utils.py --- bittensor/utils/weight_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bittensor/utils/weight_utils.py b/bittensor/utils/weight_utils.py index 0afa8269ad..2a7b7f3afd 100644 --- a/bittensor/utils/weight_utils.py +++ b/bittensor/utils/weight_utils.py @@ -249,7 +249,7 @@ def process_weights_for_netuid( logging.debug(f"subtensor: {subtensor}") logging.debug(f"metagraph: {metagraph}") - # Get latest metagraph from a chain if metagraph is None. + # Get latest metagraph from chain if metagraph is None. if metagraph is None: metagraph = subtensor.metagraph(netuid) From 4c032d28b992d97f0a5e22bdde8fe628cde4dd1c Mon Sep 17 00:00:00 2001 From: BD Himes <37844818+thewhaleking@users.noreply.github.com> Date: Fri, 2 May 2025 17:18:44 +0200 Subject: [PATCH 148/251] Update tests/e2e_tests/test_commit_weights.py --- tests/e2e_tests/test_commit_weights.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e_tests/test_commit_weights.py b/tests/e2e_tests/test_commit_weights.py index 6d77895e76..1008ea7c23 100644 --- a/tests/e2e_tests/test_commit_weights.py +++ b/tests/e2e_tests/test_commit_weights.py @@ -158,7 +158,7 @@ async def test_commit_weights_uses_next_nonce(local_chain, subtensor, alice_wall Steps: 1. Register a subnet through Alice 2. Register Alice's neuron and add stake - 3. Enable a commit-reveal mechanism on subnet + 3. Enable the commit-reveal mechanism on subnet 4. Lower the commit_reveal interval and rate limit 5. Commit weights three times 6. Assert that all commits succeeded From cb7b4d0706a2f610be578e20cf0f88337433d6b7 Mon Sep 17 00:00:00 2001 From: BD Himes <37844818+thewhaleking@users.noreply.github.com> Date: Fri, 2 May 2025 17:18:51 +0200 Subject: [PATCH 149/251] Update tests/e2e_tests/test_commit_weights.py --- tests/e2e_tests/test_commit_weights.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e_tests/test_commit_weights.py b/tests/e2e_tests/test_commit_weights.py index 1008ea7c23..d3dabf6152 100644 --- a/tests/e2e_tests/test_commit_weights.py +++ b/tests/e2e_tests/test_commit_weights.py @@ -19,7 +19,7 @@ async def test_commit_and_reveal_weights_legacy(local_chain, subtensor, alice_wa Steps: 1. Register a subnet through Alice - 2. Enable a commit-reveal mechanism on subnet + 2. Enable the commit-reveal mechanism on subnet 3. Lower the commit_reveal interval and rate limit 4. Commit weights and verify 5. Wait interval & reveal weights and verify From eb4d3d65dfe913e7f7fe8c97e4cc7a85b842b4d7 Mon Sep 17 00:00:00 2001 From: BD Himes <37844818+thewhaleking@users.noreply.github.com> Date: Fri, 2 May 2025 17:19:04 +0200 Subject: [PATCH 150/251] Update tests/e2e_tests/conftest.py --- tests/e2e_tests/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e_tests/conftest.py b/tests/e2e_tests/conftest.py index a247b85a5a..cd5229d5ee 100644 --- a/tests/e2e_tests/conftest.py +++ b/tests/e2e_tests/conftest.py @@ -69,7 +69,7 @@ def local_chain(request): if sys.platform.startswith("linux"): docker_command = ( "Install docker with command " - "[blue]sudo apt-gat update && sudo apt-get install docker.io -y[/blue]" + "[blue]sudo apt-get update && sudo apt-get install docker.io -y[/blue]" " or use documentation [blue]https://docs.docker.com/engine/install/[/blue]" ) elif sys.platform == "darwin": From 3f0b2e01ce7d5cf6b12d7498b344bcee01317016 Mon Sep 17 00:00:00 2001 From: BD Himes <37844818+thewhaleking@users.noreply.github.com> Date: Fri, 2 May 2025 17:19:21 +0200 Subject: [PATCH 151/251] Update bittensor/core/async_subtensor.py --- bittensor/core/async_subtensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 8fc95fdc0e..082c0f44e8 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -4030,7 +4030,7 @@ async def set_weights( weights (Union[NDArray[np.float32], torch.FloatTensor, list]): The corresponding weights to be set for each UID. version_key (int): Version key for compatibility with the network. Default is int representation of - a Bittensor version. + the Bittensor version. wait_for_inclusion (bool): Waits for the transaction to be included in a block. Default is ``False``. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. Default is ``False``. From a2cc42bfad1398d50526e1cbd2db6834b351b71a Mon Sep 17 00:00:00 2001 From: BD Himes <37844818+thewhaleking@users.noreply.github.com> Date: Fri, 2 May 2025 17:19:34 +0200 Subject: [PATCH 152/251] Update bittensor/core/extrinsics/asyncex/weights.py --- bittensor/core/extrinsics/asyncex/weights.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bittensor/core/extrinsics/asyncex/weights.py b/bittensor/core/extrinsics/asyncex/weights.py index 4b3d5cd56d..cf993b37eb 100644 --- a/bittensor/core/extrinsics/asyncex/weights.py +++ b/bittensor/core/extrinsics/asyncex/weights.py @@ -318,7 +318,7 @@ async def set_weights_extrinsic( wait_for_finalization: bool = False, period: Optional[int] = 8, ) -> tuple[bool, str]: - """Sets the given weights and values on a chain for a wallet hotkey account. + """Sets the given weights and values on chain for a given wallet hotkey account. Args: subtensor (bittensor.core.async_subtensor.AsyncSubtensor): Bittensor subtensor object. From a31eca70236545e2893667081897b1c55d571d67 Mon Sep 17 00:00:00 2001 From: BD Himes <37844818+thewhaleking@users.noreply.github.com> Date: Fri, 2 May 2025 17:19:46 +0200 Subject: [PATCH 153/251] Update bittensor/core/extrinsics/root.py --- bittensor/core/extrinsics/root.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bittensor/core/extrinsics/root.py b/bittensor/core/extrinsics/root.py index 4a6dfbc0ee..2987df6724 100644 --- a/bittensor/core/extrinsics/root.py +++ b/bittensor/core/extrinsics/root.py @@ -227,7 +227,7 @@ def set_root_weights_extrinsic( wait_for_finalization: bool = False, period: Optional[int] = None, ) -> bool: - """Sets the given weights and values on a chain for a wallet hotkey account. + """Sets the given weights and values on chain for a given wallet hotkey account. Arguments: subtensor (bittensor.core.subtensor.Subtensor): The Subtensor object From 81b7575bf01d93ab1eba8839aa62118e91ebb3f6 Mon Sep 17 00:00:00 2001 From: BD Himes <37844818+thewhaleking@users.noreply.github.com> Date: Fri, 2 May 2025 17:20:42 +0200 Subject: [PATCH 154/251] Update bittensor/core/extrinsics/root.py --- bittensor/core/extrinsics/root.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bittensor/core/extrinsics/root.py b/bittensor/core/extrinsics/root.py index 2987df6724..f59a1104d7 100644 --- a/bittensor/core/extrinsics/root.py +++ b/bittensor/core/extrinsics/root.py @@ -233,7 +233,7 @@ def set_root_weights_extrinsic( subtensor (bittensor.core.subtensor.Subtensor): The Subtensor object wallet (bittensor_wallet.Wallet): Bittensor wallet object. netuids (Union[NDArray[np.int64], list[int]]): The `netuid` of the subnet to set weights for. - weights (Union[NDArray[np.float32], list[float]]): Weights to set. These must be `float's and must correspond + weights (Union[NDArray[np.float32], list[float]]): Weights to set. These must be floats and must correspond to the passed `netuid` s. version_key (int): The version key of the validator. wait_for_inclusion (bool): If set, waits for the extrinsic to enter a block before returning `True`, or returns From 8000b16670f8b45c5cb7e1b27a88746bda3201e3 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 2 May 2025 08:20:48 -0700 Subject: [PATCH 155/251] initial --- bittensor/core/subtensor_api.py | 130 ++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 bittensor/core/subtensor_api.py diff --git a/bittensor/core/subtensor_api.py b/bittensor/core/subtensor_api.py new file mode 100644 index 0000000000..a28c1ae82b --- /dev/null +++ b/bittensor/core/subtensor_api.py @@ -0,0 +1,130 @@ +from typing import Optional, Union, TYPE_CHECKING +from bittensor.core.subtensor import Subtensor as _Subtensor +from bittensor.core.async_subtensor import AsyncSubtensor as _AsyncSubtensor + +if TYPE_CHECKING: + from bittensor.core.config import Config + + +class _Extrinsics: + def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): + self.add_stake = subtensor.add_stake + self.add_stake_multiple = subtensor.add_stake_multiple + self.burned_register = subtensor.burned_register + self.commit_weights = subtensor.commit_weights + self.move_stake = subtensor.move_stake + self.register = subtensor.register + self.register_subnet = subtensor.register_subnet + self.reveal_weights = subtensor.reveal_weights + self.root_register = subtensor.root_register + self.root_set_weights = subtensor.root_set_weights + self.set_subnet_identity = subtensor.set_subnet_identity + self.set_weights = subtensor.set_weights + self.serve_axon = subtensor.serve_axon + self.start_call = subtensor.start_call + self.swap_stake = subtensor.swap_stake + self.transfer = subtensor.transfer + self.transfer_stake = subtensor.transfer_stake + self.unstake = subtensor.unstake + self.unstake_multiple = subtensor.unstake_multiple + + +class _Queries: + def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): + self.query_constant = subtensor.query_constant + self.query_map = subtensor.query_map + self.query_map_subtensor = subtensor.query_map_subtensor + self.query_module = subtensor.query_module + self.query_runtime_api = subtensor.query_runtime_api + self.query_subtensor = subtensor.query_subtensor + + +class _Subnets: + def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): + self.all_subnets = subtensor.all_subnets + self.get_all_subnets_info = subtensor.get_all_subnets_info + self.get_neuron_for_pubkey_and_subnet = subtensor.get_neuron_for_pubkey_and_subnet + self.get_subnet_burn_cost = subtensor.get_subnet_burn_cost + self.get_subnet_hyperparameters = subtensor.get_subnet_hyperparameters + self.get_subnet_owner_hotkey = subtensor.get_subnet_owner_hotkey + self.get_subnet_reveal_period_epochs = subtensor.get_subnet_reveal_period_epochs + self.get_subnet_validator_permits = subtensor.get_subnet_validator_permits + self.get_subnets = subtensor.get_subnets + self.get_total_subnets = subtensor.get_total_subnets + self.get_uid_for_hotkey_on_subnet = subtensor.get_uid_for_hotkey_on_subnet + self.is_hotkey_registered_on_subnet = subtensor.is_hotkey_registered_on_subnet + self.register_subnet = subtensor.register_subnet + self.set_subnet_identity = subtensor.set_subnet_identity + self.subnet = subtensor.subnet + self.subnet_exists = subtensor.subnet_exists + self.subnetwork_n = subtensor.subnetwork_n + + +class SubtensorApi: + def __init__( + self, + network: Optional[str] = None, + config: Optional["Config"] = None, + _mock: bool = False, + log_verbose: bool = False, + async_subtensor: bool = False, + ): + if async_subtensor: + self._subtensor = _AsyncSubtensor(network=network, config=config, _mock=_mock, log_verbose=log_verbose) + else: + self._subtensor = _Subtensor(network=network, config=config, _mock=_mock, log_verbose=log_verbose) + + self.network = network + self._mock = _mock + self.log_verbose = log_verbose + self.is_async = async_subtensor + + self.config = None + self.start_call = None + self.chain_endpoint = None + self.substrate = None + self.close = None + + self.add_important_fields() + + def add_important_fields(self): + """Adds important fields from the subtensor instance to this API instance.""" + self.substrate = self._subtensor.substrate + self.chain_endpoint = self._subtensor.chain_endpoint + self.config = self._subtensor.config + self.start_call = self._subtensor.start_call + self.close = self._subtensor.close + + def __str__(self): + return f"" + + def __repr__(self): + return self.__str__() + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.close() + + async def __aenter__(self): + return await self._subtensor.__aenter__() + + async def __aexit__(self, exc_type, exc_val, exc_tb): + await self.substrate.close() + + @property + def block(self): + return self._subtensor.block + + @property + def extrinsics(self): + return _Extrinsics(self._subtensor) + + @property + def queries(self): + return _Queries(self._subtensor) + + @property + def subnets(self): + return _Subnets(self._subtensor) From 65e4f12a935270c2d475d2e678738da483311fe2 Mon Sep 17 00:00:00 2001 From: BD Himes <37844818+thewhaleking@users.noreply.github.com> Date: Fri, 2 May 2025 17:20:57 +0200 Subject: [PATCH 156/251] Update bittensor/core/extrinsics/staking.py --- bittensor/core/extrinsics/staking.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bittensor/core/extrinsics/staking.py b/bittensor/core/extrinsics/staking.py index 173fee2db1..58f5ed0b71 100644 --- a/bittensor/core/extrinsics/staking.py +++ b/bittensor/core/extrinsics/staking.py @@ -215,7 +215,7 @@ def add_stake_multiple_extrinsic( wait_for_finalization: bool = False, period: Optional[int] = None, ) -> bool: - """Adds a stake to each ``hotkey_ss58`` in the list, using each amount, from a common coldkey. + """Adds stake to each ``hotkey_ss58`` in the list, using each amount, from a common coldkey. Arguments: subtensor: The initialized SubtensorInterface object. From f02cd52af3a800b424e6f35abf8244516d885ed0 Mon Sep 17 00:00:00 2001 From: BD Himes <37844818+thewhaleking@users.noreply.github.com> Date: Fri, 2 May 2025 17:21:19 +0200 Subject: [PATCH 157/251] Update bittensor/core/extrinsics/take.py --- bittensor/core/extrinsics/take.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bittensor/core/extrinsics/take.py b/bittensor/core/extrinsics/take.py index d07e628aab..968338d8a9 100644 --- a/bittensor/core/extrinsics/take.py +++ b/bittensor/core/extrinsics/take.py @@ -69,7 +69,7 @@ def decrease_take_extrinsic( raise_error: bool = False, period: Optional[int] = None, ) -> tuple[bool, str]: - """Sets the delegate 'take' percentage for a neuron identified by its hotkey. + """Sets the delegate `take` percentage for a neuron identified by its hotkey. Args: subtensor (Subtensor): Blockchain connection. From caba14a3e204b14b85a1f83303fafea9c75a53b0 Mon Sep 17 00:00:00 2001 From: BD Himes <37844818+thewhaleking@users.noreply.github.com> Date: Fri, 2 May 2025 17:21:32 +0200 Subject: [PATCH 158/251] Update bittensor/core/subtensor.py --- bittensor/core/subtensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index a01323e050..fc923d4576 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -485,7 +485,7 @@ def commit_reveal_enabled( self, netuid: int, block: Optional[int] = None ) -> Optional[bool]: """ - Check if a commit-reveal mechanism is enabled for a given network at a specific block. + Check if the commit-reveal mechanism is enabled for a given network at a specific block. Arguments: netuid: The network identifier for which to check the commit-reveal mechanism. From aefb3851e76f73e6f84b2fa6ed47f7e7019b8141 Mon Sep 17 00:00:00 2001 From: BD Himes <37844818+thewhaleking@users.noreply.github.com> Date: Fri, 2 May 2025 17:22:34 +0200 Subject: [PATCH 159/251] Update bittensor/core/subtensor.py --- bittensor/core/subtensor.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index fc923d4576..c1691f19b3 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -2376,9 +2376,6 @@ def wait_for_block(self, block: Optional[int] = None): bool: True if the target block was reached, False if timeout occurred. Example: - >>> import bittensor as bt - >>> subtensor = bt.subtensor(network="finney")) - >>> subtensor.wait_for_block() # Waits for the next block >>> subtensor.wait_for_block(block=1234) # Waits for a specific block """ From 72ac8a4833d7804f5c702fc604c73944acb37653 Mon Sep 17 00:00:00 2001 From: BD Himes <37844818+thewhaleking@users.noreply.github.com> Date: Fri, 2 May 2025 17:22:48 +0200 Subject: [PATCH 160/251] Update bittensor/core/subtensor.py --- bittensor/core/subtensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index c1691f19b3..430c042607 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -2528,7 +2528,7 @@ def sign_and_send_extrinsic( the transaction is not included in a block within that number of blocks, it will expire and be rejected. You can think of it as an expiration date for the transaction. nonce_key: the type on nonce to use. Options are "hotkey" or "coldkey". - raise_error: raises a relevant exception rather than returning `False` if unsuccessful. + raise_error: raises the relevant exception rather than returning `False` if unsuccessful. Returns: (success, error message) From ce0d97fff71c77875c0a61f3a227c579d2e0eb8f Mon Sep 17 00:00:00 2001 From: BD Himes <37844818+thewhaleking@users.noreply.github.com> Date: Fri, 2 May 2025 17:23:00 +0200 Subject: [PATCH 161/251] Update bittensor/core/subtensor.py --- bittensor/core/subtensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 430c042607..be1a9a3cae 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -2975,7 +2975,7 @@ def reveal_weights( weights (np.ndarray): NumPy array of weight values corresponding to each UID. salt (np.ndarray): NumPy array of salt values corresponding to the hash function. version_key (int): Version key for compatibility with the network. Default is ``int representation of - a Bittensor version``. + the Bittensor version``. wait_for_inclusion (bool): Waits for the transaction to be included in a block. Default is ``False``. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. Default is ``False``. From a6a6a1300a5e165d5043d0ea17ff0b35c104b734 Mon Sep 17 00:00:00 2001 From: BD Himes <37844818+thewhaleking@users.noreply.github.com> Date: Fri, 2 May 2025 17:23:11 +0200 Subject: [PATCH 162/251] Update bittensor/core/subtensor.py --- bittensor/core/subtensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index be1a9a3cae..800790ea40 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -3552,7 +3552,7 @@ def transfer( Arguments: wallet (bittensor_wallet.Wallet): Source wallet for the transfer. dest (str): Destination address for the transfer. - amount (float): Number of tokens to transfer. + amount (float): Amount of tao to transfer. transfer_all (bool): Flag to transfer all tokens. Default is ``False``. wait_for_inclusion (bool): Waits for the transaction to be included in a block. Default is ``True``. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. Default is From 05e0fc5ccbe24edc044f7be25842f323acb097bb Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 2 May 2025 08:36:25 -0700 Subject: [PATCH 163/251] add TODO --- bittensor/core/async_subtensor.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 082c0f44e8..c9b05ceef2 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -3731,9 +3731,11 @@ async def reveal_weights( return success, message + # TODO: remove `block_hash` argument async def root_register( self, wallet: "Wallet", + block_hash: Optional[str] = None, wait_for_inclusion: bool = True, wait_for_finalization: bool = True, period: Optional[int] = None, @@ -3743,6 +3745,7 @@ async def root_register( Arguments: wallet (bittensor_wallet.Wallet): Bittensor wallet instance. + block_hash (Optional[str]): This argument will be removed in Bittensor v10 wait_for_inclusion (bool): Waits for the transaction to be included in a block. Default is ``False``. wait_for_finalization (bool): Waits for the transaction to be finalized on the blockchain. Default is ``False``. From f1e00badfaa856ac51df7fdc055a80265a057704 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 2 May 2025 08:47:50 -0700 Subject: [PATCH 164/251] add cron run --- .github/workflows/e2e-subtensor-tests.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/e2e-subtensor-tests.yaml b/.github/workflows/e2e-subtensor-tests.yaml index 81576ffeef..813ec10cb9 100644 --- a/.github/workflows/e2e-subtensor-tests.yaml +++ b/.github/workflows/e2e-subtensor-tests.yaml @@ -12,6 +12,9 @@ on: branches: [master, development, staging] types: [ opened, synchronize, reopened, ready_for_review ] + schedule: + - cron: '0 2 * * *' # Run every night at 2:00 PST + workflow_dispatch: inputs: verbose: From d50d470c43b0ef3e4f90d0c440de0d47724ce558 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 2 May 2025 08:52:29 -0700 Subject: [PATCH 165/251] PST --- .github/workflows/e2e-subtensor-tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/e2e-subtensor-tests.yaml b/.github/workflows/e2e-subtensor-tests.yaml index 813ec10cb9..f30f9c48a7 100644 --- a/.github/workflows/e2e-subtensor-tests.yaml +++ b/.github/workflows/e2e-subtensor-tests.yaml @@ -13,7 +13,7 @@ on: types: [ opened, synchronize, reopened, ready_for_review ] schedule: - - cron: '0 2 * * *' # Run every night at 2:00 PST + - cron: '0 9 * * *' # Run every night at 2:00 PST workflow_dispatch: inputs: From cbf3173b1c87bd052aa3175b1304a49fe36e5003 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 2 May 2025 09:37:38 -0700 Subject: [PATCH 166/251] period=16 for fast blocks test --- tests/e2e_tests/test_commit_reveal_v3.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/e2e_tests/test_commit_reveal_v3.py b/tests/e2e_tests/test_commit_reveal_v3.py index 4eff2ce052..5d61736969 100644 --- a/tests/e2e_tests/test_commit_reveal_v3.py +++ b/tests/e2e_tests/test_commit_reveal_v3.py @@ -129,6 +129,7 @@ async def test_commit_and_reveal_weights_cr3(local_chain, subtensor, alice_walle wait_for_inclusion=True, wait_for_finalization=True, block_time=BLOCK_TIME, + period=16 ) # Assert committing was a success From 4251e3eec2e259c43fa510fdc6d7918b183a84e7 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 2 May 2025 09:41:41 -0700 Subject: [PATCH 167/251] ruff :) --- tests/e2e_tests/test_commit_reveal_v3.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e_tests/test_commit_reveal_v3.py b/tests/e2e_tests/test_commit_reveal_v3.py index 5d61736969..6fdf02101f 100644 --- a/tests/e2e_tests/test_commit_reveal_v3.py +++ b/tests/e2e_tests/test_commit_reveal_v3.py @@ -129,7 +129,7 @@ async def test_commit_and_reveal_weights_cr3(local_chain, subtensor, alice_walle wait_for_inclusion=True, wait_for_finalization=True, block_time=BLOCK_TIME, - period=16 + period=16, ) # Assert committing was a success From 570b9825d51784ccb085bbf8c8f813eb871fcb19 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 2 May 2025 10:22:29 -0700 Subject: [PATCH 168/251] proper test --- tests/e2e_tests/test_staking.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/e2e_tests/test_staking.py b/tests/e2e_tests/test_staking.py index 9ab91351db..37bb87e332 100644 --- a/tests/e2e_tests/test_staking.py +++ b/tests/e2e_tests/test_staking.py @@ -5,7 +5,7 @@ from tests.helpers.helpers import ApproxBalance from tests.e2e_tests.utils.e2e_test_utils import wait_to_start_call -logging.enable_info() +logging.enable_debug() def test_single_operation(subtensor, alice_wallet, bob_wallet): @@ -162,11 +162,11 @@ def test_single_operation(subtensor, alice_wallet, bob_wallet): ), } + # unstale all to check in later success = subtensor.unstake( wallet=alice_wallet, hotkey_ss58=bob_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid, - amount=stake_bob, wait_for_inclusion=True, wait_for_finalization=True, period=16, @@ -180,6 +180,7 @@ def test_single_operation(subtensor, alice_wallet, bob_wallet): netuid=alice_subnet_netuid, ) + # all balances have been unstaked assert stake == Balance(0).set_unit(alice_subnet_netuid) From 8621ede42f05557889860399ee7bb3320e629c75 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 2 May 2025 13:01:51 -0700 Subject: [PATCH 169/251] add sub-package --- bittensor/core/subtensor_api/__init__.py | 155 +++++++++++++++++++++++ bittensor/utils/easy_imports.py | 1 + 2 files changed, 156 insertions(+) create mode 100644 bittensor/core/subtensor_api/__init__.py diff --git a/bittensor/core/subtensor_api/__init__.py b/bittensor/core/subtensor_api/__init__.py new file mode 100644 index 0000000000..c96080d04f --- /dev/null +++ b/bittensor/core/subtensor_api/__init__.py @@ -0,0 +1,155 @@ +from typing import Optional, Union, TYPE_CHECKING + +from bittensor.core.async_subtensor import AsyncSubtensor as _AsyncSubtensor +from bittensor.core.subtensor import Subtensor as _Subtensor +from .chain import Chain as _Chain +from .commitments import Commitments as _Commitments +from .delegates import Delegates as _Delegates +from .extrinsics import Extrinsics as _Extrinsics +from .metagraph import Metagraphs as _Metagraphs +from .neurons import Neurons as _Neurons +from .queries import Queries as _Queries +from .stakes import Stakes as _Stakes +from .subnet import Subnet as _Subnet +from .wallet import Wallet as _Wallet + +if TYPE_CHECKING: + from bittensor.core.config import Config + + +class SubtensorApi: + """Subtensor API class. + + Arguments: + network: The network to connect to. Defaults to `None` -> `finney`. + config: Bittensor configuration object. Defaults to `None`. + log_verbose: If true, sets the subtensor to log verbosely. Defaults to `False`. + async_subtensor: If true, uses the async subtensor to create the connection. Defaults to `False`. + + Example: + # sync version + import bittensor as bt + + subtensor = bt.SubtensorApi() + print(subtensor.block) + print(subtensor.delegates.get_delegate_identities()) + subtensor.chain.tx_rate_limit() + + # async version + import bittensor as bt + + subtensor = bt.SubtensorApi(async_subtensor=True) + async with subtensor: + print(await subtensor.block) + print(await subtensor.delegates.get_delegate_identities()) + print(await subtensor.chain.tx_rate_limit()) + """ + + def __init__( + self, + network: Optional[str] = None, + config: Optional["Config"] = None, + log_verbose: bool = False, + async_subtensor: bool = False, + _mock: bool = False, + ): + self.network = network + self._mock = _mock + self.log_verbose = log_verbose + self.is_async = async_subtensor + self._config = config + self._subtensor = self._get_subtensor() + + # define empty fields + self.substrate = self._subtensor.substrate + self.add_args = self._subtensor.add_args + self.chain_endpoint = self._subtensor.chain_endpoint + self.close = self._subtensor.close + self.config = self._subtensor.config + self.setup_config = self._subtensor.setup_config + self.help = self._subtensor.help + + self.determine_block_hash = self._subtensor.determine_block_hash + self.encode_params = self._subtensor.encode_params + self.sign_and_send_extrinsic = self._subtensor.sign_and_send_extrinsic + self.start_call = self._subtensor.start_call + self.wait_for_block = self._subtensor.wait_for_block + + def _get_subtensor(self) -> Union["_Subtensor", "_AsyncSubtensor"]: + """Returns the subtensor instance based on the provided config and subtensor type flag.""" + if self.is_async: + return _AsyncSubtensor( + network=self.network, + config=self._config, + _mock=self._mock, + log_verbose=self.log_verbose, + ) + else: + return _Subtensor( + network=self.network, + config=self._config, + _mock=self._mock, + log_verbose=self.log_verbose, + ) + + def __str__(self): + return f"" + + def __repr__(self): + return self.__str__() + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.close() + + async def __aenter__(self): + return await self._subtensor.__aenter__() + + async def __aexit__(self, exc_type, exc_val, exc_tb): + await self.substrate.close() + + @property + def block(self): + return self._subtensor.block + + @property + def chain(self): + return _Chain(self._subtensor) + + @property + def commitments(self): + return _Commitments(self._subtensor) + + @property + def delegates(self): + return _Delegates(self._subtensor) + + @property + def extrinsics(self): + return _Extrinsics(self._subtensor) + + @property + def metagraphs(self): + return _Metagraphs(self._subtensor) + + @property + def neurons(self): + return _Neurons(self._subtensor) + + @property + def queries(self): + return _Queries(self._subtensor) + + @property + def stakes(self): + return _Stakes(self._subtensor) + + @property + def subnet(self): + return _Subnet(self._subtensor) + + @property + def wallet(self): + return _Wallet(self._subtensor) diff --git a/bittensor/utils/easy_imports.py b/bittensor/utils/easy_imports.py index ea0e72bce9..8fa826d4d9 100644 --- a/bittensor/utils/easy_imports.py +++ b/bittensor/utils/easy_imports.py @@ -104,6 +104,7 @@ from bittensor.core.settings import BLOCKTIME from bittensor.core.stream import StreamingSynapse # noqa: F401 from bittensor.core.subtensor import Subtensor +from bittensor.core.subtensor_api import SubtensorApi # noqa: F401 from bittensor.core.synapse import TerminalInfo, Synapse # noqa: F401 from bittensor.core.tensor import Tensor # noqa: F401 from bittensor.core.threadpool import ( # noqa: F401 From 36c447fa84ff8b9aa173eed123670a2e01325169 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 2 May 2025 13:02:47 -0700 Subject: [PATCH 170/251] add sub-classes --- bittensor/core/subtensor_api.py | 130 -------------------- bittensor/core/subtensor_api/chain.py | 19 +++ bittensor/core/subtensor_api/commitments.py | 18 +++ bittensor/core/subtensor_api/delegates.py | 16 +++ bittensor/core/subtensor_api/extrinsics.py | 29 +++++ bittensor/core/subtensor_api/metagraph.py | 12 ++ bittensor/core/subtensor_api/neurons.py | 15 +++ bittensor/core/subtensor_api/queries.py | 15 +++ bittensor/core/subtensor_api/stakes.py | 22 ++++ bittensor/core/subtensor_api/subnet.py | 46 +++++++ bittensor/core/subtensor_api/wallet.py | 36 ++++++ 11 files changed, 228 insertions(+), 130 deletions(-) delete mode 100644 bittensor/core/subtensor_api.py create mode 100644 bittensor/core/subtensor_api/chain.py create mode 100644 bittensor/core/subtensor_api/commitments.py create mode 100644 bittensor/core/subtensor_api/delegates.py create mode 100644 bittensor/core/subtensor_api/extrinsics.py create mode 100644 bittensor/core/subtensor_api/metagraph.py create mode 100644 bittensor/core/subtensor_api/neurons.py create mode 100644 bittensor/core/subtensor_api/queries.py create mode 100644 bittensor/core/subtensor_api/stakes.py create mode 100644 bittensor/core/subtensor_api/subnet.py create mode 100644 bittensor/core/subtensor_api/wallet.py diff --git a/bittensor/core/subtensor_api.py b/bittensor/core/subtensor_api.py deleted file mode 100644 index a28c1ae82b..0000000000 --- a/bittensor/core/subtensor_api.py +++ /dev/null @@ -1,130 +0,0 @@ -from typing import Optional, Union, TYPE_CHECKING -from bittensor.core.subtensor import Subtensor as _Subtensor -from bittensor.core.async_subtensor import AsyncSubtensor as _AsyncSubtensor - -if TYPE_CHECKING: - from bittensor.core.config import Config - - -class _Extrinsics: - def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): - self.add_stake = subtensor.add_stake - self.add_stake_multiple = subtensor.add_stake_multiple - self.burned_register = subtensor.burned_register - self.commit_weights = subtensor.commit_weights - self.move_stake = subtensor.move_stake - self.register = subtensor.register - self.register_subnet = subtensor.register_subnet - self.reveal_weights = subtensor.reveal_weights - self.root_register = subtensor.root_register - self.root_set_weights = subtensor.root_set_weights - self.set_subnet_identity = subtensor.set_subnet_identity - self.set_weights = subtensor.set_weights - self.serve_axon = subtensor.serve_axon - self.start_call = subtensor.start_call - self.swap_stake = subtensor.swap_stake - self.transfer = subtensor.transfer - self.transfer_stake = subtensor.transfer_stake - self.unstake = subtensor.unstake - self.unstake_multiple = subtensor.unstake_multiple - - -class _Queries: - def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): - self.query_constant = subtensor.query_constant - self.query_map = subtensor.query_map - self.query_map_subtensor = subtensor.query_map_subtensor - self.query_module = subtensor.query_module - self.query_runtime_api = subtensor.query_runtime_api - self.query_subtensor = subtensor.query_subtensor - - -class _Subnets: - def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): - self.all_subnets = subtensor.all_subnets - self.get_all_subnets_info = subtensor.get_all_subnets_info - self.get_neuron_for_pubkey_and_subnet = subtensor.get_neuron_for_pubkey_and_subnet - self.get_subnet_burn_cost = subtensor.get_subnet_burn_cost - self.get_subnet_hyperparameters = subtensor.get_subnet_hyperparameters - self.get_subnet_owner_hotkey = subtensor.get_subnet_owner_hotkey - self.get_subnet_reveal_period_epochs = subtensor.get_subnet_reveal_period_epochs - self.get_subnet_validator_permits = subtensor.get_subnet_validator_permits - self.get_subnets = subtensor.get_subnets - self.get_total_subnets = subtensor.get_total_subnets - self.get_uid_for_hotkey_on_subnet = subtensor.get_uid_for_hotkey_on_subnet - self.is_hotkey_registered_on_subnet = subtensor.is_hotkey_registered_on_subnet - self.register_subnet = subtensor.register_subnet - self.set_subnet_identity = subtensor.set_subnet_identity - self.subnet = subtensor.subnet - self.subnet_exists = subtensor.subnet_exists - self.subnetwork_n = subtensor.subnetwork_n - - -class SubtensorApi: - def __init__( - self, - network: Optional[str] = None, - config: Optional["Config"] = None, - _mock: bool = False, - log_verbose: bool = False, - async_subtensor: bool = False, - ): - if async_subtensor: - self._subtensor = _AsyncSubtensor(network=network, config=config, _mock=_mock, log_verbose=log_verbose) - else: - self._subtensor = _Subtensor(network=network, config=config, _mock=_mock, log_verbose=log_verbose) - - self.network = network - self._mock = _mock - self.log_verbose = log_verbose - self.is_async = async_subtensor - - self.config = None - self.start_call = None - self.chain_endpoint = None - self.substrate = None - self.close = None - - self.add_important_fields() - - def add_important_fields(self): - """Adds important fields from the subtensor instance to this API instance.""" - self.substrate = self._subtensor.substrate - self.chain_endpoint = self._subtensor.chain_endpoint - self.config = self._subtensor.config - self.start_call = self._subtensor.start_call - self.close = self._subtensor.close - - def __str__(self): - return f"" - - def __repr__(self): - return self.__str__() - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - self.close() - - async def __aenter__(self): - return await self._subtensor.__aenter__() - - async def __aexit__(self, exc_type, exc_val, exc_tb): - await self.substrate.close() - - @property - def block(self): - return self._subtensor.block - - @property - def extrinsics(self): - return _Extrinsics(self._subtensor) - - @property - def queries(self): - return _Queries(self._subtensor) - - @property - def subnets(self): - return _Subnets(self._subtensor) diff --git a/bittensor/core/subtensor_api/chain.py b/bittensor/core/subtensor_api/chain.py new file mode 100644 index 0000000000..fe03aada99 --- /dev/null +++ b/bittensor/core/subtensor_api/chain.py @@ -0,0 +1,19 @@ +from typing import Union +from bittensor.core.subtensor import Subtensor as _Subtensor +from bittensor.core.async_subtensor import AsyncSubtensor as _AsyncSubtensor + + +class Chain: + """Class for managing chain state operations.""" + + def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): + self.get_block_hash = subtensor.get_block_hash + self.get_current_block = subtensor.get_current_block + self.get_delegate_identities = subtensor.get_delegate_identities + self.get_existential_deposit = subtensor.get_existential_deposit + self.get_minimum_required_stake = subtensor.get_minimum_required_stake + self.get_vote_data = subtensor.get_vote_data + self.get_timestamp = subtensor.get_timestamp + self.last_drand_round = subtensor.last_drand_round + self.state_call = subtensor.state_call + self.tx_rate_limit = subtensor.tx_rate_limit diff --git a/bittensor/core/subtensor_api/commitments.py b/bittensor/core/subtensor_api/commitments.py new file mode 100644 index 0000000000..26c2163a4b --- /dev/null +++ b/bittensor/core/subtensor_api/commitments.py @@ -0,0 +1,18 @@ +from typing import Union +from bittensor.core.subtensor import Subtensor as _Subtensor +from bittensor.core.async_subtensor import AsyncSubtensor as _AsyncSubtensor + + +class Commitments: + """Class for managing any commitment operations.""" + + def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): + self.commit_reveal_enabled = subtensor.commit_reveal_enabled + self.get_all_commitments = subtensor.get_all_commitments + self.get_all_revealed_commitments = subtensor.get_all_revealed_commitments + self.get_commitment = subtensor.get_commitment + self.get_current_weight_commit_info = subtensor.get_current_weight_commit_info + self.get_revealed_commitment = subtensor.get_revealed_commitment + self.get_revealed_commitment_by_hotkey = subtensor.get_revealed_commitment_by_hotkey + self.set_commitment = subtensor.set_commitment + self.set_reveal_commitment = subtensor.set_reveal_commitment diff --git a/bittensor/core/subtensor_api/delegates.py b/bittensor/core/subtensor_api/delegates.py new file mode 100644 index 0000000000..1cdbfca08c --- /dev/null +++ b/bittensor/core/subtensor_api/delegates.py @@ -0,0 +1,16 @@ +from typing import Union +from bittensor.core.subtensor import Subtensor as _Subtensor +from bittensor.core.async_subtensor import AsyncSubtensor as _AsyncSubtensor + + +class Delegates: + """Class for managing delegate operations.""" + + def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): + self.is_hotkey_delegate = subtensor.is_hotkey_delegate + self.get_delegate_by_hotkey = subtensor.get_delegate_by_hotkey + self.set_delegate_take = subtensor.set_delegate_take + self.get_delegate_identities = subtensor.get_delegate_identities + self.get_delegate_take = subtensor.get_delegate_take + self.get_delegated = subtensor.get_delegated + self.get_delegates = subtensor.get_delegates diff --git a/bittensor/core/subtensor_api/extrinsics.py b/bittensor/core/subtensor_api/extrinsics.py new file mode 100644 index 0000000000..0ff4439201 --- /dev/null +++ b/bittensor/core/subtensor_api/extrinsics.py @@ -0,0 +1,29 @@ +from typing import Union +from bittensor.core.subtensor import Subtensor as _Subtensor +from bittensor.core.async_subtensor import AsyncSubtensor as _AsyncSubtensor + + +class Extrinsics: + """Class for managing extrinsic operations.""" + + def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): + self.add_stake = subtensor.add_stake + self.add_stake_multiple = subtensor.add_stake_multiple + self.burned_register = subtensor.burned_register + self.commit_weights = subtensor.commit_weights + self.move_stake = subtensor.move_stake + self.register = subtensor.register + self.register_subnet = subtensor.register_subnet + self.reveal_weights = subtensor.reveal_weights + self.root_register = subtensor.root_register + self.root_set_weights = subtensor.root_set_weights + self.set_children = subtensor.set_children + self.set_subnet_identity = subtensor.set_subnet_identity + self.set_weights = subtensor.set_weights + self.serve_axon = subtensor.serve_axon + self.start_call = subtensor.start_call + self.swap_stake = subtensor.swap_stake + self.transfer = subtensor.transfer + self.transfer_stake = subtensor.transfer_stake + self.unstake = subtensor.unstake + self.unstake_multiple = subtensor.unstake_multiple diff --git a/bittensor/core/subtensor_api/metagraph.py b/bittensor/core/subtensor_api/metagraph.py new file mode 100644 index 0000000000..af143a1620 --- /dev/null +++ b/bittensor/core/subtensor_api/metagraph.py @@ -0,0 +1,12 @@ +from typing import Union +from bittensor.core.subtensor import Subtensor as _Subtensor +from bittensor.core.async_subtensor import AsyncSubtensor as _AsyncSubtensor + + +class Metagraphs: + """Class for managing metagraph operations.""" + + def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): + self.get_metagraph_info = subtensor.get_metagraph_info + self.get_all_metagraphs_info = subtensor.get_all_metagraphs_info + self.metagraph = subtensor.metagraph diff --git a/bittensor/core/subtensor_api/neurons.py b/bittensor/core/subtensor_api/neurons.py new file mode 100644 index 0000000000..c1fd40066f --- /dev/null +++ b/bittensor/core/subtensor_api/neurons.py @@ -0,0 +1,15 @@ +from typing import Union +from bittensor.core.subtensor import Subtensor as _Subtensor +from bittensor.core.async_subtensor import AsyncSubtensor as _AsyncSubtensor + + +class Neurons: + """Class for managing neuron operations.""" + + def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): + self.get_all_neuron_certificates = subtensor.get_all_neuron_certificates + self.get_neuron_certificate = subtensor.get_neuron_certificate + self.neuron_for_uid = subtensor.neuron_for_uid + self.neurons = subtensor.neurons + self.neurons_lite = subtensor.neurons_lite + self.query_identity = subtensor.query_identity diff --git a/bittensor/core/subtensor_api/queries.py b/bittensor/core/subtensor_api/queries.py new file mode 100644 index 0000000000..7209ffb7ae --- /dev/null +++ b/bittensor/core/subtensor_api/queries.py @@ -0,0 +1,15 @@ +from typing import Union +from bittensor.core.subtensor import Subtensor as _Subtensor +from bittensor.core.async_subtensor import AsyncSubtensor as _AsyncSubtensor + + +class Queries: + """Class for managing subtensor query operations.""" + + def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): + self.query_constant = subtensor.query_constant + self.query_map = subtensor.query_map + self.query_map_subtensor = subtensor.query_map_subtensor + self.query_module = subtensor.query_module + self.query_runtime_api = subtensor.query_runtime_api + self.query_subtensor = subtensor.query_subtensor diff --git a/bittensor/core/subtensor_api/stakes.py b/bittensor/core/subtensor_api/stakes.py new file mode 100644 index 0000000000..442f98cad6 --- /dev/null +++ b/bittensor/core/subtensor_api/stakes.py @@ -0,0 +1,22 @@ +from typing import Union +from bittensor.core.subtensor import Subtensor as _Subtensor +from bittensor.core.async_subtensor import AsyncSubtensor as _AsyncSubtensor + + +class Stakes: + """Class for managing stake operations.""" + + def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): + self.add_stake = subtensor.add_stake + self.add_stake_multiple = subtensor.add_stake_multiple + self.get_hotkey_stake = subtensor.get_hotkey_stake + self.get_minimum_required_stake = subtensor.get_minimum_required_stake + self.get_stake = subtensor.get_stake + self.get_stake_add_fee = subtensor.get_stake_add_fee + self.get_stake_for_coldkey = subtensor.get_stake_for_coldkey + self.get_stake_for_coldkey_and_hotkey = subtensor.get_stake_for_coldkey_and_hotkey + self.get_stake_info_for_coldkey = subtensor.get_stake_info_for_coldkey + self.get_stake_movement_fee = subtensor.get_stake_movement_fee + self.get_unstake_fee = subtensor.get_unstake_fee + self.unstake = subtensor.unstake + self.unstake_multiple = subtensor.unstake_multiple diff --git a/bittensor/core/subtensor_api/subnet.py b/bittensor/core/subtensor_api/subnet.py new file mode 100644 index 0000000000..1fb3d0f911 --- /dev/null +++ b/bittensor/core/subtensor_api/subnet.py @@ -0,0 +1,46 @@ +from typing import Union + +from bittensor.core.async_subtensor import AsyncSubtensor as _AsyncSubtensor +from bittensor.core.subtensor import Subtensor as _Subtensor + + +class Subnet: + """Class for managing subnet operations.""" + + def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): + self.all_subnets = subtensor.all_subnets + self.blocks_since_last_step = subtensor.blocks_since_last_step + self.blocks_since_last_update = subtensor.blocks_since_last_update + self.bonds = subtensor.bonds + self.difficulty = subtensor.difficulty + self.get_all_subnets_info = subtensor.get_all_subnets_info + self.get_children = subtensor.get_children + self.get_children_pending = subtensor.get_children_pending + self.get_current_weight_commit_info = subtensor.get_current_weight_commit_info + self.get_hyperparameter = subtensor.get_hyperparameter + self.get_neuron_for_pubkey_and_subnet = ( + subtensor.get_neuron_for_pubkey_and_subnet + ) + self.get_next_epoch_start_block = subtensor.get_next_epoch_start_block + self.get_subnet_burn_cost = subtensor.get_subnet_burn_cost + self.get_subnet_hyperparameters = subtensor.get_subnet_hyperparameters + self.get_subnet_owner_hotkey = subtensor.get_subnet_owner_hotkey + self.get_subnet_reveal_period_epochs = subtensor.get_subnet_reveal_period_epochs + self.get_subnet_validator_permits = subtensor.get_subnet_validator_permits + self.get_subnets = subtensor.get_subnets + self.get_total_subnets = subtensor.get_total_subnets + self.get_uid_for_hotkey_on_subnet = subtensor.get_uid_for_hotkey_on_subnet + self.immunity_period = subtensor.immunity_period + self.is_hotkey_registered_on_subnet = subtensor.is_hotkey_registered_on_subnet + self.max_weight_limit = subtensor.max_weight_limit + self.min_allowed_weights = subtensor.min_allowed_weights + self.recycle = subtensor.recycle + self.register_subnet = subtensor.register_subnet + self.set_subnet_identity = subtensor.set_subnet_identity + self.subnet = subtensor.subnet + self.subnet_exists = subtensor.subnet_exists + self.subnetwork_n = subtensor.subnetwork_n + self.tempo = subtensor.tempo + self.weights_rate_limit = subtensor.weights_rate_limit + self.weights = subtensor.weights + diff --git a/bittensor/core/subtensor_api/wallet.py b/bittensor/core/subtensor_api/wallet.py new file mode 100644 index 0000000000..348d786608 --- /dev/null +++ b/bittensor/core/subtensor_api/wallet.py @@ -0,0 +1,36 @@ +from typing import Union +from bittensor.core.subtensor import Subtensor as _Subtensor +from bittensor.core.async_subtensor import AsyncSubtensor as _AsyncSubtensor + + +class Wallet: + """Class for managing coldkey, hotkey, wallet operations.""" + + def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): + self.does_hotkey_exist = subtensor.does_hotkey_exist + self.filter_netuids_by_registered_hotkeys = subtensor.filter_netuids_by_registered_hotkeys + self.is_hotkey_registered_any = subtensor.is_hotkey_registered_any + self.is_hotkey_registered = subtensor.is_hotkey_registered + self.is_hotkey_delegate = subtensor.is_hotkey_delegate + self.get_balance = subtensor.get_balance + self.get_balances = subtensor.get_balances + self.get_children = subtensor.get_children + self.get_children_pending = subtensor.get_children_pending + self.get_delegate_by_hotkey = subtensor.get_delegate_by_hotkey + self.get_delegate_take = subtensor.get_delegate_take + self.get_delegated = subtensor.get_delegated + self.get_hotkey_owner = subtensor.get_hotkey_owner + self.get_hotkey_stake = subtensor.get_hotkey_stake + self.get_minimum_required_stake = subtensor.get_minimum_required_stake + self.get_netuids_for_hotkey = subtensor.get_netuids_for_hotkey + self.get_owned_hotkeys = subtensor.get_owned_hotkeys + self.get_stake = subtensor.get_stake + self.get_stake_add_fee = subtensor.get_stake_add_fee + self.get_stake_for_coldkey = subtensor.get_stake_for_coldkey + self.get_stake_for_coldkey_and_hotkey = subtensor.get_stake_for_coldkey_and_hotkey + self.get_stake_for_hotkey = subtensor.get_stake_for_hotkey + self.get_stake_info_for_coldkey = subtensor.get_stake_info_for_coldkey + self.get_stake_movement_fee = subtensor.get_stake_movement_fee + self.get_transfer_fee = subtensor.get_transfer_fee + self.get_unstake_fee = subtensor.get_unstake_fee + From 0068f2ab511dc3d935a8f878e44dca91252eb2d2 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 2 May 2025 13:28:34 -0700 Subject: [PATCH 171/251] add `utils.add_classic_fields` --- bittensor/core/subtensor_api/utils.py | 156 ++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 bittensor/core/subtensor_api/utils.py diff --git a/bittensor/core/subtensor_api/utils.py b/bittensor/core/subtensor_api/utils.py new file mode 100644 index 0000000000..d7643c6c0d --- /dev/null +++ b/bittensor/core/subtensor_api/utils.py @@ -0,0 +1,156 @@ +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from bittensor.core.subtensor_api import SubtensorApi + + +def add_classic_fields(subtensor: "SubtensorApi"): + """If SubtensorApi get `subtensor_fields=True` arguments, then all classic Subtensor fields added to root level.""" + subtensor.add_stake = subtensor._subtensor.add_stake + subtensor.add_stake_multiple = subtensor._subtensor.add_stake_multiple + subtensor.all_subnets = subtensor._subtensor.all_subnets + subtensor.blocks_since_last_step = subtensor._subtensor.blocks_since_last_step + subtensor.blocks_since_last_update = subtensor._subtensor.blocks_since_last_update + subtensor.bonds = subtensor._subtensor.bonds + subtensor.burned_register = subtensor._subtensor.burned_register + subtensor.chain_endpoint = subtensor._subtensor.chain_endpoint + subtensor.commit = subtensor._subtensor.commit + subtensor.commit_reveal_enabled = subtensor._subtensor.commit_reveal_enabled + subtensor.commit_weights = subtensor._subtensor.commit_weights + subtensor.determine_block_hash = subtensor._subtensor.determine_block_hash + subtensor.difficulty = subtensor._subtensor.difficulty + subtensor.does_hotkey_exist = subtensor._subtensor.does_hotkey_exist + subtensor.encode_params = subtensor._subtensor.encode_params + subtensor.filter_netuids_by_registered_hotkeys = ( + subtensor._subtensor.filter_netuids_by_registered_hotkeys + ) + subtensor.get_all_commitments = subtensor._subtensor.get_all_commitments + subtensor.get_all_metagraphs_info = subtensor._subtensor.get_all_metagraphs_info + subtensor.get_all_neuron_certificates = ( + subtensor._subtensor.get_all_neuron_certificates + ) + subtensor.get_all_revealed_commitments = ( + subtensor._subtensor.get_all_revealed_commitments + ) + subtensor.get_all_subnets_info = subtensor._subtensor.get_all_subnets_info + subtensor.get_balance = subtensor._subtensor.get_balance + subtensor.get_balances = subtensor._subtensor.get_balances + subtensor.get_block_hash = subtensor._subtensor.get_block_hash + subtensor.get_children = subtensor._subtensor.get_children + subtensor.get_children_pending = subtensor._subtensor.get_children_pending + subtensor.get_commitment = subtensor._subtensor.get_commitment + subtensor.get_current_block = subtensor._subtensor.get_current_block + subtensor.get_current_weight_commit_info = ( + subtensor._subtensor.get_current_weight_commit_info + ) + subtensor.get_delegate_by_hotkey = subtensor._subtensor.get_delegate_by_hotkey + subtensor.get_delegate_identities = subtensor._subtensor.get_delegate_identities + subtensor.get_delegate_take = subtensor._subtensor.get_delegate_take + subtensor.get_delegated = subtensor._subtensor.get_delegated + subtensor.get_delegates = subtensor._subtensor.get_delegates + subtensor.get_existential_deposit = subtensor._subtensor.get_existential_deposit + subtensor.get_hotkey_owner = subtensor._subtensor.get_hotkey_owner + subtensor.get_hotkey_stake = subtensor._subtensor.get_hotkey_stake + subtensor.get_hyperparameter = subtensor._subtensor.get_hyperparameter + subtensor.get_metagraph_info = subtensor._subtensor.get_metagraph_info + subtensor.get_minimum_required_stake = ( + subtensor._subtensor.get_minimum_required_stake + ) + subtensor.get_netuids_for_hotkey = subtensor._subtensor.get_netuids_for_hotkey + subtensor.get_neuron_certificate = subtensor._subtensor.get_neuron_certificate + subtensor.get_neuron_for_pubkey_and_subnet = ( + subtensor._subtensor.get_neuron_for_pubkey_and_subnet + ) + subtensor.get_next_epoch_start_block = ( + subtensor._subtensor.get_next_epoch_start_block + ) + subtensor.get_owned_hotkeys = subtensor._subtensor.get_owned_hotkeys + subtensor.get_revealed_commitment = subtensor._subtensor.get_revealed_commitment + subtensor.get_revealed_commitment_by_hotkey = ( + subtensor._subtensor.get_revealed_commitment_by_hotkey + ) + subtensor.get_stake = subtensor._subtensor.get_stake + subtensor.get_stake_add_fee = subtensor._subtensor.get_stake_add_fee + subtensor.get_stake_for_coldkey = subtensor._subtensor.get_stake_for_coldkey + subtensor.get_stake_for_coldkey_and_hotkey = ( + subtensor._subtensor.get_stake_for_coldkey_and_hotkey + ) + subtensor.get_stake_for_hotkey = subtensor._subtensor.get_stake_for_hotkey + subtensor.get_stake_info_for_coldkey = ( + subtensor._subtensor.get_stake_info_for_coldkey + ) + subtensor.get_stake_movement_fee = subtensor._subtensor.get_stake_movement_fee + subtensor.get_subnet_burn_cost = subtensor._subtensor.get_subnet_burn_cost + subtensor.get_subnet_hyperparameters = ( + subtensor._subtensor.get_subnet_hyperparameters + ) + subtensor.get_subnet_owner_hotkey = subtensor._subtensor.get_subnet_owner_hotkey + subtensor.get_subnet_reveal_period_epochs = ( + subtensor._subtensor.get_subnet_reveal_period_epochs + ) + subtensor.get_subnet_validator_permits = ( + subtensor._subtensor.get_subnet_validator_permits + ) + subtensor.get_subnets = subtensor._subtensor.get_subnets + subtensor.get_timestamp = subtensor._subtensor.get_timestamp + subtensor.get_total_subnets = subtensor._subtensor.get_total_subnets + subtensor.get_transfer_fee = subtensor._subtensor.get_transfer_fee + subtensor.get_uid_for_hotkey_on_subnet = ( + subtensor._subtensor.get_uid_for_hotkey_on_subnet + ) + subtensor.get_unstake_fee = subtensor._subtensor.get_unstake_fee + subtensor.get_vote_data = subtensor._subtensor.get_vote_data + subtensor.immunity_period = subtensor._subtensor.immunity_period + subtensor.is_hotkey_delegate = subtensor._subtensor.is_hotkey_delegate + subtensor.is_hotkey_registered = subtensor._subtensor.is_hotkey_registered + subtensor.is_hotkey_registered_any = subtensor._subtensor.is_hotkey_registered_any + subtensor.is_hotkey_registered_on_subnet = ( + subtensor._subtensor.is_hotkey_registered_on_subnet + ) + subtensor.last_drand_round = subtensor._subtensor.last_drand_round + subtensor.log_verbose = subtensor._subtensor.log_verbose + subtensor.max_weight_limit = subtensor._subtensor.max_weight_limit + subtensor.metagraph = subtensor._subtensor.metagraph + subtensor.min_allowed_weights = subtensor._subtensor.min_allowed_weights + subtensor.move_stake = subtensor._subtensor.move_stake + subtensor.network = subtensor._subtensor.network + subtensor.neuron_for_uid = subtensor._subtensor.neuron_for_uid + subtensor.neurons_lite = subtensor._subtensor.neurons_lite + subtensor.query_constant = subtensor._subtensor.query_constant + subtensor.query_identity = subtensor._subtensor.query_identity + subtensor.query_map = subtensor._subtensor.query_map + subtensor.query_map_subtensor = subtensor._subtensor.query_map_subtensor + subtensor.query_module = subtensor._subtensor.query_module + subtensor.query_runtime_api = subtensor._subtensor.query_runtime_api + subtensor.query_subtensor = subtensor._subtensor.query_subtensor + subtensor.recycle = subtensor._subtensor.recycle + subtensor.register = subtensor._subtensor.register + subtensor.register_subnet = subtensor._subtensor.register_subnet + subtensor.reveal_weights = subtensor._subtensor.reveal_weights + subtensor.root_register = subtensor._subtensor.root_register + subtensor.root_set_weights = subtensor._subtensor.root_set_weights + subtensor.serve_axon = subtensor._subtensor.serve_axon + subtensor.set_children = subtensor._subtensor.set_children + subtensor.set_commitment = subtensor._subtensor.set_commitment + subtensor.set_delegate_take = subtensor._subtensor.set_delegate_take + subtensor.set_reveal_commitment = subtensor._subtensor.set_reveal_commitment + subtensor.set_subnet_identity = subtensor._subtensor.set_subnet_identity + subtensor.set_weights = subtensor._subtensor.set_weights + subtensor.setup_config = subtensor._subtensor.setup_config + subtensor.sign_and_send_extrinsic = subtensor._subtensor.sign_and_send_extrinsic + subtensor.start_call = subtensor._subtensor.start_call + subtensor.state_call = subtensor._subtensor.state_call + subtensor.subnet = subtensor._subtensor.subnet + subtensor.subnet_exists = subtensor._subtensor.subnet_exists + subtensor.subnetwork_n = subtensor._subtensor.subnetwork_n + subtensor.substrate = subtensor._subtensor.substrate + subtensor.swap_stake = subtensor._subtensor.swap_stake + subtensor.tempo = subtensor._subtensor.tempo + subtensor.transfer = subtensor._subtensor.transfer + subtensor.transfer_stake = subtensor._subtensor.transfer_stake + subtensor.tx_rate_limit = subtensor._subtensor.tx_rate_limit + subtensor.unstake = subtensor._subtensor.unstake + subtensor.unstake_multiple = subtensor._subtensor.unstake_multiple + subtensor.wait_for_block = subtensor._subtensor.wait_for_block + subtensor.weights = subtensor._subtensor.weights + subtensor.weights_rate_limit = subtensor._subtensor.weights_rate_limit From 66ccb7da56b4a4ecda4db577ff883d350981d42a Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 2 May 2025 13:28:52 -0700 Subject: [PATCH 172/251] ruff --- bittensor/core/subtensor_api/commitments.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bittensor/core/subtensor_api/commitments.py b/bittensor/core/subtensor_api/commitments.py index 26c2163a4b..2e594ba6db 100644 --- a/bittensor/core/subtensor_api/commitments.py +++ b/bittensor/core/subtensor_api/commitments.py @@ -13,6 +13,8 @@ def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): self.get_commitment = subtensor.get_commitment self.get_current_weight_commit_info = subtensor.get_current_weight_commit_info self.get_revealed_commitment = subtensor.get_revealed_commitment - self.get_revealed_commitment_by_hotkey = subtensor.get_revealed_commitment_by_hotkey + self.get_revealed_commitment_by_hotkey = ( + subtensor.get_revealed_commitment_by_hotkey + ) self.set_commitment = subtensor.set_commitment self.set_reveal_commitment = subtensor.set_reveal_commitment From f46a2491531990828d6df8cc864c3497a5126689 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 2 May 2025 13:29:15 -0700 Subject: [PATCH 173/251] to be consistent --- .../subtensor_api/{metagraph.py => metagraphs.py} | 0 bittensor/core/subtensor_api/stakes.py | 4 +++- .../core/subtensor_api/{subnet.py => subnets.py} | 3 +-- .../core/subtensor_api/{wallet.py => wallets.py} | 11 +++++++---- 4 files changed, 11 insertions(+), 7 deletions(-) rename bittensor/core/subtensor_api/{metagraph.py => metagraphs.py} (100%) rename bittensor/core/subtensor_api/{subnet.py => subnets.py} (99%) rename bittensor/core/subtensor_api/{wallet.py => wallets.py} (87%) diff --git a/bittensor/core/subtensor_api/metagraph.py b/bittensor/core/subtensor_api/metagraphs.py similarity index 100% rename from bittensor/core/subtensor_api/metagraph.py rename to bittensor/core/subtensor_api/metagraphs.py diff --git a/bittensor/core/subtensor_api/stakes.py b/bittensor/core/subtensor_api/stakes.py index 442f98cad6..78e497d901 100644 --- a/bittensor/core/subtensor_api/stakes.py +++ b/bittensor/core/subtensor_api/stakes.py @@ -14,7 +14,9 @@ def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): self.get_stake = subtensor.get_stake self.get_stake_add_fee = subtensor.get_stake_add_fee self.get_stake_for_coldkey = subtensor.get_stake_for_coldkey - self.get_stake_for_coldkey_and_hotkey = subtensor.get_stake_for_coldkey_and_hotkey + self.get_stake_for_coldkey_and_hotkey = ( + subtensor.get_stake_for_coldkey_and_hotkey + ) self.get_stake_info_for_coldkey = subtensor.get_stake_info_for_coldkey self.get_stake_movement_fee = subtensor.get_stake_movement_fee self.get_unstake_fee = subtensor.get_unstake_fee diff --git a/bittensor/core/subtensor_api/subnet.py b/bittensor/core/subtensor_api/subnets.py similarity index 99% rename from bittensor/core/subtensor_api/subnet.py rename to bittensor/core/subtensor_api/subnets.py index 1fb3d0f911..c3333daf30 100644 --- a/bittensor/core/subtensor_api/subnet.py +++ b/bittensor/core/subtensor_api/subnets.py @@ -4,7 +4,7 @@ from bittensor.core.subtensor import Subtensor as _Subtensor -class Subnet: +class Subnets: """Class for managing subnet operations.""" def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): @@ -43,4 +43,3 @@ def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): self.tempo = subtensor.tempo self.weights_rate_limit = subtensor.weights_rate_limit self.weights = subtensor.weights - diff --git a/bittensor/core/subtensor_api/wallet.py b/bittensor/core/subtensor_api/wallets.py similarity index 87% rename from bittensor/core/subtensor_api/wallet.py rename to bittensor/core/subtensor_api/wallets.py index 348d786608..7314a4fe39 100644 --- a/bittensor/core/subtensor_api/wallet.py +++ b/bittensor/core/subtensor_api/wallets.py @@ -3,12 +3,14 @@ from bittensor.core.async_subtensor import AsyncSubtensor as _AsyncSubtensor -class Wallet: +class Wallets: """Class for managing coldkey, hotkey, wallet operations.""" def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): self.does_hotkey_exist = subtensor.does_hotkey_exist - self.filter_netuids_by_registered_hotkeys = subtensor.filter_netuids_by_registered_hotkeys + self.filter_netuids_by_registered_hotkeys = ( + subtensor.filter_netuids_by_registered_hotkeys + ) self.is_hotkey_registered_any = subtensor.is_hotkey_registered_any self.is_hotkey_registered = subtensor.is_hotkey_registered self.is_hotkey_delegate = subtensor.is_hotkey_delegate @@ -27,10 +29,11 @@ def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): self.get_stake = subtensor.get_stake self.get_stake_add_fee = subtensor.get_stake_add_fee self.get_stake_for_coldkey = subtensor.get_stake_for_coldkey - self.get_stake_for_coldkey_and_hotkey = subtensor.get_stake_for_coldkey_and_hotkey + self.get_stake_for_coldkey_and_hotkey = ( + subtensor.get_stake_for_coldkey_and_hotkey + ) self.get_stake_for_hotkey = subtensor.get_stake_for_hotkey self.get_stake_info_for_coldkey = subtensor.get_stake_info_for_coldkey self.get_stake_movement_fee = subtensor.get_stake_movement_fee self.get_transfer_fee = subtensor.get_transfer_fee self.get_unstake_fee = subtensor.get_unstake_fee - From 745a12686b1b7e3207130d30f5a02a7f9953e3c2 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 2 May 2025 13:29:22 -0700 Subject: [PATCH 174/251] to be consistent + setter --- bittensor/core/subtensor_api/__init__.py | 38 +++++++++++++++++------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/bittensor/core/subtensor_api/__init__.py b/bittensor/core/subtensor_api/__init__.py index c96080d04f..4b5c90acbe 100644 --- a/bittensor/core/subtensor_api/__init__.py +++ b/bittensor/core/subtensor_api/__init__.py @@ -6,12 +6,13 @@ from .commitments import Commitments as _Commitments from .delegates import Delegates as _Delegates from .extrinsics import Extrinsics as _Extrinsics -from .metagraph import Metagraphs as _Metagraphs +from .metagraphs import Metagraphs as _Metagraphs from .neurons import Neurons as _Neurons from .queries import Queries as _Queries from .stakes import Stakes as _Stakes -from .subnet import Subnet as _Subnet -from .wallet import Wallet as _Wallet +from .subnets import Subnets as _Subnets +from .utils import add_classic_fields as _add_classic_fields +from .wallets import Wallets as _Wallets if TYPE_CHECKING: from bittensor.core.config import Config @@ -23,8 +24,9 @@ class SubtensorApi: Arguments: network: The network to connect to. Defaults to `None` -> `finney`. config: Bittensor configuration object. Defaults to `None`. - log_verbose: If true, sets the subtensor to log verbosely. Defaults to `False`. - async_subtensor: If true, uses the async subtensor to create the connection. Defaults to `False`. + log_verbose: If `True`, sets the subtensor to log verbosely. Defaults to `False`. + async_subtensor: If `True`, uses the async subtensor to create the connection. Defaults to `False`. + subtensor_fields: If `True`, all methods from the Subtensor class will be added to the root level of this class. Example: # sync version @@ -43,6 +45,12 @@ class SubtensorApi: print(await subtensor.block) print(await subtensor.delegates.get_delegate_identities()) print(await subtensor.chain.tx_rate_limit()) + + # using `subtensor_fields` + import bittensor as bt + + subtensor = bt.SubtensorApi(subtensor_fields=True) + print(subtensor.bonds(0)) """ def __init__( @@ -51,6 +59,7 @@ def __init__( config: Optional["Config"] = None, log_verbose: bool = False, async_subtensor: bool = False, + subtensor_fields: bool = False, _mock: bool = False, ): self.network = network @@ -60,6 +69,9 @@ def __init__( self._config = config self._subtensor = self._get_subtensor() + # fix naming collision + self._neurons = _Neurons(self._subtensor) + # define empty fields self.substrate = self._subtensor.substrate self.add_args = self._subtensor.add_args @@ -74,6 +86,8 @@ def __init__( self.sign_and_send_extrinsic = self._subtensor.sign_and_send_extrinsic self.start_call = self._subtensor.start_call self.wait_for_block = self._subtensor.wait_for_block + if subtensor_fields: + _add_classic_fields(self) def _get_subtensor(self) -> Union["_Subtensor", "_AsyncSubtensor"]: """Returns the subtensor instance based on the provided config and subtensor type flag.""" @@ -136,7 +150,11 @@ def metagraphs(self): @property def neurons(self): - return _Neurons(self._subtensor) + return self._neurons + + @neurons.setter + def neurons(self, value): + self._neurons = value @property def queries(self): @@ -147,9 +165,9 @@ def stakes(self): return _Stakes(self._subtensor) @property - def subnet(self): - return _Subnet(self._subtensor) + def subnets(self): + return _Subnets(self._subtensor) @property - def wallet(self): - return _Wallet(self._subtensor) + def wallets(self): + return _Wallets(self._subtensor) From 6595299da8b8f4efe0287cb7909b97b51c906b29 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 2 May 2025 13:58:57 -0700 Subject: [PATCH 175/251] initialize for async instance --- bittensor/core/subtensor_api/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bittensor/core/subtensor_api/__init__.py b/bittensor/core/subtensor_api/__init__.py index 4b5c90acbe..3e528c06f0 100644 --- a/bittensor/core/subtensor_api/__init__.py +++ b/bittensor/core/subtensor_api/__init__.py @@ -67,6 +67,8 @@ def __init__( self.log_verbose = log_verbose self.is_async = async_subtensor self._config = config + # assigned only for async instance + self.initialize = None self._subtensor = self._get_subtensor() # fix naming collision @@ -92,6 +94,7 @@ def __init__( def _get_subtensor(self) -> Union["_Subtensor", "_AsyncSubtensor"]: """Returns the subtensor instance based on the provided config and subtensor type flag.""" if self.is_async: + self.initialize = self._subtensor.initialize return _AsyncSubtensor( network=self.network, config=self._config, From 62ebe3acb0877c9668a2c4b1791c59890eaccefa Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 2 May 2025 14:10:58 -0700 Subject: [PATCH 176/251] add compatability tests --- tests/unit_tests/test_subtensor_api.py | 77 ++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 tests/unit_tests/test_subtensor_api.py diff --git a/tests/unit_tests/test_subtensor_api.py b/tests/unit_tests/test_subtensor_api.py new file mode 100644 index 0000000000..f332a640bf --- /dev/null +++ b/tests/unit_tests/test_subtensor_api.py @@ -0,0 +1,77 @@ +from bittensor.core.subtensor import Subtensor +from bittensor.core.subtensor_api import SubtensorApi + + +def test_properties_methods_comparable(): + """Verifies that methods in SubtensorApi and its properties contains all Subtensors methods.""" + # Preps + subtensor = Subtensor(_mock=True) + subtensor_api = SubtensorApi(_mock=True) + + subtensor_methods = [m for m in dir(subtensor) if not m.startswith("_")] + + excluded_subtensor_methods = ["commit"] + + subtensor_api_methods = [m for m in dir(subtensor_api) if not m.startswith("_")] + chain_methods = [m for m in dir(subtensor_api.chain) if not m.startswith("_")] + commitments_methods = [ + m for m in dir(subtensor_api.commitments) if not m.startswith("_") + ] + delegates_methods = [ + m for m in dir(subtensor_api.delegates) if not m.startswith("_") + ] + extrinsics_methods = [ + m for m in dir(subtensor_api.extrinsics) if not m.startswith("_") + ] + metagraphs_methods = [ + m for m in dir(subtensor_api.metagraphs) if not m.startswith("_") + ] + neurons_methods = [m for m in dir(subtensor_api.neurons) if not m.startswith("_")] + queries_methods = [m for m in dir(subtensor_api.queries) if not m.startswith("_")] + stakes_methods = [m for m in dir(subtensor_api.stakes) if not m.startswith("_")] + subnets_methods = [m for m in dir(subtensor_api.subnets) if not m.startswith("_")] + wallets_methods = [m for m in dir(subtensor_api.wallets) if not m.startswith("_")] + + all_subtensor_api_methods = ( + subtensor_api_methods + + chain_methods + + commitments_methods + + delegates_methods + + extrinsics_methods + + metagraphs_methods + + neurons_methods + + queries_methods + + stakes_methods + + subnets_methods + + wallets_methods + ) + + # Assertions + for method in subtensor_methods: + # skipp excluded methods + if method in excluded_subtensor_methods: + continue + assert method in all_subtensor_api_methods, ( + f"`Subtensor.{method}`is not present in class `SubtensorApi`." + ) + + +def test__methods_comparable_with_passed_subtensor_fields(): + """Verifies that methods in SubtensorApi contains all Subtensors methods if `subtensor_fields=True` is passed.""" + # Preps + subtensor = Subtensor(_mock=True) + subtensor_api = SubtensorApi(_mock=True, subtensor_fields=True) + + subtensor_methods = [m for m in dir(subtensor) if not m.startswith("_")] + subtensor_api_methods = [m for m in dir(subtensor_api) if not m.startswith("_")] + + excluded_subtensor_methods = ["commit"] + + # Assertions + for method in subtensor_methods: + # skipp excluded methods + if method in excluded_subtensor_methods: + continue + assert method in subtensor_api_methods, ( + f"`Subtensor.{method}`is not present in class `SubtensorApi`." + ) From 23a862f6cf6ab8c7314ca8f2ddac2e12a5a04833 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 2 May 2025 14:20:47 -0700 Subject: [PATCH 177/251] one more compatability test --- tests/unit_tests/test_subtensor_api.py | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/tests/unit_tests/test_subtensor_api.py b/tests/unit_tests/test_subtensor_api.py index f332a640bf..3d5ab26f47 100644 --- a/tests/unit_tests/test_subtensor_api.py +++ b/tests/unit_tests/test_subtensor_api.py @@ -1,11 +1,12 @@ from bittensor.core.subtensor import Subtensor from bittensor.core.subtensor_api import SubtensorApi +import pytest -def test_properties_methods_comparable(): +def test_properties_methods_comparable(other_class: "Subtensor" = None): """Verifies that methods in SubtensorApi and its properties contains all Subtensors methods.""" # Preps - subtensor = Subtensor(_mock=True) + subtensor = other_class(_mock=True) if other_class else Subtensor(_mock=True) subtensor_api = SubtensorApi(_mock=True) subtensor_methods = [m for m in dir(subtensor) if not m.startswith("_")] @@ -56,10 +57,12 @@ def test_properties_methods_comparable(): ) -def test__methods_comparable_with_passed_subtensor_fields(): +def test__methods_comparable_with_passed_subtensor_fields( + other_class: "Subtensor" = None, +): """Verifies that methods in SubtensorApi contains all Subtensors methods if `subtensor_fields=True` is passed.""" # Preps - subtensor = Subtensor(_mock=True) + subtensor = other_class(_mock=True) if other_class else Subtensor(_mock=True) subtensor_api = SubtensorApi(_mock=True, subtensor_fields=True) subtensor_methods = [m for m in dir(subtensor) if not m.startswith("_")] @@ -75,3 +78,17 @@ def test__methods_comparable_with_passed_subtensor_fields(): assert method in subtensor_api_methods, ( f"`Subtensor.{method}`is not present in class `SubtensorApi`." ) + + +def test_failed_if_subtensor_has_new_method(): + """Verifies that SubtensorApi fails if Subtensor has a new method.""" + # Preps + + class SubtensorWithNewMethod(Subtensor): + def return_my_id(self): + return id(self) + + # Call and assert + + with pytest.raises(AssertionError): + test_properties_methods_comparable(other_class=SubtensorWithNewMethod) From 8f9623e63b219cd3914b7c4b9400d6fd6bb64ce9 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 2 May 2025 14:24:28 -0700 Subject: [PATCH 178/251] fix async instance `initialize` --- bittensor/core/subtensor_api/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/bittensor/core/subtensor_api/__init__.py b/bittensor/core/subtensor_api/__init__.py index 3e528c06f0..e7893dd5ec 100644 --- a/bittensor/core/subtensor_api/__init__.py +++ b/bittensor/core/subtensor_api/__init__.py @@ -94,13 +94,14 @@ def __init__( def _get_subtensor(self) -> Union["_Subtensor", "_AsyncSubtensor"]: """Returns the subtensor instance based on the provided config and subtensor type flag.""" if self.is_async: - self.initialize = self._subtensor.initialize - return _AsyncSubtensor( + _subtensor = _AsyncSubtensor( network=self.network, config=self._config, _mock=self._mock, log_verbose=self.log_verbose, ) + self.initialize = _subtensor.initialize + return _subtensor else: return _Subtensor( network=self.network, From ff0ddf5beb53d28cd94d026f484ee646431d524e Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 2 May 2025 16:47:54 -0700 Subject: [PATCH 179/251] rename argument --- bittensor/core/subtensor_api/__init__.py | 12 +++++++----- tests/unit_tests/test_subtensor_api.py | 6 +++--- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/bittensor/core/subtensor_api/__init__.py b/bittensor/core/subtensor_api/__init__.py index e7893dd5ec..de0170eee6 100644 --- a/bittensor/core/subtensor_api/__init__.py +++ b/bittensor/core/subtensor_api/__init__.py @@ -26,7 +26,7 @@ class SubtensorApi: config: Bittensor configuration object. Defaults to `None`. log_verbose: If `True`, sets the subtensor to log verbosely. Defaults to `False`. async_subtensor: If `True`, uses the async subtensor to create the connection. Defaults to `False`. - subtensor_fields: If `True`, all methods from the Subtensor class will be added to the root level of this class. + backward_compatibility: If `True`, all methods from the Subtensor class will be added to the root level of this class. Example: # sync version @@ -46,10 +46,10 @@ class SubtensorApi: print(await subtensor.delegates.get_delegate_identities()) print(await subtensor.chain.tx_rate_limit()) - # using `subtensor_fields` + # using `backward_compatibility` import bittensor as bt - subtensor = bt.SubtensorApi(subtensor_fields=True) + subtensor = bt.SubtensorApi(backward_compatibility=True) print(subtensor.bonds(0)) """ @@ -59,7 +59,7 @@ def __init__( config: Optional["Config"] = None, log_verbose: bool = False, async_subtensor: bool = False, - subtensor_fields: bool = False, + backward_compatibility: bool = False, _mock: bool = False, ): self.network = network @@ -88,7 +88,9 @@ def __init__( self.sign_and_send_extrinsic = self._subtensor.sign_and_send_extrinsic self.start_call = self._subtensor.start_call self.wait_for_block = self._subtensor.wait_for_block - if subtensor_fields: + + # adds all Subtensor methods into main level os SubtensorApi class + if backward_compatibility: _add_classic_fields(self) def _get_subtensor(self) -> Union["_Subtensor", "_AsyncSubtensor"]: diff --git a/tests/unit_tests/test_subtensor_api.py b/tests/unit_tests/test_subtensor_api.py index 3d5ab26f47..c97378b427 100644 --- a/tests/unit_tests/test_subtensor_api.py +++ b/tests/unit_tests/test_subtensor_api.py @@ -57,13 +57,13 @@ def test_properties_methods_comparable(other_class: "Subtensor" = None): ) -def test__methods_comparable_with_passed_subtensor_fields( +def test__methods_comparable_with_passed_backward_compatibility( other_class: "Subtensor" = None, ): - """Verifies that methods in SubtensorApi contains all Subtensors methods if `subtensor_fields=True` is passed.""" + """Verifies that methods in SubtensorApi contains all Subtensors methods if `backward_compatibility=True` is passed.""" # Preps subtensor = other_class(_mock=True) if other_class else Subtensor(_mock=True) - subtensor_api = SubtensorApi(_mock=True, subtensor_fields=True) + subtensor_api = SubtensorApi(_mock=True, backward_compatibility=True) subtensor_methods = [m for m in dir(subtensor) if not m.startswith("_")] subtensor_api_methods = [m for m in dir(subtensor_api) if not m.startswith("_")] From d08bcf7e9fc9ebf3f7195e9135d673ee9f9cea18 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 2 May 2025 18:10:14 -0700 Subject: [PATCH 180/251] add `build-patched-image` and `cron-e2e-test-with-non-fast-block` --- .github/workflows/e2e-subtensor-tests.yaml | 105 +++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/.github/workflows/e2e-subtensor-tests.yaml b/.github/workflows/e2e-subtensor-tests.yaml index f30f9c48a7..09c40158d6 100644 --- a/.github/workflows/e2e-subtensor-tests.yaml +++ b/.github/workflows/e2e-subtensor-tests.yaml @@ -127,3 +127,108 @@ jobs: sleep 5 fi done + +# `build-patched-image` and `cron-e2e-test-with-non-fast-block` should run once per week + build-patched-image: +# if: github.event_name == 'schedule' + runs-on: ubuntu-latest + + outputs: + image-tag: ${{ steps.set-tag.outputs.tag }} + + steps: +# - name: Check if today is Saturday +# id: set-status +# run: | +# day=$(date -u +%u) +# echo "Today is weekday $day" +# if [ "$day" -ne 6 ]; then +# echo "image_built=false" >> $GITHUB_OUTPUT +# else +# echo "image_built=true" >> $GITHUB_OUTPUT + + - name: Clone + Patch (set 5 blocks instead of 7 days) + run: | + git clone https://github.com/opentensor/subtensor.git + cd subtensor + git checkout devnet-ready + sed -i 's|7 \* 24 \* 60 \* 60 / 12 // 7 days|5 // Only 5 blocks for tests|' runtime/src/lib.rs + + - name: Build Docker image + run: | + cd subtensor + docker build -t subtensor-patched-devnet . + + - name: Save Docker image + run: docker save subtensor-patched-devnet -o subtensor-patched-localnet.tar + + - name: Upload image as artifact + uses: actions/upload-artifact@v4 + with: + name: subtensor-patched-localnet + path: subtensor-patched-localnet.tar + + + cron-e2e-test-with-non-fast-block: +# if: github.event_name == 'schedule' && needs.build-patched-image.outputs.image_built == 'true' + + needs: [find-tests, build-patched-image] + + runs-on: ubuntu-latest + timeout-minutes: 300 + + strategy: + fail-fast: false # Allow other matrix jobs to run even if this job fails + max-parallel: 32 # Set the maximum number of parallel jobs (same as we have cores in ubuntu-latest runner) + matrix: + os: + - ubuntu-latest + test-file: ${{ fromJson(needs.find-tests.outputs.test-files) }} + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + + steps: + - name: Check-out repository + uses: actions/checkout@v4 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install uv + uses: astral-sh/setup-uv@v4 + + - name: install dependencies + run: uv sync --extra dev --dev + + - name: Download Cached Docker Image + uses: actions/download-artifact@v4 + with: + name: subtensor-patched-localnet + + - name: Load Docker image + run: docker load -i subtensor-patched-localnet.tar + + - name: Run patched E2E tests + env: + LOCALNET_IMAGE_NAME: ${{ needs.build-patched-image.outputs.image_name }} + FAST_BLOCKS: "False" + run: | + set +e + for i in 1 2 3; do + echo "🔁 Attempt $i: Running tests" + uv run pytest ${{ matrix.test-file }} -s + status=$? + if [ $status -eq 0 ]; then + echo "✅ Tests passed on attempt $i" + break + else + echo "❌ Tests failed on attempt $i" + if [ $i -eq 3 ]; then + echo "Tests failed after 3 attempts" + exit 1 + fi + echo "Retrying..." + sleep 5 + fi + done From d9da5c86d9cff83b3f0d5db7e369f63a75e143af Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 2 May 2025 18:10:33 -0700 Subject: [PATCH 181/251] improve logic --- tests/e2e_tests/conftest.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/e2e_tests/conftest.py b/tests/e2e_tests/conftest.py index cd5229d5ee..550232ed2a 100644 --- a/tests/e2e_tests/conftest.py +++ b/tests/e2e_tests/conftest.py @@ -195,6 +195,8 @@ def stop_existing_test_containers(): container_name = f"{CONTAINER_NAME_PREFIX}{str(time.time()).replace('.', '_')}" + localnet_image_name = os.getenv("LOCALNET_IMAGE_NAME") + # Command to start container cmds = [ "docker", @@ -206,10 +208,13 @@ def stop_existing_test_containers(): "9944:9944", "-p", "9945:9945", - LOCALNET_IMAGE_NAME, - params, + str(localnet_image_name) if localnet_image_name else LOCALNET_IMAGE_NAME, ] + cmds += params.split() if params else [] + + print("Entire run command: ", cmds) + try_start_docker() stop_existing_test_containers() From 624fa59209aaeecf0b79084fcceb7daf26aef3b9 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 2 May 2025 18:20:08 -0700 Subject: [PATCH 182/251] add `NFB` prefix to non-fast-blocks based tests --- .github/workflows/e2e-subtensor-tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/e2e-subtensor-tests.yaml b/.github/workflows/e2e-subtensor-tests.yaml index 09c40158d6..6a2dd67588 100644 --- a/.github/workflows/e2e-subtensor-tests.yaml +++ b/.github/workflows/e2e-subtensor-tests.yaml @@ -171,7 +171,7 @@ jobs: cron-e2e-test-with-non-fast-block: # if: github.event_name == 'schedule' && needs.build-patched-image.outputs.image_built == 'true' - + name: "NFB: ${{ matrix.test-file }} / Python ${{ matrix.python-version }}" needs: [find-tests, build-patched-image] runs-on: ubuntu-latest From de3b8ce705174c37aaa09d62640922840647f4c7 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 2 May 2025 18:55:28 -0700 Subject: [PATCH 183/251] test --- .github/workflows/e2e-subtensor-tests.yaml | 154 +++++++++++---------- 1 file changed, 78 insertions(+), 76 deletions(-) diff --git a/.github/workflows/e2e-subtensor-tests.yaml b/.github/workflows/e2e-subtensor-tests.yaml index 6a2dd67588..d3acc3185f 100644 --- a/.github/workflows/e2e-subtensor-tests.yaml +++ b/.github/workflows/e2e-subtensor-tests.yaml @@ -66,67 +66,67 @@ jobs: path: subtensor-localnet.tar # Job to run tests in parallel - run-e2e-test: - name: ${{ matrix.test-file }} / Python ${{ matrix.python-version }} - needs: - - find-tests - - pull-docker-image - runs-on: ubuntu-latest - timeout-minutes: 45 - strategy: - fail-fast: false # Allow other matrix jobs to run even if this job fails - max-parallel: 32 # Set the maximum number of parallel jobs (same as we have cores in ubuntu-latest runner) - matrix: - os: - - ubuntu-latest - test-file: ${{ fromJson(needs.find-tests.outputs.test-files) }} - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] - steps: - - name: Check-out repository - uses: actions/checkout@v4 - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - - - name: Install uv - uses: astral-sh/setup-uv@v4 - - - name: install dependencies - run: uv sync --extra dev --dev - - - name: Download Cached Docker Image - uses: actions/download-artifact@v4 - with: - name: subtensor-localnet - - - name: Load Docker Image - run: docker load -i subtensor-localnet.tar - -# - name: Run tests -# run: uv run pytest ${{ matrix.test-file }} -s - - - name: Run tests with retry - run: | - set +e - for i in 1 2 3; do - echo "🔁 Attempt $i: Running tests" - uv run pytest ${{ matrix.test-file }} -s - status=$? - if [ $status -eq 0 ]; then - echo "✅ Tests passed on attempt $i" - break - else - echo "❌ Tests failed on attempt $i" - if [ $i -eq 3 ]; then - echo "Tests failed after 3 attempts" - exit 1 - fi - echo "Retrying..." - sleep 5 - fi - done +# run-e2e-test: +# name: ${{ matrix.test-file }} / Python ${{ matrix.python-version }} +# needs: +# - find-tests +# - pull-docker-image +# runs-on: ubuntu-latest +# timeout-minutes: 45 +# strategy: +# fail-fast: false # Allow other matrix jobs to run even if this job fails +# max-parallel: 32 # Set the maximum number of parallel jobs (same as we have cores in ubuntu-latest runner) +# matrix: +# os: +# - ubuntu-latest +# test-file: ${{ fromJson(needs.find-tests.outputs.test-files) }} +# python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] +# steps: +# - name: Check-out repository +# uses: actions/checkout@v4 +# +# - name: Set up Python ${{ matrix.python-version }} +# uses: actions/setup-python@v5 +# with: +# python-version: ${{ matrix.python-version }} +# +# - name: Install uv +# uses: astral-sh/setup-uv@v4 +# +# - name: install dependencies +# run: uv sync --extra dev --dev +# +# - name: Download Cached Docker Image +# uses: actions/download-artifact@v4 +# with: +# name: subtensor-localnet +# +# - name: Load Docker Image +# run: docker load -i subtensor-localnet.tar +# +## - name: Run tests +## run: uv run pytest ${{ matrix.test-file }} -s +# +# - name: Run tests with retry +# run: | +# set +e +# for i in 1 2 3; do +# echo "🔁 Attempt $i: Running tests" +# uv run pytest ${{ matrix.test-file }} -s +# status=$? +# if [ $status -eq 0 ]; then +# echo "✅ Tests passed on attempt $i" +# break +# else +# echo "❌ Tests failed on attempt $i" +# if [ $i -eq 3 ]; then +# echo "Tests failed after 3 attempts" +# exit 1 +# fi +# echo "Retrying..." +# sleep 5 +# fi +# done # `build-patched-image` and `cron-e2e-test-with-non-fast-block` should run once per week build-patched-image: @@ -134,7 +134,7 @@ jobs: runs-on: ubuntu-latest outputs: - image-tag: ${{ steps.set-tag.outputs.tag }} + image_name: ${{ steps.set-image.outputs.image_name }} steps: # - name: Check if today is Saturday @@ -154,19 +154,19 @@ jobs: git checkout devnet-ready sed -i 's|7 \* 24 \* 60 \* 60 / 12 // 7 days|5 // Only 5 blocks for tests|' runtime/src/lib.rs - - name: Build Docker image - run: | - cd subtensor - docker build -t subtensor-patched-devnet . - - - name: Save Docker image - run: docker save subtensor-patched-devnet -o subtensor-patched-localnet.tar - - - name: Upload image as artifact - uses: actions/upload-artifact@v4 - with: - name: subtensor-patched-localnet - path: subtensor-patched-localnet.tar +# - name: Build Docker image +# run: | +# cd subtensor +# docker build -t subtensor-patched-devnet . +# +# - name: Save Docker image +# run: docker save subtensor-patched-devnet -o subtensor-patched-localnet.tar +# +# - name: Upload image as artifact +# uses: actions/upload-artifact@v4 +# with: +# name: subtensor-patched-localnet +# path: subtensor-patched-localnet.tar cron-e2e-test-with-non-fast-block: @@ -211,9 +211,11 @@ jobs: - name: Run patched E2E tests env: - LOCALNET_IMAGE_NAME: ${{ needs.build-patched-image.outputs.image_name }} + LOCALNET_IMAGE_NAME: "subtensor-patched-localnet" FAST_BLOCKS: "False" run: | + echo "LOCALNET_IMAGE_NAME=${LOCALNET_IMAGE_NAME}" + echo "FAST_BLOCKS=${FAST_BLOCKS}" set +e for i in 1 2 3; do echo "🔁 Attempt $i: Running tests" From 2c86707b323452d31a1cccc6bd375433d0fcba45 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 2 May 2025 18:59:06 -0700 Subject: [PATCH 184/251] one more --- .github/workflows/e2e-subtensor-tests.yaml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/e2e-subtensor-tests.yaml b/.github/workflows/e2e-subtensor-tests.yaml index d3acc3185f..a2038b17cb 100644 --- a/.github/workflows/e2e-subtensor-tests.yaml +++ b/.github/workflows/e2e-subtensor-tests.yaml @@ -67,7 +67,7 @@ jobs: # Job to run tests in parallel # run-e2e-test: -# name: ${{ matrix.test-file }} / Python ${{ matrix.python-version }} +# name: "FB: ${{ matrix.test-file }} / Python ${{ matrix.python-version }}" # needs: # - find-tests # - pull-docker-image @@ -201,13 +201,13 @@ jobs: - name: install dependencies run: uv sync --extra dev --dev - - name: Download Cached Docker Image - uses: actions/download-artifact@v4 - with: - name: subtensor-patched-localnet - - - name: Load Docker image - run: docker load -i subtensor-patched-localnet.tar +# - name: Download Cached Docker Image +# uses: actions/download-artifact@v4 +# with: +# name: subtensor-patched-localnet +# +# - name: Load Docker image +# run: docker load -i subtensor-patched-localnet.tar - name: Run patched E2E tests env: From 4748ede79a40a7bfc7a4c1d20f4c3cd57a4270f9 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 2 May 2025 19:02:15 -0700 Subject: [PATCH 185/251] real test --- .github/workflows/e2e-subtensor-tests.yaml | 162 ++++++++++----------- 1 file changed, 81 insertions(+), 81 deletions(-) diff --git a/.github/workflows/e2e-subtensor-tests.yaml b/.github/workflows/e2e-subtensor-tests.yaml index a2038b17cb..0dde199b3f 100644 --- a/.github/workflows/e2e-subtensor-tests.yaml +++ b/.github/workflows/e2e-subtensor-tests.yaml @@ -66,67 +66,67 @@ jobs: path: subtensor-localnet.tar # Job to run tests in parallel -# run-e2e-test: -# name: "FB: ${{ matrix.test-file }} / Python ${{ matrix.python-version }}" -# needs: -# - find-tests -# - pull-docker-image -# runs-on: ubuntu-latest -# timeout-minutes: 45 -# strategy: -# fail-fast: false # Allow other matrix jobs to run even if this job fails -# max-parallel: 32 # Set the maximum number of parallel jobs (same as we have cores in ubuntu-latest runner) -# matrix: -# os: -# - ubuntu-latest -# test-file: ${{ fromJson(needs.find-tests.outputs.test-files) }} -# python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] -# steps: -# - name: Check-out repository -# uses: actions/checkout@v4 -# -# - name: Set up Python ${{ matrix.python-version }} -# uses: actions/setup-python@v5 -# with: -# python-version: ${{ matrix.python-version }} -# -# - name: Install uv -# uses: astral-sh/setup-uv@v4 -# -# - name: install dependencies -# run: uv sync --extra dev --dev -# -# - name: Download Cached Docker Image -# uses: actions/download-artifact@v4 -# with: -# name: subtensor-localnet -# -# - name: Load Docker Image -# run: docker load -i subtensor-localnet.tar -# -## - name: Run tests -## run: uv run pytest ${{ matrix.test-file }} -s -# -# - name: Run tests with retry -# run: | -# set +e -# for i in 1 2 3; do -# echo "🔁 Attempt $i: Running tests" -# uv run pytest ${{ matrix.test-file }} -s -# status=$? -# if [ $status -eq 0 ]; then -# echo "✅ Tests passed on attempt $i" -# break -# else -# echo "❌ Tests failed on attempt $i" -# if [ $i -eq 3 ]; then -# echo "Tests failed after 3 attempts" -# exit 1 -# fi -# echo "Retrying..." -# sleep 5 -# fi -# done + run-e2e-test: + name: "FB: ${{ matrix.test-file }} / Python ${{ matrix.python-version }}" + needs: + - find-tests + - pull-docker-image + runs-on: ubuntu-latest + timeout-minutes: 45 + strategy: + fail-fast: false # Allow other matrix jobs to run even if this job fails + max-parallel: 32 # Set the maximum number of parallel jobs (same as we have cores in ubuntu-latest runner) + matrix: + os: + - ubuntu-latest + test-file: ${{ fromJson(needs.find-tests.outputs.test-files) }} + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + steps: + - name: Check-out repository + uses: actions/checkout@v4 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install uv + uses: astral-sh/setup-uv@v4 + + - name: install dependencies + run: uv sync --extra dev --dev + + - name: Download Cached Docker Image + uses: actions/download-artifact@v4 + with: + name: subtensor-localnet + + - name: Load Docker Image + run: docker load -i subtensor-localnet.tar + +# - name: Run tests +# run: uv run pytest ${{ matrix.test-file }} -s + + - name: Run tests with retry + run: | + set +e + for i in 1 2 3; do + echo "🔁 Attempt $i: Running tests" + uv run pytest ${{ matrix.test-file }} -s + status=$? + if [ $status -eq 0 ]; then + echo "✅ Tests passed on attempt $i" + break + else + echo "❌ Tests failed on attempt $i" + if [ $i -eq 3 ]; then + echo "Tests failed after 3 attempts" + exit 1 + fi + echo "Retrying..." + sleep 5 + fi + done # `build-patched-image` and `cron-e2e-test-with-non-fast-block` should run once per week build-patched-image: @@ -154,19 +154,19 @@ jobs: git checkout devnet-ready sed -i 's|7 \* 24 \* 60 \* 60 / 12 // 7 days|5 // Only 5 blocks for tests|' runtime/src/lib.rs -# - name: Build Docker image -# run: | -# cd subtensor -# docker build -t subtensor-patched-devnet . -# -# - name: Save Docker image -# run: docker save subtensor-patched-devnet -o subtensor-patched-localnet.tar -# -# - name: Upload image as artifact -# uses: actions/upload-artifact@v4 -# with: -# name: subtensor-patched-localnet -# path: subtensor-patched-localnet.tar + - name: Build Docker image + run: | + cd subtensor + docker build -t subtensor-patched-devnet . + + - name: Save Docker image + run: docker save subtensor-patched-devnet -o subtensor-patched-localnet.tar + + - name: Upload image as artifact + uses: actions/upload-artifact@v4 + with: + name: subtensor-patched-localnet + path: subtensor-patched-localnet.tar cron-e2e-test-with-non-fast-block: @@ -201,13 +201,13 @@ jobs: - name: install dependencies run: uv sync --extra dev --dev -# - name: Download Cached Docker Image -# uses: actions/download-artifact@v4 -# with: -# name: subtensor-patched-localnet -# -# - name: Load Docker image -# run: docker load -i subtensor-patched-localnet.tar + - name: Download Cached Docker Image + uses: actions/download-artifact@v4 + with: + name: subtensor-patched-localnet + + - name: Load Docker image + run: docker load -i subtensor-patched-localnet.tar - name: Run patched E2E tests env: From 68c3472129f0bb488715bc54816ec3303d3bbd70 Mon Sep 17 00:00:00 2001 From: Roman Date: Mon, 5 May 2025 15:45:39 -0500 Subject: [PATCH 186/251] rename argument legacy_methods --- bittensor/core/subtensor_api/__init__.py | 12 ++++++------ bittensor/core/subtensor_api/utils.py | 2 +- tests/unit_tests/test_subtensor_api.py | 6 +++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/bittensor/core/subtensor_api/__init__.py b/bittensor/core/subtensor_api/__init__.py index de0170eee6..914f9e7d85 100644 --- a/bittensor/core/subtensor_api/__init__.py +++ b/bittensor/core/subtensor_api/__init__.py @@ -11,7 +11,7 @@ from .queries import Queries as _Queries from .stakes import Stakes as _Stakes from .subnets import Subnets as _Subnets -from .utils import add_classic_fields as _add_classic_fields +from .utils import add_legacy_methods as _add_classic_fields from .wallets import Wallets as _Wallets if TYPE_CHECKING: @@ -26,7 +26,7 @@ class SubtensorApi: config: Bittensor configuration object. Defaults to `None`. log_verbose: If `True`, sets the subtensor to log verbosely. Defaults to `False`. async_subtensor: If `True`, uses the async subtensor to create the connection. Defaults to `False`. - backward_compatibility: If `True`, all methods from the Subtensor class will be added to the root level of this class. + legacy_methods: If `True`, all methods from the Subtensor class will be added to the root level of this class. Example: # sync version @@ -46,10 +46,10 @@ class SubtensorApi: print(await subtensor.delegates.get_delegate_identities()) print(await subtensor.chain.tx_rate_limit()) - # using `backward_compatibility` + # using `legacy_methods` import bittensor as bt - subtensor = bt.SubtensorApi(backward_compatibility=True) + subtensor = bt.SubtensorApi(legacy_methods=True) print(subtensor.bonds(0)) """ @@ -59,7 +59,7 @@ def __init__( config: Optional["Config"] = None, log_verbose: bool = False, async_subtensor: bool = False, - backward_compatibility: bool = False, + legacy_methods: bool = False, _mock: bool = False, ): self.network = network @@ -90,7 +90,7 @@ def __init__( self.wait_for_block = self._subtensor.wait_for_block # adds all Subtensor methods into main level os SubtensorApi class - if backward_compatibility: + if legacy_methods: _add_classic_fields(self) def _get_subtensor(self) -> Union["_Subtensor", "_AsyncSubtensor"]: diff --git a/bittensor/core/subtensor_api/utils.py b/bittensor/core/subtensor_api/utils.py index d7643c6c0d..5d8783f6e6 100644 --- a/bittensor/core/subtensor_api/utils.py +++ b/bittensor/core/subtensor_api/utils.py @@ -4,7 +4,7 @@ from bittensor.core.subtensor_api import SubtensorApi -def add_classic_fields(subtensor: "SubtensorApi"): +def add_legacy_methods(subtensor: "SubtensorApi"): """If SubtensorApi get `subtensor_fields=True` arguments, then all classic Subtensor fields added to root level.""" subtensor.add_stake = subtensor._subtensor.add_stake subtensor.add_stake_multiple = subtensor._subtensor.add_stake_multiple diff --git a/tests/unit_tests/test_subtensor_api.py b/tests/unit_tests/test_subtensor_api.py index c97378b427..f92ce48b40 100644 --- a/tests/unit_tests/test_subtensor_api.py +++ b/tests/unit_tests/test_subtensor_api.py @@ -57,13 +57,13 @@ def test_properties_methods_comparable(other_class: "Subtensor" = None): ) -def test__methods_comparable_with_passed_backward_compatibility( +def test__methods_comparable_with_passed_legacy_methods( other_class: "Subtensor" = None, ): - """Verifies that methods in SubtensorApi contains all Subtensors methods if `backward_compatibility=True` is passed.""" + """Verifies that methods in SubtensorApi contains all Subtensors methods if `legacy_methods=True` is passed.""" # Preps subtensor = other_class(_mock=True) if other_class else Subtensor(_mock=True) - subtensor_api = SubtensorApi(_mock=True, backward_compatibility=True) + subtensor_api = SubtensorApi(_mock=True, legacy_methods=True) subtensor_methods = [m for m in dir(subtensor) if not m.startswith("_")] subtensor_api_methods = [m for m in dir(subtensor_api) if not m.startswith("_")] From ad63f95af2730531f49260dbe51ae5fb1b882ccf Mon Sep 17 00:00:00 2001 From: Roman Date: Mon, 5 May 2025 16:22:13 -0500 Subject: [PATCH 187/251] update workflow --- .github/workflows/e2e-subtensor-tests.yaml | 61 ++++------------------ 1 file changed, 9 insertions(+), 52 deletions(-) diff --git a/.github/workflows/e2e-subtensor-tests.yaml b/.github/workflows/e2e-subtensor-tests.yaml index 0dde199b3f..f6f7bbfadc 100644 --- a/.github/workflows/e2e-subtensor-tests.yaml +++ b/.github/workflows/e2e-subtensor-tests.yaml @@ -66,7 +66,7 @@ jobs: path: subtensor-localnet.tar # Job to run tests in parallel - run-e2e-test: + run-fast-blocks-e2e-test: name: "FB: ${{ matrix.test-file }} / Python ${{ matrix.python-version }}" needs: - find-tests @@ -128,54 +128,14 @@ jobs: fi done -# `build-patched-image` and `cron-e2e-test-with-non-fast-block` should run once per week - build-patched-image: -# if: github.event_name == 'schedule' - runs-on: ubuntu-latest - - outputs: - image_name: ${{ steps.set-image.outputs.image_name }} - - steps: -# - name: Check if today is Saturday -# id: set-status -# run: | -# day=$(date -u +%u) -# echo "Today is weekday $day" -# if [ "$day" -ne 6 ]; then -# echo "image_built=false" >> $GITHUB_OUTPUT -# else -# echo "image_built=true" >> $GITHUB_OUTPUT - - - name: Clone + Patch (set 5 blocks instead of 7 days) - run: | - git clone https://github.com/opentensor/subtensor.git - cd subtensor - git checkout devnet-ready - sed -i 's|7 \* 24 \* 60 \* 60 / 12 // 7 days|5 // Only 5 blocks for tests|' runtime/src/lib.rs - - - name: Build Docker image - run: | - cd subtensor - docker build -t subtensor-patched-devnet . - - - name: Save Docker image - run: docker save subtensor-patched-devnet -o subtensor-patched-localnet.tar - - - name: Upload image as artifact - uses: actions/upload-artifact@v4 - with: - name: subtensor-patched-localnet - path: subtensor-patched-localnet.tar - - - cron-e2e-test-with-non-fast-block: + cron-run-non-fast-blocks-e2e-test: # if: github.event_name == 'schedule' && needs.build-patched-image.outputs.image_built == 'true' name: "NFB: ${{ matrix.test-file }} / Python ${{ matrix.python-version }}" - needs: [find-tests, build-patched-image] - + needs: + - find-tests + - pull-docker-image runs-on: ubuntu-latest - timeout-minutes: 300 + timeout-minutes: 1440 strategy: fail-fast: false # Allow other matrix jobs to run even if this job fails @@ -204,18 +164,15 @@ jobs: - name: Download Cached Docker Image uses: actions/download-artifact@v4 with: - name: subtensor-patched-localnet + name: subtensor-localnet - - name: Load Docker image - run: docker load -i subtensor-patched-localnet.tar + - name: Load Docker Image + run: docker load -i subtensor-localnet.tar - name: Run patched E2E tests env: - LOCALNET_IMAGE_NAME: "subtensor-patched-localnet" FAST_BLOCKS: "False" run: | - echo "LOCALNET_IMAGE_NAME=${LOCALNET_IMAGE_NAME}" - echo "FAST_BLOCKS=${FAST_BLOCKS}" set +e for i in 1 2 3; do echo "🔁 Attempt $i: Running tests" From f42b945230a55961f76a44772ac77f860783251e Mon Sep 17 00:00:00 2001 From: Roman Date: Mon, 5 May 2025 16:22:27 -0500 Subject: [PATCH 188/251] update conftest --- tests/e2e_tests/conftest.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/e2e_tests/conftest.py b/tests/e2e_tests/conftest.py index 550232ed2a..95d74fe871 100644 --- a/tests/e2e_tests/conftest.py +++ b/tests/e2e_tests/conftest.py @@ -195,8 +195,6 @@ def stop_existing_test_containers(): container_name = f"{CONTAINER_NAME_PREFIX}{str(time.time()).replace('.', '_')}" - localnet_image_name = os.getenv("LOCALNET_IMAGE_NAME") - # Command to start container cmds = [ "docker", @@ -208,7 +206,7 @@ def stop_existing_test_containers(): "9944:9944", "-p", "9945:9945", - str(localnet_image_name) if localnet_image_name else LOCALNET_IMAGE_NAME, + str(LOCALNET_IMAGE_NAME), ] cmds += params.split() if params else [] From a63d38a9372567d90e7b74e026c4d2280855407a Mon Sep 17 00:00:00 2001 From: Roman Date: Mon, 5 May 2025 16:23:00 -0500 Subject: [PATCH 189/251] add is_fast_block() method --- bittensor/core/async_subtensor.py | 4 ++++ bittensor/core/subtensor.py | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index c9b05ceef2..abb9cb4c18 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -2435,6 +2435,10 @@ async def immunity_period( ) return None if call is None else int(call) + async def is_fast_blocks(self): + """Returns True if the node is running with fast blocks. False if not.""" + return (await self.query_constant("SubtensorModule", "DurationOfStartCall")).value == 10 + async def is_hotkey_delegate( self, hotkey_ss58: str, diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 800790ea40..89a7492633 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -1933,6 +1933,10 @@ def immunity_period( ) return None if call is None else int(call) + def is_fast_blocks(self): + """Returns True if the node is running with fast blocks. False if not.""" + return self.query_constant("SubtensorModule", "DurationOfStartCall").value == 10 + def is_hotkey_delegate(self, hotkey_ss58: str, block: Optional[int] = None) -> bool: """ Determines whether a given hotkey (public key) is a delegate on the Bittensor network. This function checks if From a65410569b67ea2f510ca4bab8aa59d86f64f450 Mon Sep 17 00:00:00 2001 From: Roman Date: Mon, 5 May 2025 16:23:33 -0500 Subject: [PATCH 190/251] ruff --- bittensor/core/async_subtensor.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index abb9cb4c18..f0d819b0d1 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -2437,7 +2437,9 @@ async def immunity_period( async def is_fast_blocks(self): """Returns True if the node is running with fast blocks. False if not.""" - return (await self.query_constant("SubtensorModule", "DurationOfStartCall")).value == 10 + return ( + await self.query_constant("SubtensorModule", "DurationOfStartCall") + ).value == 10 async def is_hotkey_delegate( self, From b93cffa49296699c80c9d2907843666561cffabf Mon Sep 17 00:00:00 2001 From: Maximilian Hubert <64627729+gap-editor@users.noreply.github.com> Date: Mon, 5 May 2025 23:28:55 +0200 Subject: [PATCH 191/251] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 953ee9e5e8..4c07d1a739 100644 --- a/README.md +++ b/README.md @@ -239,18 +239,18 @@ pytest tests/unit_tests - are executed very quickly - require docker installed in OS -Ho to use: +How to use: ```bash pytest tests/e2e_tests ``` -#### TUsing `legacy runner`: +#### Using `legacy runner`: - Will start compilation of the collected code in your subtensor repository - you must provide the `LOCALNET_SH_PATH` variable in the local environment with the path to the file `/scripts/localnet.sh` in the cloned repository within your OS - you can use the `BUILD_BINARY=0` variable, this will skip the copy step for each test. - you can use the `USE_DOCKER=0` variable, this will run tests using the "legacy runner", even if docker is installed in your OS -#### Ho to use: +#### How to use: Regular e2e tests run ```bash LOCALNET_SH_PATH=/path/to/your/localnet.sh pytest tests/e2e_tests From 1fc420c4d10118d93d6ad6ea5a17e49a741fc75a Mon Sep 17 00:00:00 2001 From: Roman Date: Mon, 5 May 2025 16:55:01 -0500 Subject: [PATCH 192/251] improve `tests.e2e_tests.utils.e2e_test_utils.wait_to_start_call` --- tests/e2e_tests/utils/e2e_test_utils.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/e2e_tests/utils/e2e_test_utils.py b/tests/e2e_tests/utils/e2e_test_utils.py index a688156c7b..00d2045030 100644 --- a/tests/e2e_tests/utils/e2e_test_utils.py +++ b/tests/e2e_tests/utils/e2e_test_utils.py @@ -235,6 +235,9 @@ def wait_to_start_call( in_blocks: int = 10, ): """Waits for a certain number of blocks before making a start call.""" + if subtensor.is_fast_blocks() is False: + in_blocks = 5 + # make sure we passed start_call limit subtensor.wait_for_block(subtensor.block + in_blocks + 1) status, message = subtensor.start_call( @@ -254,6 +257,9 @@ async def async_wait_to_start_call( in_blocks: int = 10, ): """Waits for a certain number of blocks before making a start call.""" + if await subtensor.is_fast_blocks() is False: + in_blocks = 5 + # make sure we passed start_call limit current_block = await subtensor.block await subtensor.wait_for_block(current_block + in_blocks + 1) From ae3c8866fd77a20d4764daaf1caba9c55fbbe841 Mon Sep 17 00:00:00 2001 From: Roman Date: Mon, 5 May 2025 16:55:47 -0500 Subject: [PATCH 193/251] Update tests to be compatible with non-/fast-blocks localnet nodes --- tests/e2e_tests/test_commit_reveal_v3.py | 9 +++++---- tests/e2e_tests/test_commit_weights.py | 16 +++++++++------- tests/e2e_tests/test_commitment.py | 10 +++------- tests/e2e_tests/test_metagraph.py | 4 ++-- tests/e2e_tests/test_reveal_commitments.py | 4 ++-- tests/e2e_tests/test_root_set_weights.py | 2 +- 6 files changed, 22 insertions(+), 23 deletions(-) diff --git a/tests/e2e_tests/test_commit_reveal_v3.py b/tests/e2e_tests/test_commit_reveal_v3.py index 6fdf02101f..2dc8b44b86 100644 --- a/tests/e2e_tests/test_commit_reveal_v3.py +++ b/tests/e2e_tests/test_commit_reveal_v3.py @@ -30,7 +30,7 @@ async def test_commit_and_reveal_weights_cr3(local_chain, subtensor, alice_walle Raises: AssertionError: If any of the checks or verifications fail """ - BLOCK_TIME = 0.25 # 12 for non-fast-block, 0.25 for fast block + BLOCK_TIME = 0.25 if subtensor.is_fast_blocks() else 12.0 # 12 for non-fast-block, 0.25 for fast block netuid = 2 logging.console.info("Testing test_commit_and_reveal_weights") @@ -74,7 +74,7 @@ async def test_commit_and_reveal_weights_cr3(local_chain, subtensor, alice_walle logging.console.info("sudo_set_weights_set_rate_limit executed: set to 0") # Change the tempo of the subnet - tempo_set = 50 + tempo_set = 50 if subtensor.is_fast_blocks() else 10 assert ( sudo_set_admin_utils( local_chain, @@ -103,7 +103,7 @@ async def test_commit_and_reveal_weights_cr3(local_chain, subtensor, alice_walle ) # Wait for 2 tempos to pass as CR3 only reveals weights after 2 tempos + 1 - subtensor.wait_for_block((tempo_set * 2) + 1) + subtensor.wait_for_block(subtensor.block + (tempo_set * 2) + 1) # Lower than this might mean weights will get revealed before we can check them if upcoming_tempo - current_block < 3: @@ -117,7 +117,7 @@ async def test_commit_and_reveal_weights_cr3(local_chain, subtensor, alice_walle latest_drand_round = subtensor.last_drand_round() upcoming_tempo = next_tempo(current_block, tempo) logging.console.info( - f"Post first wait_interval (to ensure window isnt too low): {current_block}, next tempo: {upcoming_tempo}, drand: {latest_drand_round}" + f"Post first wait_interval (to ensure window isn't too low): {current_block}, next tempo: {upcoming_tempo}, drand: {latest_drand_round}" ) # Commit weights @@ -171,6 +171,7 @@ async def test_commit_and_reveal_weights_cr3(local_chain, subtensor, alice_walle subtensor, netuid=netuid, reporting_interval=1, + sleep=BLOCK_TIME ) # Fetch the latest drand pulse diff --git a/tests/e2e_tests/test_commit_weights.py b/tests/e2e_tests/test_commit_weights.py index d3dabf6152..661dd16af0 100644 --- a/tests/e2e_tests/test_commit_weights.py +++ b/tests/e2e_tests/test_commit_weights.py @@ -27,7 +27,7 @@ async def test_commit_and_reveal_weights_legacy(local_chain, subtensor, alice_wa AssertionError: If any of the checks or verifications fail """ netuid = subtensor.get_total_subnets() # 2 - + set_tempo = 100 if subtensor.is_fast_block else 10 print("Testing test_commit_and_reveal_weights") # Register root as Alice @@ -78,7 +78,7 @@ async def test_commit_and_reveal_weights_legacy(local_chain, subtensor, alice_wa call_function="sudo_set_tempo", call_params={ "netuid": netuid, - "tempo": 100, + "tempo": set_tempo, }, ) @@ -165,11 +165,11 @@ async def test_commit_weights_uses_next_nonce(local_chain, subtensor, alice_wall Raises: AssertionError: If any of the checks or verifications fail """ - subnet_tempo = 50 + subnet_tempo = 50 if subtensor.is_fast_block else 10 netuid = subtensor.get_total_subnets() # 2 # Wait for 2 tempos to pass as CR3 only reveals weights after 2 tempos - subtensor.wait_for_block(subnet_tempo * 2 + 1) + subtensor.wait_for_block(subtensor.block + (subnet_tempo * 2) + 1) print("Testing test_commit_and_reveal_weights") # Register root as Alice @@ -267,8 +267,9 @@ def send_commit(salt_, weight_uids_, weight_vals_): send_commit(salt, weight_uids, weight_vals) - # let's wait for 3 (12 fast blocks) seconds between transactions - subtensor.wait_for_block(subtensor.block + 12) + # let's wait for 3 (12 fast blocks) seconds between transactions, next block for non-fast-blocks + waiting_block = (subtensor.block + 12) if subtensor.is_fast_blocks() else None + subtensor.wait_for_block(waiting_block) logging.console.info( f"[orange]Nonce after third commit_weights: " @@ -276,7 +277,8 @@ def send_commit(salt_, weight_uids_, weight_vals_): ) # Wait a few blocks - subtensor.wait_for_block(subtensor.block + subtensor.tempo(netuid) * 2) + waiting_block = (subtensor.block + subtensor.tempo(netuid) * 2) if subtensor.is_fast_blocks() else None + subtensor.wait_for_block(waiting_block) # Query the WeightCommits storage map for all three salts weight_commits = subtensor.query_module( diff --git a/tests/e2e_tests/test_commitment.py b/tests/e2e_tests/test_commitment.py index e4704c8d8f..885e6298ef 100644 --- a/tests/e2e_tests/test_commitment.py +++ b/tests/e2e_tests/test_commitment.py @@ -3,7 +3,7 @@ from bittensor import logging from tests.e2e_tests.utils.chain_interactions import sudo_set_admin_utils -from tests.e2e_tests.utils.e2e_test_utils import wait_to_start_call +from tests.e2e_tests.utils.e2e_test_utils import wait_to_start_call, async_wait_to_start_call logging.set_trace() @@ -15,7 +15,7 @@ def test_commitment(local_chain, subtensor, alice_wallet, dave_wallet): "Subnet wasn't created successfully" ) - assert wait_to_start_call(subtensor, dave_wallet, dave_subnet_netuid, 10) + assert wait_to_start_call(subtensor, dave_wallet, dave_subnet_netuid) with pytest.raises(SubstrateRequestException, match="AccountNotAllowedCommit"): subtensor.set_commitment( @@ -93,11 +93,7 @@ async def test_commitment_async( "Subnet wasn't created successfully" ) - await async_subtensor.wait_for_block(await async_subtensor.block + 20) - status, message = await async_subtensor.start_call( - dave_wallet, dave_subnet_netuid, True, True - ) - assert status, message + assert async_wait_to_start_call(async_subtensor, dave_wallet, dave_subnet_netuid) async with async_subtensor as sub: with pytest.raises(SubstrateRequestException, match="AccountNotAllowedCommit"): diff --git a/tests/e2e_tests/test_metagraph.py b/tests/e2e_tests/test_metagraph.py index 104c2ffceb..108fc0448a 100644 --- a/tests/e2e_tests/test_metagraph.py +++ b/tests/e2e_tests/test_metagraph.py @@ -210,7 +210,7 @@ def test_metagraph_info(subtensor, alice_wallet, bob_wallet): owner_hotkey="5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM", owner_coldkey="5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM", block=1, - tempo=100, + tempo=100 if subtensor.is_fast_blocks() else 100, last_step=0, blocks_since_last_step=1, subnet_emission=Balance(0), @@ -303,7 +303,7 @@ def test_metagraph_info(subtensor, alice_wallet, bob_wallet): owner_hotkey="5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM", owner_coldkey="5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM", block=1, - tempo=100, + tempo=100 if subtensor.is_fast_blocks() else 100, last_step=0, blocks_since_last_step=1, subnet_emission=Balance(0), diff --git a/tests/e2e_tests/test_reveal_commitments.py b/tests/e2e_tests/test_reveal_commitments.py index 40cc8794a1..fa535d5352 100644 --- a/tests/e2e_tests/test_reveal_commitments.py +++ b/tests/e2e_tests/test_reveal_commitments.py @@ -26,8 +26,8 @@ async def test_set_reveal_commitment(local_chain, subtensor, alice_wallet, bob_w Note: Actually we can run this tests in fast block mode. For this we need to set `BLOCK_TIME` to 0.25 and replace `False` to `True` in `pytest.mark.parametrize` decorator. """ - BLOCK_TIME = 0.25 # 12 for non-fast-block, 0.25 for fast block - BLOCKS_UNTIL_REVEAL = 10 + BLOCK_TIME = 0.25 if subtensor.is_fast_blocks() else 12.0 # 12 for non-fast-block, 0.25 for fast block + BLOCKS_UNTIL_REVEAL = 10 if subtensor.is_fast_blocks() else 5 alice_subnet_netuid = subtensor.get_total_subnets() # 2 diff --git a/tests/e2e_tests/test_root_set_weights.py b/tests/e2e_tests/test_root_set_weights.py index f399a071b3..cc9ee21ad7 100644 --- a/tests/e2e_tests/test_root_set_weights.py +++ b/tests/e2e_tests/test_root_set_weights.py @@ -62,7 +62,7 @@ async def test_root_reg_hyperparams( # Default immunity period & tempo set through the subtensor side default_immunity_period = 5000 - default_tempo = 10 + default_tempo = 10 if subtensor.is_fast_blocks() else 100 # 0.2 for root network, 0.8 for sn 1 # Corresponding to [0.2, 0.8] From f0672757ec616ab9dfbf95f2ebfa5f187df05b84 Mon Sep 17 00:00:00 2001 From: Roman Date: Mon, 5 May 2025 17:04:41 -0500 Subject: [PATCH 194/251] fix FAST_BLOCKS=0 for non-fast-blocks docker image --- .github/workflows/e2e-subtensor-tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/e2e-subtensor-tests.yaml b/.github/workflows/e2e-subtensor-tests.yaml index f6f7bbfadc..e73f2a3179 100644 --- a/.github/workflows/e2e-subtensor-tests.yaml +++ b/.github/workflows/e2e-subtensor-tests.yaml @@ -171,7 +171,7 @@ jobs: - name: Run patched E2E tests env: - FAST_BLOCKS: "False" + FAST_BLOCKS: "0" run: | set +e for i in 1 2 3; do From a9e4da20a36ea4cd1bf688da36ebd2bf04002a53 Mon Sep 17 00:00:00 2001 From: Roman Date: Mon, 5 May 2025 17:12:55 -0500 Subject: [PATCH 195/251] ruff + fix typos --- tests/e2e_tests/test_commit_reveal_v3.py | 6 ++++-- tests/e2e_tests/test_commit_weights.py | 10 +++++++--- tests/e2e_tests/test_commitment.py | 5 ++++- tests/e2e_tests/test_reveal_commitments.py | 4 +++- 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/tests/e2e_tests/test_commit_reveal_v3.py b/tests/e2e_tests/test_commit_reveal_v3.py index 2dc8b44b86..a2d3ce2a1d 100644 --- a/tests/e2e_tests/test_commit_reveal_v3.py +++ b/tests/e2e_tests/test_commit_reveal_v3.py @@ -30,7 +30,9 @@ async def test_commit_and_reveal_weights_cr3(local_chain, subtensor, alice_walle Raises: AssertionError: If any of the checks or verifications fail """ - BLOCK_TIME = 0.25 if subtensor.is_fast_blocks() else 12.0 # 12 for non-fast-block, 0.25 for fast block + BLOCK_TIME = ( + 0.25 if subtensor.is_fast_blocks() else 12.0 + ) # 12 for non-fast-block, 0.25 for fast block netuid = 2 logging.console.info("Testing test_commit_and_reveal_weights") @@ -171,7 +173,7 @@ async def test_commit_and_reveal_weights_cr3(local_chain, subtensor, alice_walle subtensor, netuid=netuid, reporting_interval=1, - sleep=BLOCK_TIME + sleep=BLOCK_TIME, ) # Fetch the latest drand pulse diff --git a/tests/e2e_tests/test_commit_weights.py b/tests/e2e_tests/test_commit_weights.py index 661dd16af0..7f98d8a6f6 100644 --- a/tests/e2e_tests/test_commit_weights.py +++ b/tests/e2e_tests/test_commit_weights.py @@ -27,7 +27,7 @@ async def test_commit_and_reveal_weights_legacy(local_chain, subtensor, alice_wa AssertionError: If any of the checks or verifications fail """ netuid = subtensor.get_total_subnets() # 2 - set_tempo = 100 if subtensor.is_fast_block else 10 + set_tempo = 100 if subtensor.is_fast_blocks() else 10 print("Testing test_commit_and_reveal_weights") # Register root as Alice @@ -165,7 +165,7 @@ async def test_commit_weights_uses_next_nonce(local_chain, subtensor, alice_wall Raises: AssertionError: If any of the checks or verifications fail """ - subnet_tempo = 50 if subtensor.is_fast_block else 10 + subnet_tempo = 50 if subtensor.is_fast_blocks() else 10 netuid = subtensor.get_total_subnets() # 2 # Wait for 2 tempos to pass as CR3 only reveals weights after 2 tempos @@ -277,7 +277,11 @@ def send_commit(salt_, weight_uids_, weight_vals_): ) # Wait a few blocks - waiting_block = (subtensor.block + subtensor.tempo(netuid) * 2) if subtensor.is_fast_blocks() else None + waiting_block = ( + (subtensor.block + subtensor.tempo(netuid) * 2) + if subtensor.is_fast_blocks() + else None + ) subtensor.wait_for_block(waiting_block) # Query the WeightCommits storage map for all three salts diff --git a/tests/e2e_tests/test_commitment.py b/tests/e2e_tests/test_commitment.py index 885e6298ef..617822af4e 100644 --- a/tests/e2e_tests/test_commitment.py +++ b/tests/e2e_tests/test_commitment.py @@ -3,7 +3,10 @@ from bittensor import logging from tests.e2e_tests.utils.chain_interactions import sudo_set_admin_utils -from tests.e2e_tests.utils.e2e_test_utils import wait_to_start_call, async_wait_to_start_call +from tests.e2e_tests.utils.e2e_test_utils import ( + wait_to_start_call, + async_wait_to_start_call, +) logging.set_trace() diff --git a/tests/e2e_tests/test_reveal_commitments.py b/tests/e2e_tests/test_reveal_commitments.py index fa535d5352..865664a9a9 100644 --- a/tests/e2e_tests/test_reveal_commitments.py +++ b/tests/e2e_tests/test_reveal_commitments.py @@ -26,7 +26,9 @@ async def test_set_reveal_commitment(local_chain, subtensor, alice_wallet, bob_w Note: Actually we can run this tests in fast block mode. For this we need to set `BLOCK_TIME` to 0.25 and replace `False` to `True` in `pytest.mark.parametrize` decorator. """ - BLOCK_TIME = 0.25 if subtensor.is_fast_blocks() else 12.0 # 12 for non-fast-block, 0.25 for fast block + BLOCK_TIME = ( + 0.25 if subtensor.is_fast_blocks() else 12.0 + ) # 12 for non-fast-block, 0.25 for fast block BLOCKS_UNTIL_REVEAL = 10 if subtensor.is_fast_blocks() else 5 alice_subnet_netuid = subtensor.get_total_subnets() # 2 From 448ba63a9cb3f1f0e31bcd34cf77c44259bf8fe5 Mon Sep 17 00:00:00 2001 From: Roman Date: Mon, 5 May 2025 17:35:57 -0500 Subject: [PATCH 196/251] temporary python-version ["3.12"] only + fix bugs --- .github/workflows/e2e-subtensor-tests.yaml | 6 ++++-- tests/e2e_tests/test_commitment.py | 4 +++- tests/e2e_tests/test_metagraph.py | 4 ++-- tests/e2e_tests/test_root_set_weights.py | 2 +- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/.github/workflows/e2e-subtensor-tests.yaml b/.github/workflows/e2e-subtensor-tests.yaml index e73f2a3179..b5e891c809 100644 --- a/.github/workflows/e2e-subtensor-tests.yaml +++ b/.github/workflows/e2e-subtensor-tests.yaml @@ -80,7 +80,8 @@ jobs: os: - ubuntu-latest test-file: ${{ fromJson(needs.find-tests.outputs.test-files) }} - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] +# python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + python-version: ["3.12"] steps: - name: Check-out repository uses: actions/checkout@v4 @@ -144,7 +145,8 @@ jobs: os: - ubuntu-latest test-file: ${{ fromJson(needs.find-tests.outputs.test-files) }} - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] +# python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + python-version: ["3.12"] steps: - name: Check-out repository diff --git a/tests/e2e_tests/test_commitment.py b/tests/e2e_tests/test_commitment.py index 617822af4e..a6a33f98c2 100644 --- a/tests/e2e_tests/test_commitment.py +++ b/tests/e2e_tests/test_commitment.py @@ -96,7 +96,9 @@ async def test_commitment_async( "Subnet wasn't created successfully" ) - assert async_wait_to_start_call(async_subtensor, dave_wallet, dave_subnet_netuid) + assert await async_wait_to_start_call( + async_subtensor, dave_wallet, dave_subnet_netuid + ) async with async_subtensor as sub: with pytest.raises(SubstrateRequestException, match="AccountNotAllowedCommit"): diff --git a/tests/e2e_tests/test_metagraph.py b/tests/e2e_tests/test_metagraph.py index 108fc0448a..104c2ffceb 100644 --- a/tests/e2e_tests/test_metagraph.py +++ b/tests/e2e_tests/test_metagraph.py @@ -210,7 +210,7 @@ def test_metagraph_info(subtensor, alice_wallet, bob_wallet): owner_hotkey="5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM", owner_coldkey="5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM", block=1, - tempo=100 if subtensor.is_fast_blocks() else 100, + tempo=100, last_step=0, blocks_since_last_step=1, subnet_emission=Balance(0), @@ -303,7 +303,7 @@ def test_metagraph_info(subtensor, alice_wallet, bob_wallet): owner_hotkey="5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM", owner_coldkey="5C4hrfjw9DjXZTzV3MwzrrAr9P1MJhSrvWGWqi1eSuyUpnhM", block=1, - tempo=100 if subtensor.is_fast_blocks() else 100, + tempo=100, last_step=0, blocks_since_last_step=1, subnet_emission=Balance(0), diff --git a/tests/e2e_tests/test_root_set_weights.py b/tests/e2e_tests/test_root_set_weights.py index cc9ee21ad7..5973a144cb 100644 --- a/tests/e2e_tests/test_root_set_weights.py +++ b/tests/e2e_tests/test_root_set_weights.py @@ -62,7 +62,7 @@ async def test_root_reg_hyperparams( # Default immunity period & tempo set through the subtensor side default_immunity_period = 5000 - default_tempo = 10 if subtensor.is_fast_blocks() else 100 + default_tempo = 10 if subtensor.is_fast_blocks() else 360 # 0.2 for root network, 0.8 for sn 1 # Corresponding to [0.2, 0.8] From 9f1150579fd037b4720c2784726af4f0be474fc2 Mon Sep 17 00:00:00 2001 From: Roman Date: Mon, 5 May 2025 18:02:11 -0500 Subject: [PATCH 197/251] fix --- tests/e2e_tests/test_commit_reveal_v3.py | 9 +++++---- tests/e2e_tests/test_reveal_commitments.py | 3 ++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/tests/e2e_tests/test_commit_reveal_v3.py b/tests/e2e_tests/test_commit_reveal_v3.py index a2d3ce2a1d..0820486a3d 100644 --- a/tests/e2e_tests/test_commit_reveal_v3.py +++ b/tests/e2e_tests/test_commit_reveal_v3.py @@ -33,16 +33,17 @@ async def test_commit_and_reveal_weights_cr3(local_chain, subtensor, alice_walle BLOCK_TIME = ( 0.25 if subtensor.is_fast_blocks() else 12.0 ) # 12 for non-fast-block, 0.25 for fast block - netuid = 2 + netuid = subtensor.get_total_subnets() # 2 + logging.console.info("Testing test_commit_and_reveal_weights") # Register root as Alice assert subtensor.register_subnet(alice_wallet), "Unable to register the subnet" # Verify subnet 2 created successfully - assert subtensor.subnet_exists(netuid), "Subnet wasn't created successfully" + assert subtensor.subnet_exists(netuid), f"Subnet {netuid} wasn't created successfully" - logging.console.info("Subnet 2 is registered") + logging.console.success(f"Subnet {netuid} is registered") # Enable commit_reveal on the subnet assert sudo_set_hyperparameter_bool( @@ -105,7 +106,7 @@ async def test_commit_and_reveal_weights_cr3(local_chain, subtensor, alice_walle ) # Wait for 2 tempos to pass as CR3 only reveals weights after 2 tempos + 1 - subtensor.wait_for_block(subtensor.block + (tempo_set * 2) + 1) + subtensor.wait_for_block(tempo_set * 2 + 1) # Lower than this might mean weights will get revealed before we can check them if upcoming_tempo - current_block < 3: diff --git a/tests/e2e_tests/test_reveal_commitments.py b/tests/e2e_tests/test_reveal_commitments.py index 865664a9a9..c4757e24cb 100644 --- a/tests/e2e_tests/test_reveal_commitments.py +++ b/tests/e2e_tests/test_reveal_commitments.py @@ -81,7 +81,8 @@ async def test_set_reveal_commitment(local_chain, subtensor, alice_wallet, bob_w # Sometimes the chain doesn't update the repository right away and the commit doesn't appear in the expected # `last_drand_round`. In this case need to wait a bit. print(f"Waiting for reveal round {target_reveal_round}") - while subtensor.last_drand_round() <= target_reveal_round + 1: + chain_offset = 1 if subtensor.is_fast_blocks() else 24 + while subtensor.last_drand_round() <= target_reveal_round + chain_offset: # wait one drand period (3 sec) print(f"Current last reveled drand round {subtensor.last_drand_round()}") time.sleep(3) From dbf506a147930056d1bf05966f2db6cb127201af Mon Sep 17 00:00:00 2001 From: Roman Date: Mon, 5 May 2025 18:33:18 -0500 Subject: [PATCH 198/251] ruff --- tests/e2e_tests/test_commit_reveal_v3.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/e2e_tests/test_commit_reveal_v3.py b/tests/e2e_tests/test_commit_reveal_v3.py index 0820486a3d..719dbe5827 100644 --- a/tests/e2e_tests/test_commit_reveal_v3.py +++ b/tests/e2e_tests/test_commit_reveal_v3.py @@ -41,7 +41,9 @@ async def test_commit_and_reveal_weights_cr3(local_chain, subtensor, alice_walle assert subtensor.register_subnet(alice_wallet), "Unable to register the subnet" # Verify subnet 2 created successfully - assert subtensor.subnet_exists(netuid), f"Subnet {netuid} wasn't created successfully" + assert subtensor.subnet_exists(netuid), ( + f"Subnet {netuid} wasn't created successfully" + ) logging.console.success(f"Subnet {netuid} is registered") From 1adaa20ef0269691f66c9b74a8333816c2602558 Mon Sep 17 00:00:00 2001 From: Roman Date: Mon, 5 May 2025 18:34:08 -0500 Subject: [PATCH 199/251] all python-version back --- .github/workflows/e2e-subtensor-tests.yaml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/e2e-subtensor-tests.yaml b/.github/workflows/e2e-subtensor-tests.yaml index b5e891c809..e73f2a3179 100644 --- a/.github/workflows/e2e-subtensor-tests.yaml +++ b/.github/workflows/e2e-subtensor-tests.yaml @@ -80,8 +80,7 @@ jobs: os: - ubuntu-latest test-file: ${{ fromJson(needs.find-tests.outputs.test-files) }} -# python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] - python-version: ["3.12"] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] steps: - name: Check-out repository uses: actions/checkout@v4 @@ -145,8 +144,7 @@ jobs: os: - ubuntu-latest test-file: ${{ fromJson(needs.find-tests.outputs.test-files) }} -# python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] - python-version: ["3.12"] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] steps: - name: Check-out repository From 370e0a0f542ec773db8ce49da1f5b66f00e559e9 Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 6 May 2025 11:05:34 -0500 Subject: [PATCH 200/251] add logging to wait_to_start_call --- tests/e2e_tests/utils/e2e_test_utils.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/e2e_tests/utils/e2e_test_utils.py b/tests/e2e_tests/utils/e2e_test_utils.py index 00d2045030..745c8977ab 100644 --- a/tests/e2e_tests/utils/e2e_test_utils.py +++ b/tests/e2e_tests/utils/e2e_test_utils.py @@ -237,6 +237,10 @@ def wait_to_start_call( """Waits for a certain number of blocks before making a start call.""" if subtensor.is_fast_blocks() is False: in_blocks = 5 + bittensor.logging.console.info( + f"Waiting for [blue]{in_blocks}[/blue] blocks before [red]start call[/red]. " + f"Current block: [blue]{subtensor.block}[/blue]." + ) # make sure we passed start_call limit subtensor.wait_for_block(subtensor.block + in_blocks + 1) @@ -260,6 +264,11 @@ async def async_wait_to_start_call( if await subtensor.is_fast_blocks() is False: in_blocks = 5 + bittensor.logging.console.info( + f"Waiting for [blue]{in_blocks}[/blue] blocks before [red]start call[/red]. " + f"Current block: [blue]{subtensor.block}[/blue]." + ) + # make sure we passed start_call limit current_block = await subtensor.block await subtensor.wait_for_block(current_block + in_blocks + 1) From 6667aec82af32e9553f9d4ca16cb736acc8880f5 Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 6 May 2025 11:06:09 -0500 Subject: [PATCH 201/251] make `test_delegates` compatible with both modes --- tests/e2e_tests/test_delegate.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/tests/e2e_tests/test_delegate.py b/tests/e2e_tests/test_delegate.py index c66691351d..c3157c9db1 100644 --- a/tests/e2e_tests/test_delegate.py +++ b/tests/e2e_tests/test_delegate.py @@ -171,7 +171,7 @@ def test_change_take(local_chain, subtensor, alice_wallet, bob_wallet): @pytest.mark.asyncio -async def test_delegates(subtensor, alice_wallet, bob_wallet): +async def test_delegates(local_chain, subtensor, alice_wallet, bob_wallet): """ Tests: - Check default Delegates @@ -240,6 +240,7 @@ async def test_delegates(subtensor, alice_wallet, bob_wallet): assert subtensor.get_delegated(bob_wallet.coldkey.ss58_address) == [] alice_subnet_netuid = subtensor.get_total_subnets() # 2 + set_tempo = 10 # Register a subnet, netuid 2 assert subtensor.register_subnet(alice_wallet), "Subnet wasn't created" @@ -250,6 +251,17 @@ async def test_delegates(subtensor, alice_wallet, bob_wallet): assert wait_to_start_call(subtensor, alice_wallet, alice_subnet_netuid) + # set the same tempo for both type of nodes (fast and non-fast blocks) + assert ( + sudo_set_admin_utils( + local_chain, + alice_wallet, + call_function="sudo_set_tempo", + call_params={"netuid": alice_subnet_netuid, "tempo": set_tempo}, + )[0] + is True + ) + subtensor.add_stake( bob_wallet, alice_wallet.hotkey.ss58_address, @@ -259,6 +271,9 @@ async def test_delegates(subtensor, alice_wallet, bob_wallet): wait_for_finalization=True, ) + # let chain update validator_permits + subtensor.wait_for_block(subtensor.block + set_tempo + 1) + bob_delegated = subtensor.get_delegated(bob_wallet.coldkey.ss58_address) assert bob_delegated == [ DelegatedInfo( @@ -272,9 +287,10 @@ async def test_delegates(subtensor, alice_wallet, bob_wallet): bob_delegated[0].total_daily_return.rao ), netuid=alice_subnet_netuid, - stake=get_dynamic_balance(bob_delegated[0].stake.rao), + stake=get_dynamic_balance(bob_delegated[0].stake.rao, alice_subnet_netuid), ), ] + bittensor.logging.console.success("Test [green]test_delegates[/green] passed.") def test_nominator_min_required_stake(local_chain, subtensor, alice_wallet, bob_wallet): From 6b4dbf496257b9c084d5cd74175f87ab7a136282 Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 6 May 2025 11:30:34 -0500 Subject: [PATCH 202/251] update `test_hotkeys.py` --- tests/e2e_tests/test_hotkeys.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/tests/e2e_tests/test_hotkeys.py b/tests/e2e_tests/test_hotkeys.py index f28f4c07f9..1cd4bea943 100644 --- a/tests/e2e_tests/test_hotkeys.py +++ b/tests/e2e_tests/test_hotkeys.py @@ -18,7 +18,7 @@ def test_hotkeys(subtensor, alice_wallet, dave_wallet): - Check if Hotkey is registered """ - dave_subnet_netuid = 2 + dave_subnet_netuid = subtensor.get_total_subnets() # 2 assert subtensor.register_subnet(dave_wallet, True, True) assert subtensor.subnet_exists(dave_subnet_netuid), ( f"Subnet #{dave_subnet_netuid} does not exist." @@ -76,7 +76,8 @@ async def test_children(local_chain, subtensor, alice_wallet, bob_wallet, dave_w - Clear children list """ - dave_subnet_netuid = 2 + dave_subnet_netuid = subtensor.get_total_subnets() # 2 + set_tempo = 10 assert subtensor.register_subnet(dave_wallet, True, True) assert subtensor.subnet_exists(dave_subnet_netuid), ( f"Subnet #{dave_subnet_netuid} does not exist." @@ -84,6 +85,17 @@ async def test_children(local_chain, subtensor, alice_wallet, bob_wallet, dave_w assert wait_to_start_call(subtensor, dave_wallet, dave_subnet_netuid) + # set the same tempo for both type of nodes (to avoid tests timeout) + assert ( + sudo_set_admin_utils( + local_chain, + alice_wallet, + call_function="sudo_set_tempo", + call_params={"netuid": dave_subnet_netuid, "tempo": set_tempo}, + )[0] + is True + ) + with pytest.raises(bittensor.RegistrationNotPermittedOnRootSubnet): subtensor.set_children( alice_wallet, @@ -235,10 +247,11 @@ async def test_children(local_chain, subtensor, alice_wallet, bob_wallet, dave_w "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", ), ] + bittensor.logging.console.info(f"cooldown: {cooldown}") subtensor.wait_for_block(cooldown) - await wait_epoch(subtensor, netuid=1) + await wait_epoch(subtensor, netuid=dave_subnet_netuid) success, children, error = subtensor.get_children( alice_wallet.hotkey.ss58_address, From e85e2694dfe6795c15bb844e7cd3b0ddb0bcec99 Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 6 May 2025 15:32:06 -0500 Subject: [PATCH 203/251] improve subtensors --- bittensor/core/async_subtensor.py | 44 +++++++++++++++++++++++-- bittensor/core/subtensor.py | 54 ++++++++++++++++++++++++------- 2 files changed, 84 insertions(+), 14 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index c9b05ceef2..955467b8da 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -9,6 +9,7 @@ import numpy as np import scalecodec from async_substrate_interface import AsyncSubstrateInterface +from async_substrate_interface.substrate_addons import RetryAsyncSubstrate from bittensor_drand import get_encrypted_commitment from bittensor_wallet.utils import SS58_FORMAT from numpy.typing import NDArray @@ -114,8 +115,10 @@ def __init__( self, network: Optional[str] = None, config: Optional["Config"] = None, - _mock: bool = False, log_verbose: bool = False, + fallback_chains: Optional[list[str]] = None, + retry_forever: bool = False, + _mock: bool = False, ): """ Initializes an instance of the AsyncSubtensor class. @@ -123,8 +126,10 @@ def __init__( Arguments: network (str): The network name or type to connect to. config (Optional[Config]): Configuration object for the AsyncSubtensor instance. - _mock: Whether this is a mock instance. Mainly just for use in testing. log_verbose (bool): Enables or disables verbose logging. + fallback_chains (list): List of fallback chains to use if no network is specified. Defaults to `None`. + retry_forever (bool): Whether to retry forever on connection errors. Defaults to `False`. + _mock: Whether this is a mock instance. Mainly just for use in testing. Raises: Any exceptions raised during the setup, configuration, or connection process. @@ -151,6 +156,9 @@ def __init__( chain_name="Bittensor", _mock=_mock, ) + self.substrate = self._get_substrate( + fallback_chains=fallback_chains, retry_forever=retry_forever, _mock=_mock + ) if self.log_verbose: logging.info( f"Connected to {self.network} network and {self.chain_endpoint}." @@ -283,6 +291,38 @@ async def get_hyperparameter( return getattr(result, "value", result) + def _get_substrate( + self, + fallback_chains: Optional[list[str]] = None, + retry_forever: bool = False, + _mock: bool = False, + ) -> Union[AsyncSubstrateInterface, RetryAsyncSubstrate]: + """Creates the Substrate instance based on provided arguments. + + Arguments: + fallback_chains (list): List of fallback chains to use if no network is specified. Defaults to `None`. + retry_forever (bool): Whether to retry forever on connection errors. Defaults to `False`. + _mock: Whether this is a mock instance. Mainly just for use in testing. + + Returns: + the instance of the SubstrateInterface or RetrySyncSubstrate class. + """ + if fallback_chains or retry_forever: + return RetryAsyncSubstrate( + url=self.chain_endpoint, + fallback_chains=fallback_chains, + retry_forever=retry_forever, + _mock=_mock, + ) + return AsyncSubstrateInterface( + url=self.chain_endpoint, + ss58_format=SS58_FORMAT, + type_registry=TYPE_REGISTRY, + use_remote_preset=True, + chain_name="Bittensor", + _mock=_mock, + ) + # Subtensor queries =========================================================================================== async def query_constant( diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 800790ea40..9b71686ef8 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -6,6 +6,7 @@ import numpy as np import scalecodec from async_substrate_interface.errors import SubstrateRequestException +from async_substrate_interface.substrate_addons import RetrySyncSubstrate from async_substrate_interface.sync_substrate import SubstrateInterface from async_substrate_interface.types import ScaleObj from bittensor_drand import get_encrypted_commitment @@ -116,8 +117,10 @@ def __init__( self, network: Optional[str] = None, config: Optional["Config"] = None, - _mock: bool = False, log_verbose: bool = False, + fallback_chains: Optional[list[str]] = None, + retry_forever: bool = False, + _mock: bool = False, ): """ Initializes an instance of the Subtensor class. @@ -125,8 +128,10 @@ def __init__( Arguments: network (str): The network name or type to connect to. config (Optional[Config]): Configuration object for the AsyncSubtensor instance. - _mock: Whether this is a mock instance. Mainly just for use in testing. log_verbose (bool): Enables or disables verbose logging. + fallback_chains (list): List of fallback chains to use if no network is specified. Defaults to `None`. + retry_forever (bool): Whether to retry forever on connection errors. Defaults to `False`. + _mock: Whether this is a mock instance. Mainly just for use in testing. Raises: Any exceptions raised during the setup, configuration, or connection process. @@ -143,13 +148,8 @@ def __init__( f"Connecting to network: [blue]{self.network}[/blue], " f"chain_endpoint: [blue]{self.chain_endpoint}[/blue]> ..." ) - self.substrate = SubstrateInterface( - url=self.chain_endpoint, - ss58_format=SS58_FORMAT, - type_registry=TYPE_REGISTRY, - use_remote_preset=True, - chain_name="Bittensor", - _mock=_mock, + self.substrate = self._get_substrate( + fallback_chains=fallback_chains, retry_forever=retry_forever, _mock=_mock ) if self.log_verbose: logging.info( @@ -163,11 +163,41 @@ def __exit__(self, exc_type, exc_val, exc_tb): self.close() def close(self): - """ - Closes the websocket connection - """ + """Closes the websocket connection.""" self.substrate.close() + def _get_substrate( + self, + fallback_chains: Optional[list[str]] = None, + retry_forever: bool = False, + _mock: bool = False, + ) -> Union[SubstrateInterface, RetrySyncSubstrate]: + """Creates the Substrate instance based on provided arguments. + + Arguments: + fallback_chains (list): List of fallback chains to use if no network is specified. Defaults to `None`. + retry_forever (bool): Whether to retry forever on connection errors. Defaults to `False`. + _mock: Whether this is a mock instance. Mainly just for use in testing. + + Returns: + the instance of the SubstrateInterface or RetrySyncSubstrate class. + """ + if fallback_chains or retry_forever: + return RetrySyncSubstrate( + url=self.chain_endpoint, + fallback_chains=fallback_chains, + retry_forever=retry_forever, + _mock=_mock, + ) + return SubstrateInterface( + url=self.chain_endpoint, + ss58_format=SS58_FORMAT, + type_registry=TYPE_REGISTRY, + use_remote_preset=True, + chain_name="Bittensor", + _mock=_mock, + ) + # Subtensor queries =========================================================================================== def query_constant( From b3c71596449569c5a7993cea28afd479558d65a9 Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 6 May 2025 15:33:33 -0500 Subject: [PATCH 204/251] Update SubtensorApi --- bittensor/core/subtensor_api/__init__.py | 49 ++++++++++++++++++++---- 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/bittensor/core/subtensor_api/__init__.py b/bittensor/core/subtensor_api/__init__.py index 914f9e7d85..f760a87a7c 100644 --- a/bittensor/core/subtensor_api/__init__.py +++ b/bittensor/core/subtensor_api/__init__.py @@ -24,9 +24,11 @@ class SubtensorApi: Arguments: network: The network to connect to. Defaults to `None` -> `finney`. config: Bittensor configuration object. Defaults to `None`. - log_verbose: If `True`, sets the subtensor to log verbosely. Defaults to `False`. - async_subtensor: If `True`, uses the async subtensor to create the connection. Defaults to `False`. legacy_methods: If `True`, all methods from the Subtensor class will be added to the root level of this class. + fallback_chains (list): List of fallback chains to use if no network is specified. Defaults to `None`. + retry_forever (bool): Whether to retry forever on connection errors. Defaults to `False`. + log_verbose (bool): Enables or disables verbose logging. + mock: Whether this is a mock instance. Mainly just for use in testing. Example: # sync version @@ -57,16 +59,21 @@ def __init__( self, network: Optional[str] = None, config: Optional["Config"] = None, - log_verbose: bool = False, async_subtensor: bool = False, legacy_methods: bool = False, - _mock: bool = False, + fallback_chains: Optional[list[str]] = None, + retry_forever: bool = False, + log_verbose: bool = False, + mock: bool = False, ): self.network = network - self._mock = _mock + self._fallback_chains = fallback_chains + self._retry_forever = retry_forever + self._mock = mock self.log_verbose = log_verbose self.is_async = async_subtensor self._config = config + # assigned only for async instance self.initialize = None self._subtensor = self._get_subtensor() @@ -99,8 +106,10 @@ def _get_subtensor(self) -> Union["_Subtensor", "_AsyncSubtensor"]: _subtensor = _AsyncSubtensor( network=self.network, config=self._config, - _mock=self._mock, log_verbose=self.log_verbose, + fallback_chains=self._fallback_chains, + retry_forever=self._retry_forever, + _mock=self._mock, ) self.initialize = _subtensor.initialize return _subtensor @@ -108,12 +117,18 @@ def _get_subtensor(self) -> Union["_Subtensor", "_AsyncSubtensor"]: return _Subtensor( network=self.network, config=self._config, - _mock=self._mock, log_verbose=self.log_verbose, + fallback_chains=self._fallback_chains, + retry_forever=self._retry_forever, + _mock=self._mock, ) def __str__(self): - return f"" + return ( + f"" + ) def __repr__(self): return self.__str__() @@ -130,50 +145,68 @@ async def __aenter__(self): async def __aexit__(self, exc_type, exc_val, exc_tb): await self.substrate.close() + def _determine_chain_endpoint(self) -> str: + """Determines the connection and mock flag.""" + if self._mock: + return "Mock" + return self.substrate.url + @property def block(self): + """Returns current chain block number.""" return self._subtensor.block @property def chain(self): + """Property to access chain methods.""" return _Chain(self._subtensor) @property def commitments(self): + """Property to access commitments methods.""" return _Commitments(self._subtensor) @property def delegates(self): + """Property to access delegates methods.""" return _Delegates(self._subtensor) @property def extrinsics(self): + """Property to access extrinsics methods.""" return _Extrinsics(self._subtensor) @property def metagraphs(self): + """Property to access metagraphs methods.""" return _Metagraphs(self._subtensor) @property def neurons(self): + """Property to access neurons methods.""" return self._neurons @neurons.setter def neurons(self, value): + """Setter for neurons property.""" self._neurons = value @property def queries(self): + """Property to access queries methods.""" return _Queries(self._subtensor) @property def stakes(self): + """Property to access stakes methods.""" return _Stakes(self._subtensor) @property def subnets(self): + """Property to access subnets methods.""" return _Subnets(self._subtensor) @property def wallets(self): + """Property to access wallets methods.""" return _Wallets(self._subtensor) From 329ce85291fc7432ac0cb506716a641bedb3dd17 Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 6 May 2025 15:33:38 -0500 Subject: [PATCH 205/251] fix tests --- tests/unit_tests/test_subtensor_api.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/unit_tests/test_subtensor_api.py b/tests/unit_tests/test_subtensor_api.py index f92ce48b40..4078cb5a96 100644 --- a/tests/unit_tests/test_subtensor_api.py +++ b/tests/unit_tests/test_subtensor_api.py @@ -7,7 +7,7 @@ def test_properties_methods_comparable(other_class: "Subtensor" = None): """Verifies that methods in SubtensorApi and its properties contains all Subtensors methods.""" # Preps subtensor = other_class(_mock=True) if other_class else Subtensor(_mock=True) - subtensor_api = SubtensorApi(_mock=True) + subtensor_api = SubtensorApi(mock=True) subtensor_methods = [m for m in dir(subtensor) if not m.startswith("_")] @@ -62,8 +62,8 @@ def test__methods_comparable_with_passed_legacy_methods( ): """Verifies that methods in SubtensorApi contains all Subtensors methods if `legacy_methods=True` is passed.""" # Preps - subtensor = other_class(_mock=True) if other_class else Subtensor(_mock=True) - subtensor_api = SubtensorApi(_mock=True, legacy_methods=True) + subtensor = other_class(mock=True) if other_class else Subtensor(_mock=True) + subtensor_api = SubtensorApi(mock=True, legacy_methods=True) subtensor_methods = [m for m in dir(subtensor) if not m.startswith("_")] subtensor_api_methods = [m for m in dir(subtensor_api) if not m.startswith("_")] From b470d788ad257b3df75b8b34ec62b02ea214c368 Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 6 May 2025 15:42:08 -0500 Subject: [PATCH 206/251] update docstrings, fix bug --- bittensor/core/async_subtensor.py | 12 ++---------- bittensor/core/subtensor.py | 4 ++-- bittensor/core/subtensor_api/__init__.py | 17 +++++++++++------ 3 files changed, 15 insertions(+), 18 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 955467b8da..2cda6446d1 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -127,7 +127,7 @@ def __init__( network (str): The network name or type to connect to. config (Optional[Config]): Configuration object for the AsyncSubtensor instance. log_verbose (bool): Enables or disables verbose logging. - fallback_chains (list): List of fallback chains to use if no network is specified. Defaults to `None`. + fallback_chains (list): List of fallback chains endpoints to use if no network is specified. Defaults to `None`. retry_forever (bool): Whether to retry forever on connection errors. Defaults to `False`. _mock: Whether this is a mock instance. Mainly just for use in testing. @@ -148,14 +148,6 @@ def __init__( f"Connecting to network: [blue]{self.network}[/blue], " f"chain_endpoint: [blue]{self.chain_endpoint}[/blue]..." ) - self.substrate = AsyncSubstrateInterface( - url=self.chain_endpoint, - ss58_format=SS58_FORMAT, - type_registry=TYPE_REGISTRY, - use_remote_preset=True, - chain_name="Bittensor", - _mock=_mock, - ) self.substrate = self._get_substrate( fallback_chains=fallback_chains, retry_forever=retry_forever, _mock=_mock ) @@ -300,7 +292,7 @@ def _get_substrate( """Creates the Substrate instance based on provided arguments. Arguments: - fallback_chains (list): List of fallback chains to use if no network is specified. Defaults to `None`. + fallback_chains (list): List of fallback chains endpoints to use if no network is specified. Defaults to `None`. retry_forever (bool): Whether to retry forever on connection errors. Defaults to `False`. _mock: Whether this is a mock instance. Mainly just for use in testing. diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 9b71686ef8..e315205e0e 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -129,7 +129,7 @@ def __init__( network (str): The network name or type to connect to. config (Optional[Config]): Configuration object for the AsyncSubtensor instance. log_verbose (bool): Enables or disables verbose logging. - fallback_chains (list): List of fallback chains to use if no network is specified. Defaults to `None`. + fallback_chains (list): List of fallback chains endpoints to use if no network is specified. Defaults to `None`. retry_forever (bool): Whether to retry forever on connection errors. Defaults to `False`. _mock: Whether this is a mock instance. Mainly just for use in testing. @@ -175,7 +175,7 @@ def _get_substrate( """Creates the Substrate instance based on provided arguments. Arguments: - fallback_chains (list): List of fallback chains to use if no network is specified. Defaults to `None`. + fallback_chains (list): List of fallback chains endpoints to use if no network is specified. Defaults to `None`. retry_forever (bool): Whether to retry forever on connection errors. Defaults to `False`. _mock: Whether this is a mock instance. Mainly just for use in testing. diff --git a/bittensor/core/subtensor_api/__init__.py b/bittensor/core/subtensor_api/__init__.py index f760a87a7c..84ad91a124 100644 --- a/bittensor/core/subtensor_api/__init__.py +++ b/bittensor/core/subtensor_api/__init__.py @@ -53,6 +53,11 @@ class SubtensorApi: subtensor = bt.SubtensorApi(legacy_methods=True) print(subtensor.bonds(0)) + + # using `fallback_chains` or `retry_forever` + import bittensor as bt + + """ def __init__( @@ -123,6 +128,12 @@ def _get_subtensor(self) -> Union["_Subtensor", "_AsyncSubtensor"]: _mock=self._mock, ) + def _determine_chain_endpoint(self) -> str: + """Determines the connection and mock flag.""" + if self._mock: + return "Mock" + return self.substrate.url + def __str__(self): return ( f" str: - """Determines the connection and mock flag.""" - if self._mock: - return "Mock" - return self.substrate.url - @property def block(self): """Returns current chain block number.""" From 04d4c425a9ec034ae2e4135bddc85e358281ee25 Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 6 May 2025 16:10:08 -0500 Subject: [PATCH 207/251] move add_args --- bittensor/core/subtensor_api/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/bittensor/core/subtensor_api/__init__.py b/bittensor/core/subtensor_api/__init__.py index 84ad91a124..2b24ad2f6b 100644 --- a/bittensor/core/subtensor_api/__init__.py +++ b/bittensor/core/subtensor_api/__init__.py @@ -88,7 +88,6 @@ def __init__( # define empty fields self.substrate = self._subtensor.substrate - self.add_args = self._subtensor.add_args self.chain_endpoint = self._subtensor.chain_endpoint self.close = self._subtensor.close self.config = self._subtensor.config @@ -156,6 +155,10 @@ async def __aenter__(self): async def __aexit__(self, exc_type, exc_val, exc_tb): await self.substrate.close() + @classmethod + def add_args(cls, parser): + _Subtensor.add_args(parser) + @property def block(self): """Returns current chain block number.""" From 2093abf995e551c2da911e01a96481fd7fe4f2b1 Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 6 May 2025 17:17:18 -0500 Subject: [PATCH 208/251] fix docstrings --- bittensor/core/subtensor_api/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bittensor/core/subtensor_api/__init__.py b/bittensor/core/subtensor_api/__init__.py index 2b24ad2f6b..b88609fbac 100644 --- a/bittensor/core/subtensor_api/__init__.py +++ b/bittensor/core/subtensor_api/__init__.py @@ -166,7 +166,7 @@ def block(self): @property def chain(self): - """Property to access chain methods.""" + """Property of interaction with chain methods.""" return _Chain(self._subtensor) @property @@ -201,7 +201,7 @@ def neurons(self, value): @property def queries(self): - """Property to access queries methods.""" + """Property to access subtensor queries methods.""" return _Queries(self._subtensor) @property @@ -211,10 +211,10 @@ def stakes(self): @property def subnets(self): - """Property to access subnets methods.""" + """Property of interaction with subnets methods.""" return _Subnets(self._subtensor) @property def wallets(self): - """Property to access wallets methods.""" + """Property of interaction methods with cold/hotkeys, and balances, etc.""" return _Wallets(self._subtensor) From 32c94a5bc69b7a06aa5c0f0c6fc3eb3792394d66 Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 6 May 2025 20:01:10 -0500 Subject: [PATCH 209/251] trigger --- tests/e2e_tests/test_hotkeys.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/e2e_tests/test_hotkeys.py b/tests/e2e_tests/test_hotkeys.py index 1cd4bea943..6ee1889e38 100644 --- a/tests/e2e_tests/test_hotkeys.py +++ b/tests/e2e_tests/test_hotkeys.py @@ -16,6 +16,7 @@ def test_hotkeys(subtensor, alice_wallet, dave_wallet): Tests: - Check if Hotkey exists - Check if Hotkey is registered + """ dave_subnet_netuid = subtensor.get_total_subnets() # 2 From a6c301ae5a399ddbe96b3b336803218438088f4c Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 7 May 2025 11:27:35 -0500 Subject: [PATCH 210/251] temp run 2 tests for debug --- .github/workflows/e2e-subtensor-tests.yaml | 4 +- tests/e2e_tests/test_hotkeys.py | 56 +++++++++++++++------- 2 files changed, 41 insertions(+), 19 deletions(-) diff --git a/.github/workflows/e2e-subtensor-tests.yaml b/.github/workflows/e2e-subtensor-tests.yaml index e73f2a3179..a2dc8246fe 100644 --- a/.github/workflows/e2e-subtensor-tests.yaml +++ b/.github/workflows/e2e-subtensor-tests.yaml @@ -41,9 +41,9 @@ jobs: - name: Find test files id: get-tests run: | - test_files=$(find tests/e2e_tests -name "test*.py" | jq -R -s -c 'split("\n") | map(select(. != ""))') + # test_files=$(find tests/e2e_tests -name "test*.py" | jq -R -s -c 'split("\n") | map(select(. != ""))') # keep it here for future debug - # test_files=$(find tests/e2e_tests -type f -name "test*.py" | grep -E 'test_(incentive|commit_weights|set_weights)\.py$' | jq -R -s -c 'split("\n") | map(select(. != ""))') + test_files=$(find tests/e2e_tests -type f -name "test*.py" | grep -E 'test_(hotkeys|staking)\.py$' | jq -R -s -c 'split("\n") | map(select(. != ""))') echo "test-files=$test_files" >> "$GITHUB_OUTPUT" shell: bash diff --git a/tests/e2e_tests/test_hotkeys.py b/tests/e2e_tests/test_hotkeys.py index 6ee1889e38..c4a76323fa 100644 --- a/tests/e2e_tests/test_hotkeys.py +++ b/tests/e2e_tests/test_hotkeys.py @@ -1,6 +1,17 @@ import pytest -import bittensor +from bittensor.core.errors import ( + NotEnoughStakeToSetChildkeys, + RegistrationNotPermittedOnRootSubnet, + SubNetworkDoesNotExist, + InvalidChild, + TooManyChildren, + ProportionOverflow, + DuplicateChild, + TxRateLimitExceeded, + NonAssociatedColdKey, +) +from bittensor.utils.btlogging import logging from tests.e2e_tests.utils.chain_interactions import ( sudo_set_admin_utils, wait_epoch, @@ -63,6 +74,7 @@ def test_hotkeys(subtensor, alice_wallet, dave_wallet): ) is True ) + logging.console.success("Hotkey tests passed.") @pytest.mark.asyncio @@ -97,7 +109,17 @@ async def test_children(local_chain, subtensor, alice_wallet, bob_wallet, dave_w is True ) - with pytest.raises(bittensor.RegistrationNotPermittedOnRootSubnet): + # assert ( + # sudo_set_admin_utils( + # local_chain, + # alice_wallet, + # call_function="sudo_set_tx_rate_limit", + # call_params={"tx_rate_limit": 100}, + # )[0] + # is True + # ) + + with pytest.raises(RegistrationNotPermittedOnRootSubnet): subtensor.set_children( alice_wallet, alice_wallet.hotkey.ss58_address, @@ -106,7 +128,7 @@ async def test_children(local_chain, subtensor, alice_wallet, bob_wallet, dave_w raise_error=True, ) - with pytest.raises(bittensor.NonAssociatedColdKey): + with pytest.raises(NonAssociatedColdKey): subtensor.set_children( alice_wallet, alice_wallet.hotkey.ss58_address, @@ -115,7 +137,7 @@ async def test_children(local_chain, subtensor, alice_wallet, bob_wallet, dave_w raise_error=True, ) - with pytest.raises(bittensor.SubNetworkDoesNotExist): + with pytest.raises(SubNetworkDoesNotExist): subtensor.set_children( alice_wallet, alice_wallet.hotkey.ss58_address, @@ -128,10 +150,12 @@ async def test_children(local_chain, subtensor, alice_wallet, bob_wallet, dave_w alice_wallet, netuid=dave_subnet_netuid, ) + logging.console.success(f"Alice registered on subnet {dave_subnet_netuid}") subtensor.burned_register( bob_wallet, netuid=dave_subnet_netuid, ) + logging.console.success(f"Bob registered on subnet {dave_subnet_netuid}") success, children, error = subtensor.get_children( alice_wallet.hotkey.ss58_address, @@ -142,7 +166,7 @@ async def test_children(local_chain, subtensor, alice_wallet, bob_wallet, dave_w assert success is True assert children == [] - with pytest.raises(bittensor.InvalidChild): + with pytest.raises(InvalidChild): subtensor.set_children( alice_wallet, alice_wallet.hotkey.ss58_address, @@ -156,7 +180,7 @@ async def test_children(local_chain, subtensor, alice_wallet, bob_wallet, dave_w raise_error=True, ) - with pytest.raises(bittensor.TooManyChildren): + with pytest.raises(TooManyChildren): subtensor.set_children( alice_wallet, alice_wallet.hotkey.ss58_address, @@ -171,7 +195,7 @@ async def test_children(local_chain, subtensor, alice_wallet, bob_wallet, dave_w raise_error=True, ) - with pytest.raises(bittensor.ProportionOverflow): + with pytest.raises(ProportionOverflow): subtensor.set_children( alice_wallet, alice_wallet.hotkey.ss58_address, @@ -189,7 +213,7 @@ async def test_children(local_chain, subtensor, alice_wallet, bob_wallet, dave_w raise_error=True, ) - with pytest.raises(bittensor.DuplicateChild): + with pytest.raises(DuplicateChild): subtensor.set_children( alice_wallet, alice_wallet.hotkey.ss58_address, @@ -242,13 +266,8 @@ async def test_children(local_chain, subtensor, alice_wallet, bob_wallet, dave_w netuid=dave_subnet_netuid, ) - assert pending == [ - ( - 1.0, - "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty", - ), - ] - bittensor.logging.console.info(f"cooldown: {cooldown}") + assert pending == [(1.0, bob_wallet.hotkey.ss58_address)] + logging.console.info(f"Cooldown 1 is: {cooldown}") subtensor.wait_for_block(cooldown) @@ -276,7 +295,7 @@ async def test_children(local_chain, subtensor, alice_wallet, bob_wallet, dave_w assert pending == [] - with pytest.raises(bittensor.TxRateLimitExceeded): + with pytest.raises(TxRateLimitExceeded): subtensor.set_children( alice_wallet, alice_wallet.hotkey.ss58_address, @@ -301,6 +320,8 @@ async def test_children(local_chain, subtensor, alice_wallet, bob_wallet, dave_w netuid=dave_subnet_netuid, ) + logging.console.info(f"Cooldown 2 is: {cooldown}") + assert pending == [] subtensor.wait_for_block(cooldown) @@ -325,7 +346,7 @@ async def test_children(local_chain, subtensor, alice_wallet, bob_wallet, dave_w }, ) - with pytest.raises(bittensor.NotEnoughStakeToSetChildkeys): + with pytest.raises(NotEnoughStakeToSetChildkeys): subtensor.set_children( alice_wallet, alice_wallet.hotkey.ss58_address, @@ -338,3 +359,4 @@ async def test_children(local_chain, subtensor, alice_wallet, bob_wallet, dave_w ], raise_error=True, ) + logging.console.success("[green]test_children[/green] passed.") From 45d62db9e3174e4c7cd4242b6b1ecc2373b5188e Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 7 May 2025 11:43:50 -0500 Subject: [PATCH 211/251] add logging after tests --- tests/e2e_tests/test_hotkeys.py | 9 +++++++-- tests/e2e_tests/test_staking.py | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/tests/e2e_tests/test_hotkeys.py b/tests/e2e_tests/test_hotkeys.py index c4a76323fa..c02d185937 100644 --- a/tests/e2e_tests/test_hotkeys.py +++ b/tests/e2e_tests/test_hotkeys.py @@ -74,7 +74,9 @@ def test_hotkeys(subtensor, alice_wallet, dave_wallet): ) is True ) - logging.console.success("Hotkey tests passed.") + logging.console.success( + f":white_check_mark: Test [green]test_hotkeys[/green] passed" + ) @pytest.mark.asyncio @@ -359,4 +361,7 @@ async def test_children(local_chain, subtensor, alice_wallet, bob_wallet, dave_w ], raise_error=True, ) - logging.console.success("[green]test_children[/green] passed.") + + logging.console.success( + f":white_check_mark: Test [green]test_children[/green] passed" + ) diff --git a/tests/e2e_tests/test_staking.py b/tests/e2e_tests/test_staking.py index 37bb87e332..00fd907d02 100644 --- a/tests/e2e_tests/test_staking.py +++ b/tests/e2e_tests/test_staking.py @@ -182,6 +182,9 @@ def test_single_operation(subtensor, alice_wallet, bob_wallet): # all balances have been unstaked assert stake == Balance(0).set_unit(alice_subnet_netuid) + logging.console.success( + f":white_check_mark: Test [green]test_single_operation[/green] passed" + ) def test_batch_operations(subtensor, alice_wallet, bob_wallet): @@ -303,6 +306,9 @@ def test_batch_operations(subtensor, alice_wallet, bob_wallet): bob_wallet.coldkey.ss58_address: Balance.from_tao(999_998), } assert balances[alice_wallet.coldkey.ss58_address] > alice_balance + logging.console.success( + f":white_check_mark: Test [green]test_batch_operations[/green] passed" + ) def test_safe_staking_scenarios(subtensor, alice_wallet, bob_wallet): @@ -477,6 +483,9 @@ def test_safe_staking_scenarios(subtensor, alice_wallet, bob_wallet): allow_partial_stake=False, ) assert success is True, "Unstake should succeed" + logging.console.success( + f":white_check_mark: Test [green]test_safe_staking_scenarios[/green] passed" + ) def test_safe_swap_stake_scenarios(subtensor, alice_wallet, bob_wallet): @@ -588,6 +597,9 @@ def test_safe_swap_stake_scenarios(subtensor, alice_wallet, bob_wallet): assert dest_stake > Balance(0), ( "Destination stake should be non-zero after successful swap" ) + logging.console.success( + f":white_check_mark: Test [green]test_safe_swap_stake_scenarios[/green] passed" + ) def test_move_stake(subtensor, alice_wallet, bob_wallet): @@ -679,6 +691,9 @@ def test_move_stake(subtensor, alice_wallet, bob_wallet): is_registered=True, ), ] + logging.console.success( + f":white_check_mark: Test [green]test_move_stake[/green] passed" + ) def test_transfer_stake(subtensor, alice_wallet, bob_wallet, dave_wallet): @@ -789,3 +804,6 @@ def test_transfer_stake(subtensor, alice_wallet, bob_wallet, dave_wallet): is_registered=False, ), ] + logging.console.success( + f":white_check_mark: Test [green]test_transfer_stake[/green] passed" + ) From 6b442a2c02eae877fc2e2f19ffa9c2baf8a767b4 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 7 May 2025 12:09:52 -0500 Subject: [PATCH 212/251] add logging after tests --- tests/e2e_tests/test_hotkeys.py | 8 ++------ tests/e2e_tests/test_staking.py | 20 ++++++-------------- 2 files changed, 8 insertions(+), 20 deletions(-) diff --git a/tests/e2e_tests/test_hotkeys.py b/tests/e2e_tests/test_hotkeys.py index c02d185937..4eb03e8b5a 100644 --- a/tests/e2e_tests/test_hotkeys.py +++ b/tests/e2e_tests/test_hotkeys.py @@ -74,9 +74,7 @@ def test_hotkeys(subtensor, alice_wallet, dave_wallet): ) is True ) - logging.console.success( - f":white_check_mark: Test [green]test_hotkeys[/green] passed" - ) + logging.console.success(f"✅ Test [green]test_hotkeys[/green] passed") @pytest.mark.asyncio @@ -362,6 +360,4 @@ async def test_children(local_chain, subtensor, alice_wallet, bob_wallet, dave_w raise_error=True, ) - logging.console.success( - f":white_check_mark: Test [green]test_children[/green] passed" - ) + logging.console.success(f"✅ Test [green]test_children[/green] passed") diff --git a/tests/e2e_tests/test_staking.py b/tests/e2e_tests/test_staking.py index 00fd907d02..1e54b15de7 100644 --- a/tests/e2e_tests/test_staking.py +++ b/tests/e2e_tests/test_staking.py @@ -182,9 +182,7 @@ def test_single_operation(subtensor, alice_wallet, bob_wallet): # all balances have been unstaked assert stake == Balance(0).set_unit(alice_subnet_netuid) - logging.console.success( - f":white_check_mark: Test [green]test_single_operation[/green] passed" - ) + logging.console.success(f"✅ Test [green]test_single_operation[/green] passed") def test_batch_operations(subtensor, alice_wallet, bob_wallet): @@ -306,9 +304,7 @@ def test_batch_operations(subtensor, alice_wallet, bob_wallet): bob_wallet.coldkey.ss58_address: Balance.from_tao(999_998), } assert balances[alice_wallet.coldkey.ss58_address] > alice_balance - logging.console.success( - f":white_check_mark: Test [green]test_batch_operations[/green] passed" - ) + logging.console.success(f"✅ Test [green]test_batch_operations[/green] passed") def test_safe_staking_scenarios(subtensor, alice_wallet, bob_wallet): @@ -484,7 +480,7 @@ def test_safe_staking_scenarios(subtensor, alice_wallet, bob_wallet): ) assert success is True, "Unstake should succeed" logging.console.success( - f":white_check_mark: Test [green]test_safe_staking_scenarios[/green] passed" + f"✅ Test [green]test_safe_staking_scenarios[/green] passed" ) @@ -598,7 +594,7 @@ def test_safe_swap_stake_scenarios(subtensor, alice_wallet, bob_wallet): "Destination stake should be non-zero after successful swap" ) logging.console.success( - f":white_check_mark: Test [green]test_safe_swap_stake_scenarios[/green] passed" + f"✅ Test [green]test_safe_swap_stake_scenarios[/green] passed" ) @@ -691,9 +687,7 @@ def test_move_stake(subtensor, alice_wallet, bob_wallet): is_registered=True, ), ] - logging.console.success( - f":white_check_mark: Test [green]test_move_stake[/green] passed" - ) + logging.console.success(f"✅ Test [green]test_move_stake[/green] passed") def test_transfer_stake(subtensor, alice_wallet, bob_wallet, dave_wallet): @@ -804,6 +798,4 @@ def test_transfer_stake(subtensor, alice_wallet, bob_wallet, dave_wallet): is_registered=False, ), ] - logging.console.success( - f":white_check_mark: Test [green]test_transfer_stake[/green] passed" - ) + logging.console.success(f"✅ Test [green]test_transfer_stake[/green] passed") From af4be54b0b84907f4aa746bd9b015c9d9261a0c4 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 7 May 2025 15:11:33 -0500 Subject: [PATCH 213/251] fix --- tests/e2e_tests/test_hotkeys.py | 44 ++++++++++++++++----------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/tests/e2e_tests/test_hotkeys.py b/tests/e2e_tests/test_hotkeys.py index 4eb03e8b5a..71623be218 100644 --- a/tests/e2e_tests/test_hotkeys.py +++ b/tests/e2e_tests/test_hotkeys.py @@ -90,7 +90,8 @@ async def test_children(local_chain, subtensor, alice_wallet, bob_wallet, dave_w """ dave_subnet_netuid = subtensor.get_total_subnets() # 2 - set_tempo = 10 + set_tempo = 10 # affect to non-fast-blocks mode + assert subtensor.register_subnet(dave_wallet, True, True) assert subtensor.subnet_exists(dave_subnet_netuid), ( f"Subnet #{dave_subnet_netuid} does not exist." @@ -99,25 +100,26 @@ async def test_children(local_chain, subtensor, alice_wallet, bob_wallet, dave_w assert wait_to_start_call(subtensor, dave_wallet, dave_subnet_netuid) # set the same tempo for both type of nodes (to avoid tests timeout) - assert ( - sudo_set_admin_utils( - local_chain, - alice_wallet, - call_function="sudo_set_tempo", - call_params={"netuid": dave_subnet_netuid, "tempo": set_tempo}, - )[0] - is True - ) + if not subtensor.is_fast_blocks(): + assert ( + sudo_set_admin_utils( + local_chain, + alice_wallet, + call_function="sudo_set_tempo", + call_params={"netuid": dave_subnet_netuid, "tempo": set_tempo}, + )[0] + is True + ) - # assert ( - # sudo_set_admin_utils( - # local_chain, - # alice_wallet, - # call_function="sudo_set_tx_rate_limit", - # call_params={"tx_rate_limit": 100}, - # )[0] - # is True - # ) + # assert ( + # sudo_set_admin_utils( + # local_chain, + # alice_wallet, + # call_function="sudo_set_tx_rate_limit", + # call_params={"tx_rate_limit": 100}, + # )[0] + # is True + # ) with pytest.raises(RegistrationNotPermittedOnRootSubnet): subtensor.set_children( @@ -269,9 +271,7 @@ async def test_children(local_chain, subtensor, alice_wallet, bob_wallet, dave_w assert pending == [(1.0, bob_wallet.hotkey.ss58_address)] logging.console.info(f"Cooldown 1 is: {cooldown}") - subtensor.wait_for_block(cooldown) - - await wait_epoch(subtensor, netuid=dave_subnet_netuid) + subtensor.wait_for_block(cooldown + 1) success, children, error = subtensor.get_children( alice_wallet.hotkey.ss58_address, From 5b05f4de26934ccd556dbc89cba6904ae43eca67 Mon Sep 17 00:00:00 2001 From: Roman <167799377+basfroman@users.noreply.github.com> Date: Wed, 7 May 2025 13:31:32 -0700 Subject: [PATCH 214/251] Update bittensor/core/subtensor.py Co-authored-by: BD Himes <37844818+thewhaleking@users.noreply.github.com> --- bittensor/core/subtensor.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index e315205e0e..e642ad40aa 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -185,6 +185,11 @@ def _get_substrate( if fallback_chains or retry_forever: return RetrySyncSubstrate( url=self.chain_endpoint, + url=self.chain_endpoint, + ss58_format=SS58_FORMAT, + type_registry=TYPE_REGISTRY, + use_remote_preset=True, + chain_name="Bittensor", fallback_chains=fallback_chains, retry_forever=retry_forever, _mock=_mock, From d0243e4e598f956839371d506bdb5b1abff43c7f Mon Sep 17 00:00:00 2001 From: Roman <167799377+basfroman@users.noreply.github.com> Date: Wed, 7 May 2025 13:31:39 -0700 Subject: [PATCH 215/251] Update bittensor/core/subtensor_api/__init__.py Co-authored-by: BD Himes <37844818+thewhaleking@users.noreply.github.com> --- bittensor/core/subtensor_api/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bittensor/core/subtensor_api/__init__.py b/bittensor/core/subtensor_api/__init__.py index b88609fbac..98038d28de 100644 --- a/bittensor/core/subtensor_api/__init__.py +++ b/bittensor/core/subtensor_api/__init__.py @@ -22,7 +22,7 @@ class SubtensorApi: """Subtensor API class. Arguments: - network: The network to connect to. Defaults to `None` -> `finney`. + network: The network to connect to. Defaults to `None` -> "finney". config: Bittensor configuration object. Defaults to `None`. legacy_methods: If `True`, all methods from the Subtensor class will be added to the root level of this class. fallback_chains (list): List of fallback chains to use if no network is specified. Defaults to `None`. From 6a255d76b312989d687fcee5e36f85b8d3464bdc Mon Sep 17 00:00:00 2001 From: Roman <167799377+basfroman@users.noreply.github.com> Date: Wed, 7 May 2025 13:37:56 -0700 Subject: [PATCH 216/251] Update bittensor/core/async_subtensor.py Co-authored-by: BD Himes <37844818+thewhaleking@users.noreply.github.com> --- bittensor/core/async_subtensor.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 2cda6446d1..7b8a87b37f 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -303,7 +303,11 @@ def _get_substrate( return RetryAsyncSubstrate( url=self.chain_endpoint, fallback_chains=fallback_chains, + ss58_format=SS58_FORMAT, + type_registry=TYPE_REGISTRY, retry_forever=retry_forever, + use_remote_preset=True, + chain_name="Bittensor", _mock=_mock, ) return AsyncSubstrateInterface( From 4ad25520cc7ad0801df123bc63ec5569871649ea Mon Sep 17 00:00:00 2001 From: Benjamin Himes Date: Wed, 7 May 2025 22:38:37 +0200 Subject: [PATCH 217/251] Adds `__all__` to easy_imports.py to get rid of all the #noqa stuff --- bittensor/utils/easy_imports.py | 170 +++++++++++++++++++++++++++----- 1 file changed, 148 insertions(+), 22 deletions(-) diff --git a/bittensor/utils/easy_imports.py b/bittensor/utils/easy_imports.py index 8fa826d4d9..8a421fd43d 100644 --- a/bittensor/utils/easy_imports.py +++ b/bittensor/utils/easy_imports.py @@ -7,9 +7,9 @@ import importlib import sys -from bittensor_wallet import Keypair # noqa: F401 -from bittensor_wallet.errors import KeyFileError # noqa: F401 -from bittensor_wallet.keyfile import ( # noqa: F401 +from bittensor_wallet import Keypair +from bittensor_wallet.errors import KeyFileError +from bittensor_wallet.keyfile import ( serialized_keypair_to_keyfile_data, deserialize_keypair_from_keyfile_data, validate_password, @@ -25,12 +25,12 @@ decrypt_keyfile_data, Keyfile, ) -from bittensor_wallet.wallet import display_mnemonic_msg, Wallet # noqa: F401 +from bittensor_wallet.wallet import display_mnemonic_msg, Wallet -from bittensor.core import settings, timelock # noqa: F401 +from bittensor.core import settings, timelock from bittensor.core.async_subtensor import AsyncSubtensor from bittensor.core.axon import Axon -from bittensor.core.chain_data import ( # noqa: F401 +from bittensor.core.chain_data import ( AxonInfo, ChainIdentity, DelegateInfo, @@ -55,14 +55,14 @@ SubnetState, WeightCommitInfo, ) -from bittensor.core.config import ( # noqa: F401 +from bittensor.core.config import ( InvalidConfigFile, DefaultConfig, Config, T, ) -from bittensor.core.dendrite import Dendrite # noqa: F401 -from bittensor.core.errors import ( # noqa: F401 +from bittensor.core.dendrite import Dendrite +from bittensor.core.errors import ( BlacklistedException, ChainConnectionError, ChainError, @@ -102,15 +102,13 @@ ) from bittensor.core.metagraph import Metagraph from bittensor.core.settings import BLOCKTIME -from bittensor.core.stream import StreamingSynapse # noqa: F401 +from bittensor.core.stream import StreamingSynapse from bittensor.core.subtensor import Subtensor -from bittensor.core.subtensor_api import SubtensorApi # noqa: F401 -from bittensor.core.synapse import TerminalInfo, Synapse # noqa: F401 -from bittensor.core.tensor import Tensor # noqa: F401 -from bittensor.core.threadpool import ( # noqa: F401 - PriorityThreadPoolExecutor as PriorityThreadPoolExecutor, -) -from bittensor.utils import ( # noqa: F401 +from bittensor.core.subtensor_api import SubtensorApi +from bittensor.core.synapse import TerminalInfo, Synapse +from bittensor.core.tensor import Tensor +from bittensor.core.threadpool import PriorityThreadPoolExecutor +from bittensor.utils import ( ss58_to_vec_u8, version_checking, strtobool, @@ -120,14 +118,12 @@ u64_normalized_float, get_hash, ) -from bittensor.utils.balance import Balance as Balance # noqa: F401 +from bittensor.utils.balance import Balance from bittensor.utils.balance import tao, rao from bittensor.utils.btlogging import logging -from bittensor.utils.mock.subtensor_mock import MockSubtensor as MockSubtensor # noqa: F401 -from bittensor.utils.subnets import SubnetsAPI # noqa: F401 +from bittensor.utils.mock.subtensor_mock import MockSubtensor +from bittensor.utils.subnets import SubnetsAPI -tao = tao -rao = rao # Backwards compatibility with previous bittensor versions. async_subtensor = AsyncSubtensor @@ -201,3 +197,133 @@ def info(on: bool = True): on (bool): If True, enables info logging. If False, disables info logging and sets default (WARNING) level. """ logging.set_info(on) + + +__all__ = [ + "Keypair", + "KeyFileError", + "serialized_keypair_to_keyfile_data", + "deserialize_keypair_from_keyfile_data", + "validate_password", + "ask_password_to_encrypt", + "keyfile_data_is_encrypted_nacl", + "keyfile_data_is_encrypted_ansible", + "keyfile_data_is_encrypted_legacy", + "keyfile_data_is_encrypted", + "keyfile_data_encryption_method", + "legacy_encrypt_keyfile_data", + "encrypt_keyfile_data", + "get_coldkey_password_from_environment", + "decrypt_keyfile_data", + "Keyfile", + "display_mnemonic_msg", + "Wallet", + "settings", + "timelock", + "AsyncSubtensor", + "Axon", + "AxonInfo", + "ChainIdentity", + "DelegateInfo", + "DelegateInfoLite", + "DynamicInfo", + "IPInfo", + "MetagraphInfo", + "MetagraphInfoEmissions", + "MetagraphInfoParams", + "MetagraphInfoPool", + "NeuronInfo", + "NeuronInfoLite", + "PrometheusInfo", + "ProposalCallData", + "ProposalVoteData", + "ScheduledColdkeySwapInfo", + "SelectiveMetagraphIndex", + "StakeInfo", + "SubnetHyperparameters", + "SubnetIdentity", + "SubnetInfo", + "SubnetState", + "WeightCommitInfo", + "InvalidConfigFile", + "DefaultConfig", + "Config", + "T", + "Dendrite", + "BlacklistedException", + "ChainConnectionError", + "ChainError", + "ChainQueryError", + "ChainTransactionError", + "DelegateTakeTooHigh", + "DelegateTakeTooLow", + "DelegateTxRateLimitExceeded", + "DuplicateChild", + "HotKeyAccountNotExists", + "IdentityError", + "InternalServerError", + "InvalidChild", + "InvalidRequestNameError", + "MetadataError", + "NominationError", + "NonAssociatedColdKey", + "NotDelegateError", + "NotEnoughStakeToSetChildkeys", + "NotRegisteredError", + "NotVerifiedException", + "PostProcessException", + "PriorityException", + "ProportionOverflow", + "RegistrationError", + "RegistrationNotPermittedOnRootSubnet", + "RunException", + "StakeError", + "SubNetworkDoesNotExist", + "SynapseDendriteNoneException", + "SynapseParsingError", + "TooManyChildren", + "TransferError", + "TxRateLimitExceeded", + "UnknownSynapseError", + "UnstakeError", + "Metagraph", + "BLOCKTIME", + "StreamingSynapse", + "Subtensor", + "SubtensorApi", + "TerminalInfo", + "Synapse", + "Tensor", + "PriorityThreadPoolExecutor", + "ss58_to_vec_u8", + "version_checking", + "strtobool", + "get_explorer_url_for_network", + "ss58_address_to_bytes", + "u16_normalized_float", + "u64_normalized_float", + "get_hash", + "Balance", + "tao", + "rao", + "logging", + "MockSubtensor", + "SubnetsAPI", + "__blocktime__", + "__network_explorer_map__", + "__pipaddress__", + "__ss58_format__", + "__type_registry__", + "__ss58_address_length__", + "__networks__", + "__finney_entrypoint__", + "__finney_test_entrypoint__", + "__archive_entrypoint__", + "__local_entrypoint__", + "__tao_symbol__", + "__rao_symbol__", + "trace", + "debug", + "warning", + "info", +] From 0e1ccbf1ddf82e0e0677538c0c779a99c831b2bf Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 7 May 2025 15:45:26 -0500 Subject: [PATCH 218/251] fix --- bittensor/core/subtensor.py | 1 - 1 file changed, 1 deletion(-) diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index e642ad40aa..47d5741113 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -184,7 +184,6 @@ def _get_substrate( """ if fallback_chains or retry_forever: return RetrySyncSubstrate( - url=self.chain_endpoint, url=self.chain_endpoint, ss58_format=SS58_FORMAT, type_registry=TYPE_REGISTRY, From afb5f3dc307fd330baec9dc918c84b1ab2b14436 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 7 May 2025 15:45:49 -0500 Subject: [PATCH 219/251] bumping version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 7ef6313fcc..fe5d089278 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,7 +36,7 @@ dependencies = [ "uvicorn", "bittensor-drand>=0.5.0", "bittensor-wallet>=3.0.8", - "async-substrate-interface>=1.1.0" + "async-substrate-interface>=1.2.0" ] [project.optional-dependencies] From ff9c533a7006ab7d28dd491faea466488b6c70a6 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 7 May 2025 15:48:33 -0500 Subject: [PATCH 220/251] checker --- bittensor/core/subtensor_api/__init__.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/bittensor/core/subtensor_api/__init__.py b/bittensor/core/subtensor_api/__init__.py index 98038d28de..3c1bacda8a 100644 --- a/bittensor/core/subtensor_api/__init__.py +++ b/bittensor/core/subtensor_api/__init__.py @@ -144,15 +144,31 @@ def __repr__(self): return self.__str__() def __enter__(self): + if self.is_async: + raise NotImplementedError( + "Async version of SubtensorApi cannot be used with sync context manager." + ) return self def __exit__(self, exc_type, exc_val, exc_tb): + if self.is_async: + raise NotImplementedError( + "Async version of SubtensorApi cannot be used with sync context manager." + ) self.close() async def __aenter__(self): + if not self.is_async: + raise NotImplementedError( + "Sync version of SubtensorApi cannot be used with async context manager." + ) return await self._subtensor.__aenter__() async def __aexit__(self, exc_type, exc_val, exc_tb): + if not self.is_async: + raise NotImplementedError( + "Sync version of SubtensorApi cannot be used with async context manager." + ) await self.substrate.close() @classmethod From b83945d07339a038e7eed785d2189b27776ef6fa Mon Sep 17 00:00:00 2001 From: Benjamin Himes Date: Wed, 7 May 2025 22:54:33 +0200 Subject: [PATCH 221/251] Missed some --- bittensor/utils/easy_imports.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/bittensor/utils/easy_imports.py b/bittensor/utils/easy_imports.py index 8a421fd43d..93389e50ab 100644 --- a/bittensor/utils/easy_imports.py +++ b/bittensor/utils/easy_imports.py @@ -309,6 +309,15 @@ def info(on: bool = True): "logging", "MockSubtensor", "SubnetsAPI", + "async_subtensor", + "axon", + "config", + "dendrite", + "keyfile", + "metagraph", + "wallet", + "subtensor", + "synapse", "__blocktime__", "__network_explorer_map__", "__pipaddress__", From 0bb6682557fd3033da7ac10b4081160b9e1961d8 Mon Sep 17 00:00:00 2001 From: Benjamin Himes Date: Wed, 7 May 2025 23:30:26 +0200 Subject: [PATCH 222/251] Added test --- bittensor/utils/easy_imports.py | 2 ++ tests/unit_tests/test_easy_imports.py | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 tests/unit_tests/test_easy_imports.py diff --git a/bittensor/utils/easy_imports.py b/bittensor/utils/easy_imports.py index 93389e50ab..7774e3f4d8 100644 --- a/bittensor/utils/easy_imports.py +++ b/bittensor/utils/easy_imports.py @@ -335,4 +335,6 @@ def info(on: bool = True): "debug", "warning", "info", + "mock_subpackage", + "extrinsics_subpackage", ] diff --git a/tests/unit_tests/test_easy_imports.py b/tests/unit_tests/test_easy_imports.py new file mode 100644 index 0000000000..bfe3ca6f23 --- /dev/null +++ b/tests/unit_tests/test_easy_imports.py @@ -0,0 +1,16 @@ +from bittensor.utils import easy_imports +import bittensor + +import pytest + + +@pytest.mark.parametrize( + "attr", + [ + a + for a in dir(easy_imports) + if (not a.startswith("__") and a not in ["sys", "importlib"]) # we don't care about systemwide pkgs + ], +) +def test_easy_imports(attr): + assert getattr(bittensor, attr), attr From db58ca2941858dd0b289ef1b758a94e4e547c49c Mon Sep 17 00:00:00 2001 From: Benjamin Himes Date: Wed, 7 May 2025 23:48:31 +0200 Subject: [PATCH 223/251] Removed items. --- bittensor/utils/easy_imports.py | 23 +---------------------- tests/unit_tests/test_easy_imports.py | 4 +++- 2 files changed, 4 insertions(+), 23 deletions(-) diff --git a/bittensor/utils/easy_imports.py b/bittensor/utils/easy_imports.py index 7774e3f4d8..512ca764ac 100644 --- a/bittensor/utils/easy_imports.py +++ b/bittensor/utils/easy_imports.py @@ -55,12 +55,7 @@ SubnetState, WeightCommitInfo, ) -from bittensor.core.config import ( - InvalidConfigFile, - DefaultConfig, - Config, - T, -) +from bittensor.core.config import Config from bittensor.core.dendrite import Dendrite from bittensor.core.errors import ( BlacklistedException, @@ -245,10 +240,7 @@ def info(on: bool = True): "SubnetInfo", "SubnetState", "WeightCommitInfo", - "InvalidConfigFile", - "DefaultConfig", "Config", - "T", "Dendrite", "BlacklistedException", "ChainConnectionError", @@ -318,19 +310,6 @@ def info(on: bool = True): "wallet", "subtensor", "synapse", - "__blocktime__", - "__network_explorer_map__", - "__pipaddress__", - "__ss58_format__", - "__type_registry__", - "__ss58_address_length__", - "__networks__", - "__finney_entrypoint__", - "__finney_test_entrypoint__", - "__archive_entrypoint__", - "__local_entrypoint__", - "__tao_symbol__", - "__rao_symbol__", "trace", "debug", "warning", diff --git a/tests/unit_tests/test_easy_imports.py b/tests/unit_tests/test_easy_imports.py index bfe3ca6f23..8ebd020faf 100644 --- a/tests/unit_tests/test_easy_imports.py +++ b/tests/unit_tests/test_easy_imports.py @@ -9,7 +9,9 @@ [ a for a in dir(easy_imports) - if (not a.startswith("__") and a not in ["sys", "importlib"]) # we don't care about systemwide pkgs + if ( + not a.startswith("__") and a not in ["sys", "importlib"] + ) # we don't care about systemwide pkgs ], ) def test_easy_imports(attr): From 69b55ec72e3e1380fc02a28c6a791afcd3986f0f Mon Sep 17 00:00:00 2001 From: Benjamin Himes Date: Wed, 7 May 2025 23:51:35 +0200 Subject: [PATCH 224/251] Removed more items. --- bittensor/utils/easy_imports.py | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/bittensor/utils/easy_imports.py b/bittensor/utils/easy_imports.py index 512ca764ac..a2645b0668 100644 --- a/bittensor/utils/easy_imports.py +++ b/bittensor/utils/easy_imports.py @@ -131,23 +131,6 @@ subtensor = Subtensor synapse = Synapse -__blocktime__ = BLOCKTIME -__network_explorer_map__ = settings.NETWORK_EXPLORER_MAP -__pipaddress__ = settings.PIPADDRESS -__ss58_format__ = settings.SS58_FORMAT -__type_registry__ = settings.TYPE_REGISTRY -__ss58_address_length__ = settings.SS58_ADDRESS_LENGTH - -__networks__ = settings.NETWORKS - -__finney_entrypoint__ = settings.FINNEY_ENTRYPOINT -__finney_test_entrypoint__ = settings.FINNEY_TEST_ENTRYPOINT -__archive_entrypoint__ = settings.ARCHIVE_ENTRYPOINT -__local_entrypoint__ = settings.LOCAL_ENTRYPOINT - -__tao_symbol__ = settings.TAO_SYMBOL -__rao_symbol__ = settings.RAO_SYMBOL - # Makes the `bittensor.utils.mock` subpackage available as `bittensor.mock` for backwards compatibility. mock_subpackage = importlib.import_module("bittensor.utils.mock") sys.modules["bittensor.mock"] = mock_subpackage From b9ca6d6a3fdb9de58126f6bca250cfbf60e35cb4 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 7 May 2025 17:05:29 -0500 Subject: [PATCH 225/251] rename class --- bittensor/core/subtensor_api/{stakes.py => staking.py} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename bittensor/core/subtensor_api/{stakes.py => staking.py} (94%) diff --git a/bittensor/core/subtensor_api/stakes.py b/bittensor/core/subtensor_api/staking.py similarity index 94% rename from bittensor/core/subtensor_api/stakes.py rename to bittensor/core/subtensor_api/staking.py index 78e497d901..6ccce7fd4d 100644 --- a/bittensor/core/subtensor_api/stakes.py +++ b/bittensor/core/subtensor_api/staking.py @@ -3,8 +3,8 @@ from bittensor.core.async_subtensor import AsyncSubtensor as _AsyncSubtensor -class Stakes: - """Class for managing stake operations.""" +class Staking: + """Class for managing staking operations.""" def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): self.add_stake = subtensor.add_stake From 480045e067c7da34ef0ffdaedcbedd3ad11f0299 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 7 May 2025 17:05:44 -0500 Subject: [PATCH 226/251] rename import --- bittensor/core/subtensor_api/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bittensor/core/subtensor_api/__init__.py b/bittensor/core/subtensor_api/__init__.py index 3c1bacda8a..be5a61638a 100644 --- a/bittensor/core/subtensor_api/__init__.py +++ b/bittensor/core/subtensor_api/__init__.py @@ -9,7 +9,7 @@ from .metagraphs import Metagraphs as _Metagraphs from .neurons import Neurons as _Neurons from .queries import Queries as _Queries -from .stakes import Stakes as _Stakes +from .staking import Staking as _Staking from .subnets import Subnets as _Subnets from .utils import add_legacy_methods as _add_classic_fields from .wallets import Wallets as _Wallets @@ -221,9 +221,9 @@ def queries(self): return _Queries(self._subtensor) @property - def stakes(self): - """Property to access stakes methods.""" - return _Stakes(self._subtensor) + def staking(self): + """Property to access staking methods.""" + return _Staking(self._subtensor) @property def subnets(self): From 1c93f16f54d66f6834ce4c20b5c1c61d85593cf5 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 7 May 2025 17:05:49 -0500 Subject: [PATCH 227/251] fix tests --- tests/unit_tests/test_subtensor_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit_tests/test_subtensor_api.py b/tests/unit_tests/test_subtensor_api.py index 4078cb5a96..35d6c6a15c 100644 --- a/tests/unit_tests/test_subtensor_api.py +++ b/tests/unit_tests/test_subtensor_api.py @@ -29,7 +29,7 @@ def test_properties_methods_comparable(other_class: "Subtensor" = None): ] neurons_methods = [m for m in dir(subtensor_api.neurons) if not m.startswith("_")] queries_methods = [m for m in dir(subtensor_api.queries) if not m.startswith("_")] - stakes_methods = [m for m in dir(subtensor_api.stakes) if not m.startswith("_")] + stakes_methods = [m for m in dir(subtensor_api.staking) if not m.startswith("_")] subnets_methods = [m for m in dir(subtensor_api.subnets) if not m.startswith("_")] wallets_methods = [m for m in dir(subtensor_api.wallets) if not m.startswith("_")] From 3e7ea6be432ab0de09a911c626d888116362b24d Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 8 May 2025 17:57:00 -0500 Subject: [PATCH 228/251] improve get_next_epoch_start_block --- bittensor/core/async_subtensor.py | 31 ++++++++++++++++++++++++------- bittensor/core/subtensor.py | 6 +++++- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 7b8a87b37f..edf83cf78b 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -616,19 +616,31 @@ async def all_subnets( return subnets async def blocks_since_last_step( - self, netuid: int, block: Optional[int] = None + self, + netuid: int, + block: Optional[int] = None, + block_hash: Optional[str] = None, + reuse_block: bool = False, ) -> Optional[int]: """Returns number of blocks since the last epoch of the subnet. Arguments: netuid (int): The unique identifier of the subnetwork. block: the block number for this query. + block_hash: The hash of the blockchain block number for the query. Do not specify if using reuse_block or + block. + reuse_block: Whether to reuse the last-used blockchain block hash. Do not set if using block_hash or block. + Returns: block number of the last step in the subnet. """ query = await self.query_subtensor( - name="BlocksSinceLastStep", block=block, params=[netuid] + name="BlocksSinceLastStep", + block=block, + block_hash=block_hash, + reuse_block=reuse_block, + params=[netuid], ) return query.value if query is not None and hasattr(query, "value") else query @@ -1803,11 +1815,16 @@ async def get_next_epoch_start_block( int: The block number at which the next epoch will start. """ block_hash = await self.determine_block_hash(block, block_hash, reuse_block) - if not block_hash and reuse_block: - block_hash = self.substrate.last_block_hash - block = await self.substrate.get_block_number(block_hash=block_hash) - tempo = await self.tempo(netuid=netuid, block_hash=block_hash) - return (((block // tempo) + 1) * tempo) + 1 if tempo else None + blocks_since_last_step = await self.blocks_since_last_step( + netuid=netuid, block=block, block_hash=block_hash, reuse_block=reuse_block + ) + tempo = await self.tempo( + netuid=netuid, block=block, block_hash=block_hash, reuse_block=reuse_block + ) + + if block and blocks_since_last_step and tempo: + return block - blocks_since_last_step + tempo + 1 + return None async def get_owned_hotkeys( self, diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 47d5741113..796440cb90 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -1410,8 +1410,12 @@ def get_next_epoch_start_block( int: The block number at which the next epoch will start. """ block = block or self.block + blocks_since_last_step = self.blocks_since_last_step(netuid=netuid, block=block) tempo = self.tempo(netuid=netuid, block=block) - return (((block // tempo) + 1) * tempo) + 1 if tempo else None + + if block and blocks_since_last_step and tempo: + return block - blocks_since_last_step + tempo + 1 + return None def get_owned_hotkeys( self, From 29736b0ab4a303e2b36ab134e6234370e14666f0 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 8 May 2025 17:57:27 -0500 Subject: [PATCH 229/251] fix test --- tests/unit_tests/test_async_subtensor.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/unit_tests/test_async_subtensor.py b/tests/unit_tests/test_async_subtensor.py index b61df6dfaa..8c0c5c6288 100644 --- a/tests/unit_tests/test_async_subtensor.py +++ b/tests/unit_tests/test_async_subtensor.py @@ -3192,6 +3192,8 @@ async def test_blocks_since_last_step_with_value(subtensor, mocker): mocked_query_subtensor.assert_awaited_once_with( name="BlocksSinceLastStep", block=block, + block_hash=None, + reuse_block=False, params=[netuid], ) @@ -3214,6 +3216,8 @@ async def test_blocks_since_last_step_is_none(subtensor, mocker): mocked_query_subtensor.assert_awaited_once_with( name="BlocksSinceLastStep", block=block, + block_hash=None, + reuse_block=False, params=[netuid], ) From 3d17182f5a6d62856f4c334c977a4f678ba6e549 Mon Sep 17 00:00:00 2001 From: GarmashAlex Date: Fri, 9 May 2025 15:41:55 +0300 Subject: [PATCH 230/251] fix dead link --- contrib/CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/CONTRIBUTING.md b/contrib/CONTRIBUTING.md index e0a0a75cee..31006caae1 100644 --- a/contrib/CONTRIBUTING.md +++ b/contrib/CONTRIBUTING.md @@ -102,7 +102,7 @@ Patchsets and enhancements should always be focused. A pull request could add a Specifically, pull requests must adhere to the following criteria: - **Must** branch off from `staging`. Make sure that all your PRs are using `staging` branch as a base or will be closed. - Contain fewer than 50 files. PRs with more than 50 files will be closed. -- Use the specific [template](./.github/pull_request_template.md) appropriate to your contribution. +- Use the specific [template](https://github.com/opentensor/bittensor/blob/master/.github/pull_request_template.md) appropriate to your contribution. - If a PR introduces a new feature, it *must* include corresponding tests. - Other PRs (bug fixes, refactoring, etc.) should ideally also have tests, as they provide proof of concept and prevent regression. - Categorize your PR properly by using GitHub labels. This aids in the review process by informing reviewers about the type of change at a glance. @@ -127,7 +127,7 @@ Please follow these steps to have your contribution considered by the maintainer 1. Read the [development workflow](./DEVELOPMENT_WORKFLOW.md) defined for this repository to understand our workflow. 2. Ensure your PR meets the criteria stated in the 'Pull Request Philosophy' section. 3. Include relevant tests for any fixed bugs or new features as stated in the [testing guide](./TESTING.md). -4. Follow all instructions in [the template](https://github.com/opentensor/bittensor/blob/master/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md) to create the PR. +4. Follow all instructions in [the template](https://github.com/opentensor/bittensor/blob/master/.github/pull_request_template.md) to create the PR. 5. Ensure your commit messages are clear and concise. Include the issue number if applicable. 6. If you have multiple commits, rebase them into a single commit using `git rebase -i`. 7. Explain what your changes do and why you think they should be merged in the PR description consistent with the [style guide](./STYLE.md). From 2d04c3966f26b0ac1f7a86ad290226bfef06b415 Mon Sep 17 00:00:00 2001 From: GarmashAlex Date: Fri, 9 May 2025 18:40:17 +0300 Subject: [PATCH 231/251] Update contrib/CONTRIBUTING.md Co-authored-by: BD Himes <37844818+thewhaleking@users.noreply.github.com> --- contrib/CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/CONTRIBUTING.md b/contrib/CONTRIBUTING.md index 31006caae1..79ca1e7bb4 100644 --- a/contrib/CONTRIBUTING.md +++ b/contrib/CONTRIBUTING.md @@ -102,7 +102,7 @@ Patchsets and enhancements should always be focused. A pull request could add a Specifically, pull requests must adhere to the following criteria: - **Must** branch off from `staging`. Make sure that all your PRs are using `staging` branch as a base or will be closed. - Contain fewer than 50 files. PRs with more than 50 files will be closed. -- Use the specific [template](https://github.com/opentensor/bittensor/blob/master/.github/pull_request_template.md) appropriate to your contribution. +- Use the specific [template](../.github/pull_request_template.md) appropriate to your contribution. - If a PR introduces a new feature, it *must* include corresponding tests. - Other PRs (bug fixes, refactoring, etc.) should ideally also have tests, as they provide proof of concept and prevent regression. - Categorize your PR properly by using GitHub labels. This aids in the review process by informing reviewers about the type of change at a glance. From de54deaf2654c800eee98a5f18e4c9c06f49b7d6 Mon Sep 17 00:00:00 2001 From: GarmashAlex Date: Fri, 9 May 2025 18:40:24 +0300 Subject: [PATCH 232/251] Update contrib/CONTRIBUTING.md Co-authored-by: BD Himes <37844818+thewhaleking@users.noreply.github.com> --- contrib/CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/CONTRIBUTING.md b/contrib/CONTRIBUTING.md index 79ca1e7bb4..714102bfd2 100644 --- a/contrib/CONTRIBUTING.md +++ b/contrib/CONTRIBUTING.md @@ -127,7 +127,7 @@ Please follow these steps to have your contribution considered by the maintainer 1. Read the [development workflow](./DEVELOPMENT_WORKFLOW.md) defined for this repository to understand our workflow. 2. Ensure your PR meets the criteria stated in the 'Pull Request Philosophy' section. 3. Include relevant tests for any fixed bugs or new features as stated in the [testing guide](./TESTING.md). -4. Follow all instructions in [the template](https://github.com/opentensor/bittensor/blob/master/.github/pull_request_template.md) to create the PR. +4. Follow all instructions in [the template](../.github/pull_request_template.md) to create the PR. 5. Ensure your commit messages are clear and concise. Include the issue number if applicable. 6. If you have multiple commits, rebase them into a single commit using `git rebase -i`. 7. Explain what your changes do and why you think they should be merged in the PR description consistent with the [style guide](./STYLE.md). From a82606b4a14bdb3d9dd7495010f24f5192703865 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 9 May 2025 14:36:29 -0500 Subject: [PATCH 233/251] fix `tests/e2e_tests/test_hotkeys.py` --- tests/e2e_tests/test_hotkeys.py | 50 +++++++++++++++------------------ 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/tests/e2e_tests/test_hotkeys.py b/tests/e2e_tests/test_hotkeys.py index 71623be218..3a699f08d0 100644 --- a/tests/e2e_tests/test_hotkeys.py +++ b/tests/e2e_tests/test_hotkeys.py @@ -12,14 +12,11 @@ NonAssociatedColdKey, ) from bittensor.utils.btlogging import logging -from tests.e2e_tests.utils.chain_interactions import ( - sudo_set_admin_utils, - wait_epoch, -) +from tests.e2e_tests.utils.chain_interactions import sudo_set_admin_utils from tests.e2e_tests.utils.e2e_test_utils import wait_to_start_call -SET_CHILDREN_COOLDOWN_PERIOD = 15 -SET_CHILDREN_RATE_LIMIT = 150 + +SET_CHILDREN_RATE_LIMIT = 15 def test_hotkeys(subtensor, alice_wallet, dave_wallet): @@ -27,9 +24,7 @@ def test_hotkeys(subtensor, alice_wallet, dave_wallet): Tests: - Check if Hotkey exists - Check if Hotkey is registered - """ - dave_subnet_netuid = subtensor.get_total_subnets() # 2 assert subtensor.register_subnet(dave_wallet, True, True) assert subtensor.subnet_exists(dave_subnet_netuid), ( @@ -111,15 +106,15 @@ async def test_children(local_chain, subtensor, alice_wallet, bob_wallet, dave_w is True ) - # assert ( - # sudo_set_admin_utils( - # local_chain, - # alice_wallet, - # call_function="sudo_set_tx_rate_limit", - # call_params={"tx_rate_limit": 100}, - # )[0] - # is True - # ) + assert ( + sudo_set_admin_utils( + local_chain, + alice_wallet, + call_function="sudo_set_tx_rate_limit", + call_params={"tx_rate_limit": 0}, + )[0] + is True + ) with pytest.raises(RegistrationNotPermittedOnRootSubnet): subtensor.set_children( @@ -269,9 +264,8 @@ async def test_children(local_chain, subtensor, alice_wallet, bob_wallet, dave_w ) assert pending == [(1.0, bob_wallet.hotkey.ss58_address)] - logging.console.info(f"Cooldown 1 is: {cooldown}") - subtensor.wait_for_block(cooldown + 1) + subtensor.wait_for_block(cooldown + 15) success, children, error = subtensor.get_children( alice_wallet.hotkey.ss58_address, @@ -280,22 +274,24 @@ async def test_children(local_chain, subtensor, alice_wallet, bob_wallet, dave_w assert error == "" assert success is True - assert children == [ - ( - 1.0, - bob_wallet.hotkey.ss58_address, - ) - ] + assert children == [(1.0, bob_wallet.hotkey.ss58_address)] # pending queue is empty pending, cooldown = subtensor.get_children_pending( alice_wallet.hotkey.ss58_address, netuid=dave_subnet_netuid, ) - assert pending == [] with pytest.raises(TxRateLimitExceeded): + set_children_block = subtensor.get_current_block() + subtensor.set_children( + alice_wallet, + alice_wallet.hotkey.ss58_address, + netuid=dave_subnet_netuid, + children=[], + raise_error=True, + ) subtensor.set_children( alice_wallet, alice_wallet.hotkey.ss58_address, @@ -320,8 +316,6 @@ async def test_children(local_chain, subtensor, alice_wallet, bob_wallet, dave_w netuid=dave_subnet_netuid, ) - logging.console.info(f"Cooldown 2 is: {cooldown}") - assert pending == [] subtensor.wait_for_block(cooldown) From ab5e594be2c9eb7cb6a7cf31157547b77246015c Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 9 May 2025 17:21:47 -0500 Subject: [PATCH 234/251] fix `tests.e2e_tests.test_staking.test_single_operation` --- tests/e2e_tests/test_staking.py | 87 +++++++++++++++------------------ 1 file changed, 39 insertions(+), 48 deletions(-) diff --git a/tests/e2e_tests/test_staking.py b/tests/e2e_tests/test_staking.py index 1e54b15de7..77194d8b41 100644 --- a/tests/e2e_tests/test_staking.py +++ b/tests/e2e_tests/test_staking.py @@ -5,8 +5,6 @@ from tests.helpers.helpers import ApproxBalance from tests.e2e_tests.utils.e2e_test_utils import wait_to_start_call -logging.enable_debug() - def test_single_operation(subtensor, alice_wallet, bob_wallet): """ @@ -28,29 +26,31 @@ def test_single_operation(subtensor, alice_wallet, bob_wallet): assert wait_to_start_call(subtensor, alice_wallet, alice_subnet_netuid) subtensor.burned_register( - alice_wallet, + wallet=alice_wallet, netuid=alice_subnet_netuid, wait_for_inclusion=True, wait_for_finalization=True, ) + logging.console.success(f"Alice is registered in subnet {alice_subnet_netuid}") subtensor.burned_register( - bob_wallet, + wallet=bob_wallet, netuid=alice_subnet_netuid, wait_for_inclusion=True, wait_for_finalization=True, ) + logging.console.success(f"Bob is registered in subnet {alice_subnet_netuid}") stake = subtensor.get_stake( - alice_wallet.coldkey.ss58_address, - bob_wallet.hotkey.ss58_address, + coldkey_ss58=alice_wallet.coldkey.ss58_address, + hotkey_ss58=bob_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid, ) assert stake == Balance(0).set_unit(alice_subnet_netuid) success = subtensor.add_stake( - alice_wallet, - bob_wallet.hotkey.ss58_address, + wallet=alice_wallet, + hotkey_ss58=bob_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid, amount=Balance.from_tao(1), wait_for_inclusion=True, @@ -61,24 +61,26 @@ def test_single_operation(subtensor, alice_wallet, bob_wallet): assert success is True stake_alice = subtensor.get_stake( - alice_wallet.coldkey.ss58_address, - alice_wallet.hotkey.ss58_address, + coldkey_ss58=alice_wallet.coldkey.ss58_address, + hotkey_ss58=alice_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid, ) + logging.console.info(f"Alice stake: {stake_alice}") stake_bob = subtensor.get_stake( - alice_wallet.coldkey.ss58_address, - bob_wallet.hotkey.ss58_address, + coldkey_ss58=alice_wallet.coldkey.ss58_address, + hotkey_ss58=bob_wallet.hotkey.ss58_address, netuid=alice_subnet_netuid, ) + logging.console.info(f"Bob stake: {stake_bob}") assert stake_bob > Balance(0).set_unit(alice_subnet_netuid) stakes = subtensor.get_stake_for_coldkey(alice_wallet.coldkey.ss58_address) - assert stakes == [ + expected_stakes = [ StakeInfo( - hotkey_ss58=alice_wallet.hotkey.ss58_address, + hotkey_ss58=stakes[0].hotkey_ss58, coldkey_ss58=alice_wallet.coldkey.ss58_address, netuid=alice_subnet_netuid, stake=get_dynamic_balance(stakes[0].stake.rao, alice_subnet_netuid), @@ -87,42 +89,31 @@ def test_single_operation(subtensor, alice_wallet, bob_wallet): drain=0, is_registered=True, ), - StakeInfo( - hotkey_ss58=bob_wallet.hotkey.ss58_address, - coldkey_ss58=alice_wallet.coldkey.ss58_address, - netuid=alice_subnet_netuid, - stake=get_dynamic_balance(stakes[1].stake.rao, alice_subnet_netuid), - locked=Balance(0).set_unit(alice_subnet_netuid), - emission=get_dynamic_balance(stakes[1].emission.rao, alice_subnet_netuid), - drain=0, - is_registered=True, - ), ] - stakes = subtensor.get_stake_info_for_coldkey(alice_wallet.coldkey.ss58_address) - - assert stakes == [ - StakeInfo( - hotkey_ss58=alice_wallet.hotkey.ss58_address, - coldkey_ss58=alice_wallet.coldkey.ss58_address, - netuid=alice_subnet_netuid, - stake=get_dynamic_balance(stakes[0].stake.rao, alice_subnet_netuid), - locked=Balance(0).set_unit(alice_subnet_netuid), - emission=get_dynamic_balance(stakes[0].emission.rao, alice_subnet_netuid), - drain=0, - is_registered=True, - ), - StakeInfo( - hotkey_ss58=bob_wallet.hotkey.ss58_address, - coldkey_ss58=alice_wallet.coldkey.ss58_address, - netuid=alice_subnet_netuid, - stake=get_dynamic_balance(stakes[1].stake.rao, alice_subnet_netuid), - locked=Balance(0).set_unit(alice_subnet_netuid), - emission=get_dynamic_balance(stakes[1].emission.rao, alice_subnet_netuid), - drain=0, - is_registered=True, - ), - ] + fast_blocks_stake = ( + [ + StakeInfo( + hotkey_ss58=stakes[1].hotkey_ss58, + coldkey_ss58=alice_wallet.coldkey.ss58_address, + netuid=alice_subnet_netuid, + stake=get_dynamic_balance(stakes[1].stake.rao, alice_subnet_netuid), + locked=Balance(0).set_unit(alice_subnet_netuid), + emission=get_dynamic_balance( + stakes[1].emission.rao, alice_subnet_netuid + ), + drain=0, + is_registered=True, + ) + ] + if subtensor.is_fast_blocks() + else [] + ) + + expected_stakes += fast_blocks_stake + + assert stakes == expected_stakes + assert subtensor.get_stake_for_coldkey == subtensor.get_stake_info_for_coldkey stakes = subtensor.get_stake_for_coldkey_and_hotkey( alice_wallet.coldkey.ss58_address, From 6d49fa3785e8257c6c4e07c064b9a7f2f6a7246f Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 9 May 2025 17:29:07 -0500 Subject: [PATCH 235/251] fix unit tests --- bittensor/core/subtensor_api/chain.py | 1 + bittensor/core/subtensor_api/utils.py | 1 + 2 files changed, 2 insertions(+) diff --git a/bittensor/core/subtensor_api/chain.py b/bittensor/core/subtensor_api/chain.py index fe03aada99..cd2bfda02f 100644 --- a/bittensor/core/subtensor_api/chain.py +++ b/bittensor/core/subtensor_api/chain.py @@ -14,6 +14,7 @@ def __init__(self, subtensor: Union["_Subtensor", "_AsyncSubtensor"]): self.get_minimum_required_stake = subtensor.get_minimum_required_stake self.get_vote_data = subtensor.get_vote_data self.get_timestamp = subtensor.get_timestamp + self.is_fast_blocks = subtensor.is_fast_blocks self.last_drand_round = subtensor.last_drand_round self.state_call = subtensor.state_call self.tx_rate_limit = subtensor.tx_rate_limit diff --git a/bittensor/core/subtensor_api/utils.py b/bittensor/core/subtensor_api/utils.py index 5d8783f6e6..0002b60216 100644 --- a/bittensor/core/subtensor_api/utils.py +++ b/bittensor/core/subtensor_api/utils.py @@ -101,6 +101,7 @@ def add_legacy_methods(subtensor: "SubtensorApi"): subtensor.get_unstake_fee = subtensor._subtensor.get_unstake_fee subtensor.get_vote_data = subtensor._subtensor.get_vote_data subtensor.immunity_period = subtensor._subtensor.immunity_period + subtensor.is_fast_blocks = subtensor._subtensor.is_fast_blocks subtensor.is_hotkey_delegate = subtensor._subtensor.is_hotkey_delegate subtensor.is_hotkey_registered = subtensor._subtensor.is_hotkey_registered subtensor.is_hotkey_registered_any = subtensor._subtensor.is_hotkey_registered_any From 58a41b7336a1c7da5de65c800f2435431827b807 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 9 May 2025 18:05:54 -0500 Subject: [PATCH 236/251] adapted `tests.e2e_tests.test_staking.test_transfer_stake` for both mods --- tests/e2e_tests/test_staking.py | 72 ++++++++++++++++++++------------- 1 file changed, 45 insertions(+), 27 deletions(-) diff --git a/tests/e2e_tests/test_staking.py b/tests/e2e_tests/test_staking.py index 77194d8b41..262c7874fe 100644 --- a/tests/e2e_tests/test_staking.py +++ b/tests/e2e_tests/test_staking.py @@ -656,28 +656,41 @@ def test_move_stake(subtensor, alice_wallet, bob_wallet): stakes = subtensor.get_stake_for_coldkey(alice_wallet.coldkey.ss58_address) - assert stakes == [ + expected_stakes = [ StakeInfo( - hotkey_ss58=alice_wallet.hotkey.ss58_address, + hotkey_ss58=stakes[0].hotkey_ss58, coldkey_ss58=alice_wallet.coldkey.ss58_address, - netuid=alice_subnet_netuid, + netuid=alice_subnet_netuid + if subtensor.is_fast_blocks() + else bob_subnet_netuid, stake=get_dynamic_balance(stakes[0].stake.rao, bob_subnet_netuid), locked=Balance(0).set_unit(bob_subnet_netuid), emission=get_dynamic_balance(stakes[0].emission.rao, bob_subnet_netuid), drain=0, is_registered=True, - ), - StakeInfo( - hotkey_ss58=bob_wallet.hotkey.ss58_address, - coldkey_ss58=alice_wallet.coldkey.ss58_address, - netuid=bob_subnet_netuid, - stake=get_dynamic_balance(stakes[1].stake.rao, bob_subnet_netuid), - locked=Balance(0).set_unit(bob_subnet_netuid), - emission=get_dynamic_balance(stakes[1].emission.rao, bob_subnet_netuid), - drain=0, - is_registered=True, - ), + ) ] + + fast_block_stake = ( + [ + StakeInfo( + hotkey_ss58=stakes[1].hotkey_ss58, + coldkey_ss58=alice_wallet.coldkey.ss58_address, + netuid=bob_subnet_netuid, + stake=get_dynamic_balance(stakes[1].stake.rao, bob_subnet_netuid), + locked=Balance(0).set_unit(bob_subnet_netuid), + emission=get_dynamic_balance(stakes[1].emission.rao, bob_subnet_netuid), + drain=0, + is_registered=True, + ), + ] + if subtensor.is_fast_blocks() + else [] + ) + + expected_stakes += fast_block_stake + + assert stakes == expected_stakes logging.console.success(f"✅ Test [green]test_move_stake[/green] passed") @@ -775,18 +788,23 @@ def test_transfer_stake(subtensor, alice_wallet, bob_wallet, dave_wallet): bob_stakes = subtensor.get_stake_for_coldkey(bob_wallet.coldkey.ss58_address) - assert bob_stakes == [ - StakeInfo( - hotkey_ss58=alice_wallet.hotkey.ss58_address, - coldkey_ss58=bob_wallet.coldkey.ss58_address, - netuid=dave_subnet_netuid, - stake=get_dynamic_balance(bob_stakes[0].stake.rao, dave_subnet_netuid), - locked=Balance(0), - emission=get_dynamic_balance( - bob_stakes[0].emission.rao, dave_subnet_netuid + assert ( + bob_stakes + == [ + StakeInfo( + hotkey_ss58=alice_wallet.hotkey.ss58_address, + coldkey_ss58=bob_wallet.coldkey.ss58_address, + netuid=dave_subnet_netuid, + stake=get_dynamic_balance(bob_stakes[0].stake.rao, dave_subnet_netuid), + locked=Balance(0), + emission=get_dynamic_balance( + bob_stakes[0].emission.rao, dave_subnet_netuid + ), + drain=0, + is_registered=False, ), - drain=0, - is_registered=False, - ), - ] + ] + if subtensor.is_fast_blocks() + else [] + ) logging.console.success(f"✅ Test [green]test_transfer_stake[/green] passed") From e26d787cdaf1eb50708b8d38950d86e6c04df48c Mon Sep 17 00:00:00 2001 From: Roman Date: Sat, 10 May 2025 19:45:35 -0500 Subject: [PATCH 237/251] adapted `tests.e2e_tests.test_staking.test_transfer_stake` for both mods --- tests/e2e_tests/test_staking.py | 56 ++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 26 deletions(-) diff --git a/tests/e2e_tests/test_staking.py b/tests/e2e_tests/test_staking.py index 262c7874fe..f9bcc45510 100644 --- a/tests/e2e_tests/test_staking.py +++ b/tests/e2e_tests/test_staking.py @@ -771,40 +771,44 @@ def test_transfer_stake(subtensor, alice_wallet, bob_wallet, dave_wallet): alice_stakes = subtensor.get_stake_for_coldkey(alice_wallet.coldkey.ss58_address) - assert alice_stakes == [ - StakeInfo( - hotkey_ss58=alice_wallet.hotkey.ss58_address, - coldkey_ss58=alice_wallet.coldkey.ss58_address, - netuid=alice_subnet_netuid, - stake=get_dynamic_balance(alice_stakes[0].stake.rao, alice_subnet_netuid), - locked=Balance(0).set_unit(alice_subnet_netuid), - emission=get_dynamic_balance( - alice_stakes[0].emission.rao, alice_subnet_netuid - ), - drain=0, - is_registered=True, - ), - ] - - bob_stakes = subtensor.get_stake_for_coldkey(bob_wallet.coldkey.ss58_address) - - assert ( - bob_stakes - == [ + expected_alice_stake = ( + [ StakeInfo( hotkey_ss58=alice_wallet.hotkey.ss58_address, - coldkey_ss58=bob_wallet.coldkey.ss58_address, - netuid=dave_subnet_netuid, - stake=get_dynamic_balance(bob_stakes[0].stake.rao, dave_subnet_netuid), - locked=Balance(0), + coldkey_ss58=alice_wallet.coldkey.ss58_address, + netuid=alice_subnet_netuid, + stake=get_dynamic_balance( + alice_stakes[0].stake.rao, alice_subnet_netuid + ), + locked=Balance(0).set_unit(alice_subnet_netuid), emission=get_dynamic_balance( - bob_stakes[0].emission.rao, dave_subnet_netuid + alice_stakes[0].emission.rao, alice_subnet_netuid ), drain=0, - is_registered=False, + is_registered=True, ), ] if subtensor.is_fast_blocks() else [] ) + + assert alice_stakes == expected_alice_stake + + bob_stakes = subtensor.get_stake_for_coldkey(bob_wallet.coldkey.ss58_address) + + expected_bob_stake = [ + StakeInfo( + hotkey_ss58=alice_wallet.hotkey.ss58_address, + coldkey_ss58=bob_wallet.coldkey.ss58_address, + netuid=dave_subnet_netuid, + stake=get_dynamic_balance(bob_stakes[0].stake.rao, dave_subnet_netuid), + locked=Balance(0), + emission=get_dynamic_balance( + bob_stakes[0].emission.rao, dave_subnet_netuid + ), + drain=0, + is_registered=False, + ), + ] + assert bob_stakes == expected_bob_stake logging.console.success(f"✅ Test [green]test_transfer_stake[/green] passed") From c6f4c58b992b3ec6eace64660fc0f4047f81156d Mon Sep 17 00:00:00 2001 From: Roman Date: Sat, 10 May 2025 19:57:00 -0500 Subject: [PATCH 238/251] restore workflow to run all tests --- .github/workflows/e2e-subtensor-tests.yaml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/e2e-subtensor-tests.yaml b/.github/workflows/e2e-subtensor-tests.yaml index a2dc8246fe..ff48272227 100644 --- a/.github/workflows/e2e-subtensor-tests.yaml +++ b/.github/workflows/e2e-subtensor-tests.yaml @@ -41,9 +41,9 @@ jobs: - name: Find test files id: get-tests run: | - # test_files=$(find tests/e2e_tests -name "test*.py" | jq -R -s -c 'split("\n") | map(select(. != ""))') + test_files=$(find tests/e2e_tests -name "test*.py" | jq -R -s -c 'split("\n") | map(select(. != ""))') # keep it here for future debug - test_files=$(find tests/e2e_tests -type f -name "test*.py" | grep -E 'test_(hotkeys|staking)\.py$' | jq -R -s -c 'split("\n") | map(select(. != ""))') + # test_files=$(find tests/e2e_tests -type f -name "test*.py" | grep -E 'test_(hotkeys|staking)\.py$' | jq -R -s -c 'split("\n") | map(select(. != ""))') echo "test-files=$test_files" >> "$GITHUB_OUTPUT" shell: bash @@ -128,8 +128,9 @@ jobs: fi done + cron-run-non-fast-blocks-e2e-test: -# if: github.event_name == 'schedule' && needs.build-patched-image.outputs.image_built == 'true' + if: github.event_name == 'schedule' && needs.build-patched-image.outputs.image_built == 'true' name: "NFB: ${{ matrix.test-file }} / Python ${{ matrix.python-version }}" needs: - find-tests From c0750650ca45a6f10a96aa22865a9e2b65ca7527 Mon Sep 17 00:00:00 2001 From: Roman Date: Sat, 10 May 2025 20:00:17 -0500 Subject: [PATCH 239/251] add all supported wallets for tests --- tests/e2e_tests/conftest.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/e2e_tests/conftest.py b/tests/e2e_tests/conftest.py index 95d74fe871..ae3d11b50d 100644 --- a/tests/e2e_tests/conftest.py +++ b/tests/e2e_tests/conftest.py @@ -278,7 +278,19 @@ def bob_wallet(): return wallet +@pytest.fixture +def charlie_wallet(): + keypair, wallet = setup_wallet("//Charlie") + return wallet + + @pytest.fixture def dave_wallet(): keypair, wallet = setup_wallet("//Dave") return wallet + + +@pytest.fixture +def eve_wallet(): + keypair, wallet = setup_wallet("//Eve") + return wallet From 299ed91507ed3e51cd3044534f2c669be31425ce Mon Sep 17 00:00:00 2001 From: Roman Date: Sat, 10 May 2025 20:17:28 -0500 Subject: [PATCH 240/251] run all tests without cron --- .github/workflows/e2e-subtensor-tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/e2e-subtensor-tests.yaml b/.github/workflows/e2e-subtensor-tests.yaml index ff48272227..3136688a13 100644 --- a/.github/workflows/e2e-subtensor-tests.yaml +++ b/.github/workflows/e2e-subtensor-tests.yaml @@ -130,7 +130,7 @@ jobs: cron-run-non-fast-blocks-e2e-test: - if: github.event_name == 'schedule' && needs.build-patched-image.outputs.image_built == 'true' + if: github.event_name == 'schedule' name: "NFB: ${{ matrix.test-file }} / Python ${{ matrix.python-version }}" needs: - find-tests From 6669060865670daea30b183717a9c8d283be9497 Mon Sep 17 00:00:00 2001 From: Roman Date: Sat, 10 May 2025 20:23:30 -0500 Subject: [PATCH 241/251] run all tests without cron --- .github/workflows/e2e-subtensor-tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/e2e-subtensor-tests.yaml b/.github/workflows/e2e-subtensor-tests.yaml index 3136688a13..2f372ce80e 100644 --- a/.github/workflows/e2e-subtensor-tests.yaml +++ b/.github/workflows/e2e-subtensor-tests.yaml @@ -130,7 +130,7 @@ jobs: cron-run-non-fast-blocks-e2e-test: - if: github.event_name == 'schedule' + # if: github.event_name == 'schedule' name: "NFB: ${{ matrix.test-file }} / Python ${{ matrix.python-version }}" needs: - find-tests From f899c20a0bc78034fc2721619c0e1c93d5881918 Mon Sep 17 00:00:00 2001 From: Roman Date: Sat, 10 May 2025 23:14:49 -0500 Subject: [PATCH 242/251] add condition --- .github/workflows/e2e-subtensor-tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/e2e-subtensor-tests.yaml b/.github/workflows/e2e-subtensor-tests.yaml index 2f372ce80e..3136688a13 100644 --- a/.github/workflows/e2e-subtensor-tests.yaml +++ b/.github/workflows/e2e-subtensor-tests.yaml @@ -130,7 +130,7 @@ jobs: cron-run-non-fast-blocks-e2e-test: - # if: github.event_name == 'schedule' + if: github.event_name == 'schedule' name: "NFB: ${{ matrix.test-file }} / Python ${{ matrix.python-version }}" needs: - find-tests From ef2edcf5594bc0fa774720173bfa0ca6fcb8054c Mon Sep 17 00:00:00 2001 From: Roman Date: Sun, 11 May 2025 07:40:13 -0500 Subject: [PATCH 243/251] add step `Check if today is Saturday` --- .github/workflows/e2e-subtensor-tests.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/e2e-subtensor-tests.yaml b/.github/workflows/e2e-subtensor-tests.yaml index 3136688a13..494da0b011 100644 --- a/.github/workflows/e2e-subtensor-tests.yaml +++ b/.github/workflows/e2e-subtensor-tests.yaml @@ -148,6 +148,14 @@ jobs: python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] steps: + - name: Check if today is Saturday + run: | + day=$(date -u +%u) + echo "Today is weekday $day" + if [ "$day" -ne 6 ]; then + echo "⏭️ Skipping: not Saturday" + exit 78 + fi - name: Check-out repository uses: actions/checkout@v4 From e35013c7a0f05c83522fa5d35f90139d73fac8ae Mon Sep 17 00:00:00 2001 From: Roman Date: Mon, 12 May 2025 12:31:59 -0500 Subject: [PATCH 244/251] add forgotten field for legacy_methods --- bittensor/core/subtensor_api/utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/bittensor/core/subtensor_api/utils.py b/bittensor/core/subtensor_api/utils.py index 0002b60216..fdfde50697 100644 --- a/bittensor/core/subtensor_api/utils.py +++ b/bittensor/core/subtensor_api/utils.py @@ -115,6 +115,7 @@ def add_legacy_methods(subtensor: "SubtensorApi"): subtensor.min_allowed_weights = subtensor._subtensor.min_allowed_weights subtensor.move_stake = subtensor._subtensor.move_stake subtensor.network = subtensor._subtensor.network + subtensor.neurons = subtensor._subtensor.neurons subtensor.neuron_for_uid = subtensor._subtensor.neuron_for_uid subtensor.neurons_lite = subtensor._subtensor.neurons_lite subtensor.query_constant = subtensor._subtensor.query_constant From af3eafd4e1c06086771a53f576f8cdec08a1e162 Mon Sep 17 00:00:00 2001 From: Roman Date: Mon, 12 May 2025 12:32:33 -0500 Subject: [PATCH 245/251] update subtensor usage in e2e tests --- tests/e2e_tests/conftest.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/e2e_tests/conftest.py b/tests/e2e_tests/conftest.py index ae3d11b50d..eb407068ca 100644 --- a/tests/e2e_tests/conftest.py +++ b/tests/e2e_tests/conftest.py @@ -11,8 +11,7 @@ import pytest from async_substrate_interface import SubstrateInterface -from bittensor.core.async_subtensor import AsyncSubtensor -from bittensor.core.subtensor import Subtensor +from bittensor.core.subtensor_api import SubtensorApi from bittensor.utils.btlogging import logging from tests.e2e_tests.utils.e2e_test_utils import ( Templates, @@ -258,12 +257,12 @@ def templates(): @pytest.fixture def subtensor(local_chain): - return Subtensor(network="ws://localhost:9944") + return SubtensorApi(network="ws://localhost:9944", legacy_methods=True) @pytest.fixture def async_subtensor(local_chain): - return AsyncSubtensor(network="ws://localhost:9944") + return SubtensorApi(network="ws://localhost:9944", legacy_methods=True, async_subtensor=True) @pytest.fixture From 20c1ca1320630ae71221dc5476a807bdc103ffd1 Mon Sep 17 00:00:00 2001 From: Roman Date: Mon, 12 May 2025 12:32:49 -0500 Subject: [PATCH 246/251] improve README.md --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 4c07d1a739..e401bea455 100644 --- a/README.md +++ b/README.md @@ -234,6 +234,12 @@ pytest tests/unit_tests - using a compiler based on the substrait code - using an already built docker image (docker runner) +#### Local environment variables: +- `LOCALNET_SH_PATH` - path to `localnet.sh` script in cloned subtensor repository (for legacy runner); +- `BUILD_BINARY` - (`=0` or `=1`) - used with `LOCALNET_SH_PATH` for build or not before start localnet node (for legacy runner); +- `USE_DOCKER` - (`=0` or `=1`) - used if you want to use specific runner to run e2e tests (for docker runner); +- `FAST_BLOCKS` - (`=0` or `=1`) - allows you to run a localnet node in fast or non-fast blocks mode (for both types of runers). + #### Using `docker runner` (default for now): - E2E tests with docker image do not require preliminary compilation - are executed very quickly From b62cb6db9caddeae0b0c57aea7f1be90786be3fe Mon Sep 17 00:00:00 2001 From: Roman Date: Mon, 12 May 2025 13:03:27 -0500 Subject: [PATCH 247/251] update get_metagraph_info --- bittensor/core/async_subtensor.py | 28 +++++++++++++++++++++++----- bittensor/core/subtensor.py | 25 ++++++++++++++++++++++--- 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 7c290fa480..2772013f8b 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -1573,6 +1573,22 @@ async def get_metagraph_info( field_indices=[SelectiveMetagraphIndex.Name, SelectiveMetagraphIndex.OwnerHotkeys] ) """ + block_hash = await self.determine_block_hash(block, block_hash, reuse_block) + if not block_hash and reuse_block: + block_hash = self.substrate.last_block_hash + + if field_indices is None: + query = await self.substrate.runtime_call( + "SubnetInfoRuntimeApi", + "get_metagraph", + params=[netuid], + block_hash=block_hash, + ) + if query.value is None: + logging.error(f"Subnet {netuid} does not exist.") + return None + return MetagraphInfo.from_dict(query.value) + indexes = SelectiveMetagraphIndex.all_indices() if field_indices: @@ -1588,10 +1604,6 @@ async def get_metagraph_info( "`field_indices` must be a list of SelectiveMetagraphIndex items." ) - block_hash = await self.determine_block_hash(block, block_hash, reuse_block) - if not block_hash and reuse_block: - block_hash = self.substrate.last_block_hash - query = await self.substrate.runtime_call( "SubnetInfoRuntimeApi", "get_selective_metagraph", @@ -1601,7 +1613,13 @@ async def get_metagraph_info( if query.value is None: logging.error(f"Subnet {netuid} does not exist.") return None - return MetagraphInfo.from_dict(query.value) + meta: MetagraphInfo = MetagraphInfo.from_dict(query.value) + # TODO: remove this after SelectiveMetagraph is updated in mainnet. + if meta.netuid == 0 and meta.name != "root": + logging.warning( + "Do not use the 'field_indices' argument while you see this message. Mainnet update pending." + ) + return meta async def get_all_metagraphs_info( self, diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index 8dde2487af..efb8338ed6 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -1217,6 +1217,20 @@ def get_metagraph_info( field_indices=[SelectiveMetagraphIndex.Name, SelectiveMetagraphIndex.OwnerHotkeys] ) """ + block_hash = self.determine_block_hash(block) + + if field_indices is None: + query = self.substrate.runtime_call( + "SubnetInfoRuntimeApi", + "get_metagraph", + params=[netuid], + block_hash=block_hash, + ) + if query.value is None: + logging.error(f"Subnet {netuid} does not exist.") + return None + return MetagraphInfo.from_dict(query.value) + indexes = SelectiveMetagraphIndex.all_indices() if field_indices: @@ -1229,10 +1243,9 @@ def get_metagraph_info( ] else: raise ValueError( - "`field_indices` must be a list of SelectiveMetagraphIndex items." + "`field_indices` must be a list of SelectiveMetagraphIndex items or integers." ) - block_hash = self.determine_block_hash(block) query = self.substrate.runtime_call( "SubnetInfoRuntimeApi", "get_selective_metagraph", @@ -1242,7 +1255,13 @@ def get_metagraph_info( if query.value is None: logging.error(f"Subnet {netuid} does not exist.") return None - return MetagraphInfo.from_dict(query.value) + meta: MetagraphInfo = MetagraphInfo.from_dict(query.value) + # TODO: remove this after SelectiveMetagraph is updated in mainnet. + if meta.netuid == 0 and meta.name != "root": + logging.warning( + "Do not use the 'field_indices' argument while you see this message. Mainnet update pending." + ) + return meta def get_all_metagraphs_info( self, block: Optional[int] = None From 6c9355c3e0aae662f6e6571c52efd8ea539cc2fe Mon Sep 17 00:00:00 2001 From: Roman Date: Mon, 12 May 2025 13:11:23 -0500 Subject: [PATCH 248/251] ruff --- tests/e2e_tests/conftest.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/e2e_tests/conftest.py b/tests/e2e_tests/conftest.py index eb407068ca..ab1e840102 100644 --- a/tests/e2e_tests/conftest.py +++ b/tests/e2e_tests/conftest.py @@ -262,7 +262,9 @@ def subtensor(local_chain): @pytest.fixture def async_subtensor(local_chain): - return SubtensorApi(network="ws://localhost:9944", legacy_methods=True, async_subtensor=True) + return SubtensorApi( + network="ws://localhost:9944", legacy_methods=True, async_subtensor=True + ) @pytest.fixture From 547dbcb2ae0750cbb8fad9085008b79b6772f0d1 Mon Sep 17 00:00:00 2001 From: Roman Date: Mon, 12 May 2025 13:14:25 -0500 Subject: [PATCH 249/251] Temporarily return the previous implementation of get_metagraph_info without SelectiveMetagraph. --- bittensor/core/async_subtensor.py | 58 +++----------------------- bittensor/core/subtensor.py | 67 +++++-------------------------- 2 files changed, 15 insertions(+), 110 deletions(-) diff --git a/bittensor/core/async_subtensor.py b/bittensor/core/async_subtensor.py index 2772013f8b..1402e4c683 100644 --- a/bittensor/core/async_subtensor.py +++ b/bittensor/core/async_subtensor.py @@ -23,7 +23,6 @@ NeuronInfo, ProposalVoteData, StakeInfo, - SelectiveMetagraphIndex, SubnetHyperparameters, SubnetIdentity, SubnetInfo, @@ -1543,18 +1542,15 @@ async def get_minimum_required_stake(self): async def get_metagraph_info( self, netuid: int, - field_indices: Optional[list["SelectiveMetagraphIndex"]] = None, block: Optional[int] = None, block_hash: Optional[str] = None, reuse_block: bool = False, ) -> Optional[MetagraphInfo]: """ - Retrieves full or partial metagraph information for the specified subnet (netuid). + Retrieves the MetagraphInfo dataclass from the node for a single subnet (netuid) Arguments: - netuid: The NetUID of the subnet to query. - field_indices: An optional list of SelectiveMetagraphIndex values specifying which fields to retrieve. If - not provided, all available fields will be returned. + netuid: The NetUID of the subnet. block: the block number at which to retrieve the hyperparameter. Do not specify if using block_hash or reuse_block block_hash: The hash of blockchain block number for the query. Do not specify if using @@ -1562,64 +1558,22 @@ async def get_metagraph_info( reuse_block: Whether to reuse the last-used block hash. Do not set if using block_hash or block. Returns: - Optional[MetagraphInfo]: A MetagraphInfo object containing the requested subnet data, or None if the subnet - with the given netuid does not exist. - - Example: - meta_info = await subtensor.get_metagraph_info(netuid=2) - - partial_meta_info = await subtensor.get_metagraph_info( - netuid=2, - field_indices=[SelectiveMetagraphIndex.Name, SelectiveMetagraphIndex.OwnerHotkeys] - ) + MetagraphInfo dataclass """ block_hash = await self.determine_block_hash(block, block_hash, reuse_block) if not block_hash and reuse_block: block_hash = self.substrate.last_block_hash - if field_indices is None: - query = await self.substrate.runtime_call( - "SubnetInfoRuntimeApi", - "get_metagraph", - params=[netuid], - block_hash=block_hash, - ) - if query.value is None: - logging.error(f"Subnet {netuid} does not exist.") - return None - return MetagraphInfo.from_dict(query.value) - - indexes = SelectiveMetagraphIndex.all_indices() - - if field_indices: - if isinstance(field_indices, list) and all( - isinstance(f, (SelectiveMetagraphIndex, int)) for f in field_indices - ): - indexes = [ - f.value if isinstance(f, SelectiveMetagraphIndex) else f - for f in field_indices - ] - else: - raise ValueError( - "`field_indices` must be a list of SelectiveMetagraphIndex items." - ) - query = await self.substrate.runtime_call( "SubnetInfoRuntimeApi", - "get_selective_metagraph", - params=[netuid, indexes if 0 in indexes else [0] + indexes], + "get_metagraph", + params=[netuid], block_hash=block_hash, ) if query.value is None: logging.error(f"Subnet {netuid} does not exist.") return None - meta: MetagraphInfo = MetagraphInfo.from_dict(query.value) - # TODO: remove this after SelectiveMetagraph is updated in mainnet. - if meta.netuid == 0 and meta.name != "root": - logging.warning( - "Do not use the 'field_indices' argument while you see this message. Mainnet update pending." - ) - return meta + return MetagraphInfo.from_dict(query.value) async def get_all_metagraphs_info( self, diff --git a/bittensor/core/subtensor.py b/bittensor/core/subtensor.py index efb8338ed6..63634d9053 100644 --- a/bittensor/core/subtensor.py +++ b/bittensor/core/subtensor.py @@ -21,7 +21,6 @@ MetagraphInfo, NeuronInfo, NeuronInfoLite, - SelectiveMetagraphIndex, StakeInfo, SubnetInfo, SubnetIdentity, @@ -1190,78 +1189,30 @@ def get_minimum_required_stake(self) -> Balance: return Balance.from_rao(getattr(result, "value", 0)) def get_metagraph_info( - self, - netuid: int, - field_indices: Optional[list[Union["SelectiveMetagraphIndex", int]]] = None, - block: Optional[int] = None, + self, netuid: int, block: Optional[int] = None ) -> Optional[MetagraphInfo]: """ - Retrieves full or partial metagraph information for the specified subnet (netuid). + Retrieves the MetagraphInfo dataclass from the node for a single subnet (netuid) Arguments: - netuid: The NetUID of the subnet to query. - field_indices: An optional list of SelectiveMetagraphIndex values specifying which fields to retrieve. If - not provided, all available fields will be returned. - block: The block number at which to query the data. If not specified, the current block or one determined - via reuse_block or block_hash will be used. + netuid: The NetUID of the subnet. + block: the block number at which to retrieve the hyperparameter. Do not specify if using block_hash or + reuse_block Returns: - Optional[MetagraphInfo]: A MetagraphInfo object containing the requested subnet data, or None if the subnet - with the given netuid does not exist. - - Example: - meta_info = subtensor.get_metagraph_info(netuid=2) - - partial_meta_info = subtensor.get_metagraph_info( - netuid=2, - field_indices=[SelectiveMetagraphIndex.Name, SelectiveMetagraphIndex.OwnerHotkeys] - ) + MetagraphInfo dataclass """ block_hash = self.determine_block_hash(block) - - if field_indices is None: - query = self.substrate.runtime_call( - "SubnetInfoRuntimeApi", - "get_metagraph", - params=[netuid], - block_hash=block_hash, - ) - if query.value is None: - logging.error(f"Subnet {netuid} does not exist.") - return None - return MetagraphInfo.from_dict(query.value) - - indexes = SelectiveMetagraphIndex.all_indices() - - if field_indices: - if isinstance(field_indices, list) and all( - isinstance(f, (SelectiveMetagraphIndex, int)) for f in field_indices - ): - indexes = [ - f.value if isinstance(f, SelectiveMetagraphIndex) else f - for f in field_indices - ] - else: - raise ValueError( - "`field_indices` must be a list of SelectiveMetagraphIndex items or integers." - ) - query = self.substrate.runtime_call( "SubnetInfoRuntimeApi", - "get_selective_metagraph", - params=[netuid, indexes if 0 in indexes else [0] + indexes], + "get_metagraph", + params=[netuid], block_hash=block_hash, ) if query.value is None: logging.error(f"Subnet {netuid} does not exist.") return None - meta: MetagraphInfo = MetagraphInfo.from_dict(query.value) - # TODO: remove this after SelectiveMetagraph is updated in mainnet. - if meta.netuid == 0 and meta.name != "root": - logging.warning( - "Do not use the 'field_indices' argument while you see this message. Mainnet update pending." - ) - return meta + return MetagraphInfo.from_dict(query.value) def get_all_metagraphs_info( self, block: Optional[int] = None From 850170bdf278ddcfca4b3718dafb68011df0c232 Mon Sep 17 00:00:00 2001 From: Roman Date: Mon, 12 May 2025 13:19:45 -0500 Subject: [PATCH 250/251] comment selective metagraph related tests --- tests/e2e_tests/test_metagraph.py | 453 ++++++++++++----------- tests/unit_tests/test_async_subtensor.py | 171 ++++----- tests/unit_tests/test_subtensor.py | 165 +++++---- 3 files changed, 396 insertions(+), 393 deletions(-) diff --git a/tests/e2e_tests/test_metagraph.py b/tests/e2e_tests/test_metagraph.py index 104c2ffceb..c3c485ebbe 100644 --- a/tests/e2e_tests/test_metagraph.py +++ b/tests/e2e_tests/test_metagraph.py @@ -435,232 +435,233 @@ def test_metagraph_info(subtensor, alice_wallet, bob_wallet): assert metagraph_info is None -def test_metagraph_info_with_indexes(subtensor, alice_wallet, bob_wallet): - """ - Tests: - - Check MetagraphInfo - - Register Neuron - - Register Subnet - - Check MetagraphInfo is updated - """ - - alice_subnet_netuid = subtensor.get_total_subnets() # 2 - subtensor.register_subnet(alice_wallet, True, True) - - field_indices = [ - SelectiveMetagraphIndex.Name, - SelectiveMetagraphIndex.Active, - SelectiveMetagraphIndex.OwnerHotkey, - SelectiveMetagraphIndex.OwnerColdkey, - SelectiveMetagraphIndex.Axons, - ] - - metagraph_info = subtensor.get_metagraph_info( - netuid=alice_subnet_netuid, field_indices=field_indices - ) - - assert metagraph_info == MetagraphInfo( - netuid=alice_subnet_netuid, - name="omron", - owner_hotkey=alice_wallet.hotkey.ss58_address, - owner_coldkey=alice_wallet.coldkey.ss58_address, - active=(True,), - axons=( - { - "block": 0, - "ip": 0, - "ip_type": 0, - "placeholder1": 0, - "placeholder2": 0, - "port": 0, - "protocol": 0, - "version": 0, - }, - ), - symbol=None, - identity=None, - network_registered_at=None, - block=None, - tempo=None, - last_step=None, - blocks_since_last_step=None, - subnet_emission=None, - alpha_in=None, - alpha_out=None, - tao_in=None, - alpha_out_emission=None, - alpha_in_emission=None, - tao_in_emission=None, - pending_alpha_emission=None, - pending_root_emission=None, - subnet_volume=None, - moving_price=None, - rho=None, - kappa=None, - min_allowed_weights=None, - max_weights_limit=None, - weights_version=None, - weights_rate_limit=None, - activity_cutoff=None, - max_validators=None, - num_uids=None, - max_uids=None, - burn=None, - difficulty=None, - registration_allowed=None, - pow_registration_allowed=None, - immunity_period=None, - min_difficulty=None, - max_difficulty=None, - min_burn=None, - max_burn=None, - adjustment_alpha=None, - adjustment_interval=None, - target_regs_per_interval=None, - max_regs_per_block=None, - serving_rate_limit=None, - commit_reveal_weights_enabled=None, - commit_reveal_period=None, - liquid_alpha_enabled=None, - alpha_high=None, - alpha_low=None, - bonds_moving_avg=None, - hotkeys=None, - coldkeys=None, - identities=None, - validator_permit=None, - pruning_score=None, - last_update=None, - emission=None, - dividends=None, - incentives=None, - consensus=None, - trust=None, - rank=None, - block_at_registration=None, - alpha_stake=None, - tao_stake=None, - total_stake=None, - tao_dividends_per_hotkey=None, - alpha_dividends_per_hotkey=None, - ) - - assert wait_to_start_call(subtensor, alice_wallet, alice_subnet_netuid) - - assert subtensor.burned_register( - bob_wallet, - netuid=alice_subnet_netuid, - wait_for_inclusion=True, - wait_for_finalization=True, - ) - - fields = [ - SelectiveMetagraphIndex.Name, - SelectiveMetagraphIndex.Active, - SelectiveMetagraphIndex.OwnerHotkey, - SelectiveMetagraphIndex.OwnerColdkey, - SelectiveMetagraphIndex.Axons, - ] - - metagraph_info = subtensor.get_metagraph_info( - netuid=alice_subnet_netuid, field_indices=fields - ) - - assert metagraph_info == MetagraphInfo( - netuid=alice_subnet_netuid, - name="omron", - owner_hotkey=alice_wallet.hotkey.ss58_address, - owner_coldkey=alice_wallet.coldkey.ss58_address, - active=(True, True), - axons=( - { - "block": 0, - "ip": 0, - "ip_type": 0, - "placeholder1": 0, - "placeholder2": 0, - "port": 0, - "protocol": 0, - "version": 0, - }, - { - "block": 0, - "ip": 0, - "ip_type": 0, - "placeholder1": 0, - "placeholder2": 0, - "port": 0, - "protocol": 0, - "version": 0, - }, - ), - symbol=None, - identity=None, - network_registered_at=None, - block=None, - tempo=None, - last_step=None, - blocks_since_last_step=None, - subnet_emission=None, - alpha_in=None, - alpha_out=None, - tao_in=None, - alpha_out_emission=None, - alpha_in_emission=None, - tao_in_emission=None, - pending_alpha_emission=None, - pending_root_emission=None, - subnet_volume=None, - moving_price=None, - rho=None, - kappa=None, - min_allowed_weights=None, - max_weights_limit=None, - weights_version=None, - weights_rate_limit=None, - activity_cutoff=None, - max_validators=None, - num_uids=None, - max_uids=None, - burn=None, - difficulty=None, - registration_allowed=None, - pow_registration_allowed=None, - immunity_period=None, - min_difficulty=None, - max_difficulty=None, - min_burn=None, - max_burn=None, - adjustment_alpha=None, - adjustment_interval=None, - target_regs_per_interval=None, - max_regs_per_block=None, - serving_rate_limit=None, - commit_reveal_weights_enabled=None, - commit_reveal_period=None, - liquid_alpha_enabled=None, - alpha_high=None, - alpha_low=None, - bonds_moving_avg=None, - hotkeys=None, - coldkeys=None, - identities=None, - validator_permit=None, - pruning_score=None, - last_update=None, - emission=None, - dividends=None, - incentives=None, - consensus=None, - trust=None, - rank=None, - block_at_registration=None, - alpha_stake=None, - tao_stake=None, - total_stake=None, - tao_dividends_per_hotkey=None, - alpha_dividends_per_hotkey=None, - ) +# TODO: get back after SelectiveMetagraph come to the mainnet +# def test_metagraph_info_with_indexes(subtensor, alice_wallet, bob_wallet): +# """ +# Tests: +# - Check MetagraphInfo +# - Register Neuron +# - Register Subnet +# - Check MetagraphInfo is updated +# """ +# +# alice_subnet_netuid = subtensor.get_total_subnets() # 2 +# subtensor.register_subnet(alice_wallet, True, True) +# +# field_indices = [ +# SelectiveMetagraphIndex.Name, +# SelectiveMetagraphIndex.Active, +# SelectiveMetagraphIndex.OwnerHotkey, +# SelectiveMetagraphIndex.OwnerColdkey, +# SelectiveMetagraphIndex.Axons, +# ] +# +# metagraph_info = subtensor.get_metagraph_info( +# netuid=alice_subnet_netuid, field_indices=field_indices +# ) +# +# assert metagraph_info == MetagraphInfo( +# netuid=alice_subnet_netuid, +# name="omron", +# owner_hotkey=alice_wallet.hotkey.ss58_address, +# owner_coldkey=alice_wallet.coldkey.ss58_address, +# active=(True,), +# axons=( +# { +# "block": 0, +# "ip": 0, +# "ip_type": 0, +# "placeholder1": 0, +# "placeholder2": 0, +# "port": 0, +# "protocol": 0, +# "version": 0, +# }, +# ), +# symbol=None, +# identity=None, +# network_registered_at=None, +# block=None, +# tempo=None, +# last_step=None, +# blocks_since_last_step=None, +# subnet_emission=None, +# alpha_in=None, +# alpha_out=None, +# tao_in=None, +# alpha_out_emission=None, +# alpha_in_emission=None, +# tao_in_emission=None, +# pending_alpha_emission=None, +# pending_root_emission=None, +# subnet_volume=None, +# moving_price=None, +# rho=None, +# kappa=None, +# min_allowed_weights=None, +# max_weights_limit=None, +# weights_version=None, +# weights_rate_limit=None, +# activity_cutoff=None, +# max_validators=None, +# num_uids=None, +# max_uids=None, +# burn=None, +# difficulty=None, +# registration_allowed=None, +# pow_registration_allowed=None, +# immunity_period=None, +# min_difficulty=None, +# max_difficulty=None, +# min_burn=None, +# max_burn=None, +# adjustment_alpha=None, +# adjustment_interval=None, +# target_regs_per_interval=None, +# max_regs_per_block=None, +# serving_rate_limit=None, +# commit_reveal_weights_enabled=None, +# commit_reveal_period=None, +# liquid_alpha_enabled=None, +# alpha_high=None, +# alpha_low=None, +# bonds_moving_avg=None, +# hotkeys=None, +# coldkeys=None, +# identities=None, +# validator_permit=None, +# pruning_score=None, +# last_update=None, +# emission=None, +# dividends=None, +# incentives=None, +# consensus=None, +# trust=None, +# rank=None, +# block_at_registration=None, +# alpha_stake=None, +# tao_stake=None, +# total_stake=None, +# tao_dividends_per_hotkey=None, +# alpha_dividends_per_hotkey=None, +# ) +# +# assert wait_to_start_call(subtensor, alice_wallet, alice_subnet_netuid) +# +# assert subtensor.burned_register( +# bob_wallet, +# netuid=alice_subnet_netuid, +# wait_for_inclusion=True, +# wait_for_finalization=True, +# ) +# +# fields = [ +# SelectiveMetagraphIndex.Name, +# SelectiveMetagraphIndex.Active, +# SelectiveMetagraphIndex.OwnerHotkey, +# SelectiveMetagraphIndex.OwnerColdkey, +# SelectiveMetagraphIndex.Axons, +# ] +# +# metagraph_info = subtensor.get_metagraph_info( +# netuid=alice_subnet_netuid, field_indices=fields +# ) +# +# assert metagraph_info == MetagraphInfo( +# netuid=alice_subnet_netuid, +# name="omron", +# owner_hotkey=alice_wallet.hotkey.ss58_address, +# owner_coldkey=alice_wallet.coldkey.ss58_address, +# active=(True, True), +# axons=( +# { +# "block": 0, +# "ip": 0, +# "ip_type": 0, +# "placeholder1": 0, +# "placeholder2": 0, +# "port": 0, +# "protocol": 0, +# "version": 0, +# }, +# { +# "block": 0, +# "ip": 0, +# "ip_type": 0, +# "placeholder1": 0, +# "placeholder2": 0, +# "port": 0, +# "protocol": 0, +# "version": 0, +# }, +# ), +# symbol=None, +# identity=None, +# network_registered_at=None, +# block=None, +# tempo=None, +# last_step=None, +# blocks_since_last_step=None, +# subnet_emission=None, +# alpha_in=None, +# alpha_out=None, +# tao_in=None, +# alpha_out_emission=None, +# alpha_in_emission=None, +# tao_in_emission=None, +# pending_alpha_emission=None, +# pending_root_emission=None, +# subnet_volume=None, +# moving_price=None, +# rho=None, +# kappa=None, +# min_allowed_weights=None, +# max_weights_limit=None, +# weights_version=None, +# weights_rate_limit=None, +# activity_cutoff=None, +# max_validators=None, +# num_uids=None, +# max_uids=None, +# burn=None, +# difficulty=None, +# registration_allowed=None, +# pow_registration_allowed=None, +# immunity_period=None, +# min_difficulty=None, +# max_difficulty=None, +# min_burn=None, +# max_burn=None, +# adjustment_alpha=None, +# adjustment_interval=None, +# target_regs_per_interval=None, +# max_regs_per_block=None, +# serving_rate_limit=None, +# commit_reveal_weights_enabled=None, +# commit_reveal_period=None, +# liquid_alpha_enabled=None, +# alpha_high=None, +# alpha_low=None, +# bonds_moving_avg=None, +# hotkeys=None, +# coldkeys=None, +# identities=None, +# validator_permit=None, +# pruning_score=None, +# last_update=None, +# emission=None, +# dividends=None, +# incentives=None, +# consensus=None, +# trust=None, +# rank=None, +# block_at_registration=None, +# alpha_stake=None, +# tao_stake=None, +# total_stake=None, +# tao_dividends_per_hotkey=None, +# alpha_dividends_per_hotkey=None, +# ) def test_blocks(subtensor): diff --git a/tests/unit_tests/test_async_subtensor.py b/tests/unit_tests/test_async_subtensor.py index 8c0c5c6288..0449a7b8ef 100644 --- a/tests/unit_tests/test_async_subtensor.py +++ b/tests/unit_tests/test_async_subtensor.py @@ -3070,91 +3070,92 @@ async def test_start_call(subtensor, mocker): assert result == mocked_extrinsic.return_value -@pytest.mark.asyncio -async def test_get_metagraph_info_all_fields(subtensor, mocker): - """Test get_metagraph_info with all fields (default behavior).""" - # Preps - netuid = 1 - mock_value = {"mock": "data"} - - mock_runtime_call = mocker.patch.object( - subtensor.substrate, - "runtime_call", - return_value=mocker.AsyncMock(value=mock_value), - ) - mock_from_dict = mocker.patch.object( - async_subtensor.MetagraphInfo, "from_dict", return_value="parsed_metagraph" - ) - - # Call - result = await subtensor.get_metagraph_info(netuid=netuid) - - # Asserts - assert result == "parsed_metagraph" - mock_runtime_call.assert_awaited_once_with( - "SubnetInfoRuntimeApi", - "get_selective_metagraph", - params=[netuid, SelectiveMetagraphIndex.all_indices()], - block_hash=await subtensor.determine_block_hash(None), - ) - mock_from_dict.assert_called_once_with(mock_value) - - -@pytest.mark.asyncio -async def test_get_metagraph_info_specific_fields(subtensor, mocker): - """Test get_metagraph_info with specific fields.""" - # Preps - netuid = 1 - mock_value = {"mock": "data"} - fields = [SelectiveMetagraphIndex.Name, 5] - - mock_runtime_call = mocker.patch.object( - subtensor.substrate, - "runtime_call", - return_value=mocker.AsyncMock(value=mock_value), - ) - mock_from_dict = mocker.patch.object( - async_subtensor.MetagraphInfo, "from_dict", return_value="parsed_metagraph" - ) - - # Call - result = await subtensor.get_metagraph_info(netuid=netuid, field_indices=fields) - - # Asserts - assert result == "parsed_metagraph" - mock_runtime_call.assert_awaited_once_with( - "SubnetInfoRuntimeApi", - "get_selective_metagraph", - params=[ - netuid, - [0] - + [ - f.value if isinstance(f, SelectiveMetagraphIndex) else f for f in fields - ], - ], - block_hash=await subtensor.determine_block_hash(None), - ) - mock_from_dict.assert_called_once_with(mock_value) - - -@pytest.mark.parametrize( - "wrong_fields", - [ - [ - "invalid", - ], - [SelectiveMetagraphIndex.Active, 1, "f"], - [1, 2, 3, "f"], - ], -) -@pytest.mark.asyncio -async def test_get_metagraph_info_invalid_field_indices(subtensor, wrong_fields): - """Test get_metagraph_info raises ValueError on invalid field_indices.""" - with pytest.raises( - ValueError, - match="`field_indices` must be a list of SelectiveMetagraphIndex items.", - ): - await subtensor.get_metagraph_info(netuid=1, field_indices=wrong_fields) +# TODO: get back after SelectiveMetagraph come to the mainnet +# @pytest.mark.asyncio +# async def test_get_metagraph_info_all_fields(subtensor, mocker): +# """Test get_metagraph_info with all fields (default behavior).""" +# # Preps +# netuid = 1 +# mock_value = {"mock": "data"} +# +# mock_runtime_call = mocker.patch.object( +# subtensor.substrate, +# "runtime_call", +# return_value=mocker.AsyncMock(value=mock_value), +# ) +# mock_from_dict = mocker.patch.object( +# async_subtensor.MetagraphInfo, "from_dict", return_value="parsed_metagraph" +# ) +# +# # Call +# result = await subtensor.get_metagraph_info(netuid=netuid) +# +# # Asserts +# assert result == "parsed_metagraph" +# mock_runtime_call.assert_awaited_once_with( +# "SubnetInfoRuntimeApi", +# "get_selective_metagraph", +# params=[netuid, SelectiveMetagraphIndex.all_indices()], +# block_hash=await subtensor.determine_block_hash(None), +# ) +# mock_from_dict.assert_called_once_with(mock_value) +# +# +# @pytest.mark.asyncio +# async def test_get_metagraph_info_specific_fields(subtensor, mocker): +# """Test get_metagraph_info with specific fields.""" +# # Preps +# netuid = 1 +# mock_value = {"mock": "data"} +# fields = [SelectiveMetagraphIndex.Name, 5] +# +# mock_runtime_call = mocker.patch.object( +# subtensor.substrate, +# "runtime_call", +# return_value=mocker.AsyncMock(value=mock_value), +# ) +# mock_from_dict = mocker.patch.object( +# async_subtensor.MetagraphInfo, "from_dict", return_value="parsed_metagraph" +# ) +# +# # Call +# result = await subtensor.get_metagraph_info(netuid=netuid, field_indices=fields) +# +# # Asserts +# assert result == "parsed_metagraph" +# mock_runtime_call.assert_awaited_once_with( +# "SubnetInfoRuntimeApi", +# "get_selective_metagraph", +# params=[ +# netuid, +# [0] +# + [ +# f.value if isinstance(f, SelectiveMetagraphIndex) else f for f in fields +# ], +# ], +# block_hash=await subtensor.determine_block_hash(None), +# ) +# mock_from_dict.assert_called_once_with(mock_value) +# +# +# @pytest.mark.parametrize( +# "wrong_fields", +# [ +# [ +# "invalid", +# ], +# [SelectiveMetagraphIndex.Active, 1, "f"], +# [1, 2, 3, "f"], +# ], +# ) +# @pytest.mark.asyncio +# async def test_get_metagraph_info_invalid_field_indices(subtensor, wrong_fields): +# """Test get_metagraph_info raises ValueError on invalid field_indices.""" +# with pytest.raises( +# ValueError, +# match="`field_indices` must be a list of SelectiveMetagraphIndex items.", +# ): +# await subtensor.get_metagraph_info(netuid=1, field_indices=wrong_fields) @pytest.mark.asyncio diff --git a/tests/unit_tests/test_subtensor.py b/tests/unit_tests/test_subtensor.py index 625a6aec62..9085bf1122 100644 --- a/tests/unit_tests/test_subtensor.py +++ b/tests/unit_tests/test_subtensor.py @@ -3403,88 +3403,89 @@ def test_start_call(subtensor, mocker): assert result == mocked_extrinsic.return_value -def test_get_metagraph_info_all_fields(subtensor, mocker): - """Test get_metagraph_info with all fields (default behavior).""" - # Preps - netuid = 1 - mock_value = {"mock": "data"} - - mock_runtime_call = mocker.patch.object( - subtensor.substrate, - "runtime_call", - return_value=mocker.Mock(value=mock_value), - ) - mock_from_dict = mocker.patch.object( - subtensor_module.MetagraphInfo, "from_dict", return_value="parsed_metagraph" - ) - - # Call - result = subtensor.get_metagraph_info(netuid=netuid) - - # Asserts - assert result == "parsed_metagraph" - mock_runtime_call.assert_called_once_with( - "SubnetInfoRuntimeApi", - "get_selective_metagraph", - params=[netuid, SelectiveMetagraphIndex.all_indices()], - block_hash=subtensor.determine_block_hash(None), - ) - mock_from_dict.assert_called_once_with(mock_value) - - -def test_get_metagraph_info_specific_fields(subtensor, mocker): - """Test get_metagraph_info with specific fields.""" - # Preps - netuid = 1 - mock_value = {"mock": "data"} - fields = [SelectiveMetagraphIndex.Name, 5] - - mock_runtime_call = mocker.patch.object( - subtensor.substrate, - "runtime_call", - return_value=mocker.Mock(value=mock_value), - ) - mock_from_dict = mocker.patch.object( - subtensor_module.MetagraphInfo, "from_dict", return_value="parsed_metagraph" - ) - - # Call - result = subtensor.get_metagraph_info(netuid=netuid, field_indices=fields) - - # Asserts - assert result == "parsed_metagraph" - mock_runtime_call.assert_called_once_with( - "SubnetInfoRuntimeApi", - "get_selective_metagraph", - params=[ - netuid, - [0] - + [ - f.value if isinstance(f, SelectiveMetagraphIndex) else f for f in fields - ], - ], - block_hash=subtensor.determine_block_hash(None), - ) - mock_from_dict.assert_called_once_with(mock_value) - - -@pytest.mark.parametrize( - "wrong_fields", - [ - [ - "invalid", - ], - [SelectiveMetagraphIndex.Active, 1, "f"], - [1, 2, 3, "f"], - ], -) -def test_get_metagraph_info_invalid_field_indices(subtensor, wrong_fields): - """Test get_metagraph_info raises ValueError on invalid field_indices.""" - with pytest.raises( - ValueError, - match="`field_indices` must be a list of SelectiveMetagraphIndex items.", - ): - subtensor.get_metagraph_info(netuid=1, field_indices=wrong_fields) +# TODO: get back after SelectiveMetagraph come to the mainnet +# def test_get_metagraph_info_all_fields(subtensor, mocker): +# """Test get_metagraph_info with all fields (default behavior).""" +# # Preps +# netuid = 1 +# mock_value = {"mock": "data"} +# +# mock_runtime_call = mocker.patch.object( +# subtensor.substrate, +# "runtime_call", +# return_value=mocker.Mock(value=mock_value), +# ) +# mock_from_dict = mocker.patch.object( +# subtensor_module.MetagraphInfo, "from_dict", return_value="parsed_metagraph" +# ) +# +# # Call +# result = subtensor.get_metagraph_info(netuid=netuid) +# +# # Asserts +# assert result == "parsed_metagraph" +# mock_runtime_call.assert_called_once_with( +# "SubnetInfoRuntimeApi", +# "get_selective_metagraph", +# params=[netuid, SelectiveMetagraphIndex.all_indices()], +# block_hash=subtensor.determine_block_hash(None), +# ) +# mock_from_dict.assert_called_once_with(mock_value) +# +# +# def test_get_metagraph_info_specific_fields(subtensor, mocker): +# """Test get_metagraph_info with specific fields.""" +# # Preps +# netuid = 1 +# mock_value = {"mock": "data"} +# fields = [SelectiveMetagraphIndex.Name, 5] +# +# mock_runtime_call = mocker.patch.object( +# subtensor.substrate, +# "runtime_call", +# return_value=mocker.Mock(value=mock_value), +# ) +# mock_from_dict = mocker.patch.object( +# subtensor_module.MetagraphInfo, "from_dict", return_value="parsed_metagraph" +# ) +# +# # Call +# result = subtensor.get_metagraph_info(netuid=netuid, field_indices=fields) +# +# # Asserts +# assert result == "parsed_metagraph" +# mock_runtime_call.assert_called_once_with( +# "SubnetInfoRuntimeApi", +# "get_selective_metagraph", +# params=[ +# netuid, +# [0] +# + [ +# f.value if isinstance(f, SelectiveMetagraphIndex) else f for f in fields +# ], +# ], +# block_hash=subtensor.determine_block_hash(None), +# ) +# mock_from_dict.assert_called_once_with(mock_value) +# +# +# @pytest.mark.parametrize( +# "wrong_fields", +# [ +# [ +# "invalid", +# ], +# [SelectiveMetagraphIndex.Active, 1, "f"], +# [1, 2, 3, "f"], +# ], +# ) +# def test_get_metagraph_info_invalid_field_indices(subtensor, wrong_fields): +# """Test get_metagraph_info raises ValueError on invalid field_indices.""" +# with pytest.raises( +# ValueError, +# match="`field_indices` must be a list of SelectiveMetagraphIndex items.", +# ): +# subtensor.get_metagraph_info(netuid=1, field_indices=wrong_fields) def test_get_metagraph_info_subnet_not_exist(subtensor, mocker): From 0852953262f92a86a1a379c92a984a5e041dabb1 Mon Sep 17 00:00:00 2001 From: ibraheem-opentensor Date: Mon, 12 May 2025 14:49:46 -0700 Subject: [PATCH 251/251] Updates changelog and version --- CHANGELOG.md | 39 +++++++++++++++++++++++++++++++++++++++ pyproject.toml | 2 +- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d4ca830d5..69844e7def 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,44 @@ # Changelog +## 9.5.0 /2025-05-12 + +## What's Changed +* Release/9.4.0 by @ibraheem-abe in https://github.com/opentensor/bittensor/pull/2837 +* Update subnet units by @thewhaleking in https://github.com/opentensor/bittensor/pull/2838 +* Add `force_register_neuron` into MockSubtensor by @basfroman in https://github.com/opentensor/bittensor/pull/2839 +* Add `Monitor-Requirements-Size` workflow by @basfroman in https://github.com/opentensor/bittensor/pull/2842 +* Add `SelectiveMetagraph` interface into SDK by @basfroman in https://github.com/opentensor/bittensor/pull/2846 +* Update docs for unstake amount by @thewhaleking in https://github.com/opentensor/bittensor/pull/2845 +* Add one more attempt to e2e tests by @basfroman in https://github.com/opentensor/bittensor/pull/2849 +* Fix typos in test documentation and docstrings by @leopardracer in https://github.com/opentensor/bittensor/pull/2848 +* Add bittensor-drand==0.5.0 by @basfroman in https://github.com/opentensor/bittensor/pull/2835 +* Extend selective metagraph logic by @basfroman in https://github.com/opentensor/bittensor/pull/2852 +* fix: $BASH_ENV loading issue by @defitricks in https://github.com/opentensor/bittensor/pull/2851 +* Update github actions versions due to deprecation by @PixelPil0t1 in https://github.com/opentensor/bittensor/pull/2850 +* Add methods to Async/Subtensor class by @basfroman in https://github.com/opentensor/bittensor/pull/2854 +* Cleanup, refactoring, small fix, improvement by @basfroman in https://github.com/opentensor/bittensor/pull/2856 +* Add `period` argument to extrinsics calls by @basfroman in https://github.com/opentensor/bittensor/pull/2857 +* Add nightly run of e2e tests by @basfroman in https://github.com/opentensor/bittensor/pull/2858 +* `period=16` for fast blocks test by @basfroman in https://github.com/opentensor/bittensor/pull/2859 +* Fix some unittests warnings by @zyzniewski-reef in https://github.com/opentensor/bittensor/pull/2774 +* docs: fix typos in `README.md` by @gap-editor in https://github.com/opentensor/bittensor/pull/2861 +* Introduce `SubtensorApi` interface for SDK by @basfroman in https://github.com/opentensor/bittensor/pull/2862 +* Adds `__all__` to easy_imports.py to get rid of all the #noqa stuff by @thewhaleking in https://github.com/opentensor/bittensor/pull/2863 +* `🥩s` to `staking` by @basfroman in https://github.com/opentensor/bittensor/pull/2864 +* Fix `get_next_epoch_start_block` by @basfroman in https://github.com/opentensor/bittensor/pull/2865 +* docs: fix dead link by @GarmashAlex in https://github.com/opentensor/bittensor/pull/2866 +* Add `non-fast-blocks` e2e tests each Saturday by @basfroman in https://github.com/opentensor/bittensor/pull/2860 +* Selective-metagraph -> MetagraphInfo by @basfroman in https://github.com/opentensor/bittensor/pull/2870 +* Improve e2e tests and fix bug in SubtensorApi by @basfroman in https://github.com/opentensor/bittensor/pull/2869 + +## New Contributors +* @defitricks made their first contribution in https://github.com/opentensor/bittensor/pull/2851 +* @PixelPil0t1 made their first contribution in https://github.com/opentensor/bittensor/pull/2850 +* @gap-editor made their first contribution in https://github.com/opentensor/bittensor/pull/2861 +* @GarmashAlex made their first contribution in https://github.com/opentensor/bittensor/pull/2866 + +**Full Changelog**: https://github.com/opentensor/bittensor/compare/v9.4.0...v9.5.0 + ## 9.4.0 /2025-04-17 ## What's Changed diff --git a/pyproject.toml b/pyproject.toml index fe5d089278..a9b9db6a01 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "bittensor" -version = "9.4.0" +version = "9.5.0" description = "Bittensor" readme = "README.md" authors = [