From ba0a263ad70dec53e16845d8b7d888256b52a64d Mon Sep 17 00:00:00 2001 From: philanthrope Date: Mon, 30 Oct 2023 18:53:48 -0400 Subject: [PATCH 1/8] Release/6.2.0 (#1567) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * (un)Staking multiple avoid tx limit (#1244) * add tx rate limit * wait for tx limit if not done multi stake/unstake * dont "decrypt" hotkey * additional logging for prometheus * additional logging for prometheus (#1246) * Dataset fix (#1249) * fix * added try except * Grab delegates details from GitHub (#1245) * add url to init * add dataclass and util functions * use in cli * remove delegates json --------- Co-authored-by: joeylegere * Add raw spec for local test and new bins (#1243) * add spec and new bins * fix config netuid * use dot get * check if config netuid is list * add start to mockstatus * add attr to mock neuron * add info to mock from neurons * change ordering of neuron dict to namespace * remove test for wandb for axon * use regex for looser match * fix blacklist metagraph mock * use real mock netuid * use mock network and netuid in constructor * fix patch * patch delegate check * use mock network and netuid * remove check for wallet hotkey * fix tests for subtensor init * dont set netuid for overview test * typo in docstring * add mock status stop * add low mock tx limit * oops typo * use dot get * add wait for final and incl args * use args during setup * update bins and use 100ms blocktime * pass block arg * remove bittensor.logging and a old test * use random port * backward fix * fix block time to 1s * compile no symb on linux * compile no symb mac * remove useless init on var * use dot get for new flags * update test durations * update test durations * use dot get for config * output error msg * mock to_default * remove to defaults in help * reduce neruons, remove flaky test * deactivate test * mvoe key pair tests out of the subtensor interface --------- Co-authored-by: Eugene * Fix list_delegates on non-archive nodes (#1232) * Change how pull of archival data is handled * fix for list_delegates too * . * use empty dict * fix spacing * specify exception * log out * add space in log message * use warning instead * Blacklist fixes + depreciation of old signatures (#1240) * fixes blacklist error message + remove * remove checks for parse signature * remove sign v1 tests * fix for the syanpse checks * fix tests and remove duplicate sign * Improve development workflow documentation * [BIT-636] Change u16 weight normalization to max-upscaling (#1241) * Change u16 weight normalization to max-upscaling Use full u16 bitwidth so that max_weight=U16_MAX, then rely on subtensor epoch to properly normalize weights in I32F32. This means that weights submission extrinsic to subtensor does not have to be pre-normalized. * Skip zero sum in weight conversion * Round u16 weights * remove duplicate command #1228 (#1231) * remove duplicate command #1228 * Extract create_parser for cli testing * mark as private * use in tests and test for duplicates * fix test using mock prompt answers * test_forward_priority_2nd_request_timeout fix (#1276) fix * Remove btcli query and btcli set_weights (#1144) . * Merge releases 4.0.0 and 4.0.1 back to staging (#1306) * bump version * Fix permissions for release github script (#1224) Co-authored-by: Cameron Fairchild * should be 4.1.0 * Revert "should be 4.1.0" This reverts commit 3db08ea24f4fc4775bd46858e6c77cfa165d85ed. * Staging into Release branch (#1275) * (un)Staking multiple avoid tx limit (#1244) * add tx rate limit * wait for tx limit if not done multi stake/unstake * dont "decrypt" hotkey * additional logging for prometheus (#1246) * Dataset fix (#1249) * fix * added try except * Grab delegates details from GitHub (#1245) * add url to init * add dataclass and util functions * use in cli * remove delegates json --------- Co-authored-by: joeylegere * Add raw spec for local test and new bins (#1243) * add spec and new bins * fix config netuid * use dot get * check if config netuid is list * add start to mockstatus * add attr to mock neuron * add info to mock from neurons * change ordering of neuron dict to namespace * remove test for wandb for axon * use regex for looser match * fix blacklist metagraph mock * use real mock netuid * use mock network and netuid in constructor * fix patch * patch delegate check * use mock network and netuid * remove check for wallet hotkey * fix tests for subtensor init * dont set netuid for overview test * typo in docstring * add mock status stop * add low mock tx limit * oops typo * use dot get * add wait for final and incl args * use args during setup * update bins and use 100ms blocktime * pass block arg * remove bittensor.logging and a old test * use random port * backward fix * fix block time to 1s * compile no symb on linux * compile no symb mac * remove useless init on var * use dot get for new flags * update test durations * update test durations * use dot get for config * output error msg * mock to_default * remove to defaults in help * reduce neruons, remove flaky test * deactivate test * mvoe key pair tests out of the subtensor interface --------- Co-authored-by: Eugene * Fix list_delegates on non-archive nodes (#1232) * Change how pull of archival data is handled * fix for list_delegates too * . * use empty dict * fix spacing * specify exception * log out * add space in log message * use warning instead * Blacklist fixes + depreciation of old signatures (#1240) * fixes blacklist error message + remove * remove checks for parse signature * remove sign v1 tests * fix for the syanpse checks * fix tests and remove duplicate sign * [BIT-636] Change u16 weight normalization to max-upscaling (#1241) * Change u16 weight normalization to max-upscaling Use full u16 bitwidth so that max_weight=U16_MAX, then rely on subtensor epoch to properly normalize weights in I32F32. This means that weights submission extrinsic to subtensor does not have to be pre-normalized. * Skip zero sum in weight conversion * Round u16 weights * remove duplicate command #1228 (#1231) * remove duplicate command #1228 * Extract create_parser for cli testing * mark as private * use in tests and test for duplicates * fix test using mock prompt answers * test_forward_priority_2nd_request_timeout fix (#1276) fix * Remove btcli query and btcli set_weights (#1144) . --------- Co-authored-by: Eugene-hu <85906264+Eugene-hu@users.noreply.github.com> Co-authored-by: isabella618033 <49876827+isabella618033@users.noreply.github.com> Co-authored-by: joeylegere Co-authored-by: Eugene Co-authored-by: opentaco <93473497+opentaco@users.noreply.github.com> * Remove codecov (#1282) * Use alt new preseal (#1269) * use new preseal for reg * bump cubit req * fix arg order issue * cubit req back * use alt impl * fix typehint * use 512 * modify tests for new format * refactor functions to use helpers and remove useless * refactor functions * add test for CPU solver * modify tests for privitized module and methods * private register cuda * move formatting funcs * use powsolution * privitize most methods * fix test * fix perms * remove test script * remove debug * fix call * fix seal * fix combined hash * move to method * fix test using real example * update mock bins * use new builder * fix block update tests * fix some patching in tests * mock live display for some tests * fix chain mock * update linux bin * add mock network flag * set max diff at 0 for mock netuid 1 * set min diff too * add try catch for setup * add some logging during tests * don't submit on cli register * update test durations * fix test to use mock keypair * return mock wallet * should use subtensor instance during rereg * update node subtensor bins * use fixtures and multiple subtensor instances * changelog update * skip CLI tests (#1284) * skip tests * dont test mock functions * update test durations * [Release] v4.0.0 (#1271) * bump version * Fix permissions for release github script (#1224) Co-authored-by: Cameron Fairchild * should be 4.1.0 * Revert "should be 4.1.0" This reverts commit 3db08ea24f4fc4775bd46858e6c77cfa165d85ed. * Staging into Release branch (#1275) * (un)Staking multiple avoid tx limit (#1244) * add tx rate limit * wait for tx limit if not done multi stake/unstake * dont "decrypt" hotkey * additional logging for prometheus (#1246) * Dataset fix (#1249) * fix * added try except * Grab delegates details from GitHub (#1245) * add url to init * add dataclass and util functions * use in cli * remove delegates json --------- Co-authored-by: joeylegere * Add raw spec for local test and new bins (#1243) * add spec and new bins * fix config netuid * use dot get * check if config netuid is list * add start to mockstatus * add attr to mock neuron * add info to mock from neurons * change ordering of neuron dict to namespace * remove test for wandb for axon * use regex for looser match * fix blacklist metagraph mock * use real mock netuid * use mock network and netuid in constructor * fix patch * patch delegate check * use mock network and netuid * remove check for wallet hotkey * fix tests for subtensor init * dont set netuid for overview test * typo in docstring * add mock status stop * add low mock tx limit * oops typo * use dot get * add wait for final and incl args * use args during setup * update bins and use 100ms blocktime * pass block arg * remove bittensor.logging and a old test * use random port * backward fix * fix block time to 1s * compile no symb on linux * compile no symb mac * remove useless init on var * use dot get for new flags * update test durations * update test durations * use dot get for config * output error msg * mock to_default * remove to defaults in help * reduce neruons, remove flaky test * deactivate test * mvoe key pair tests out of the subtensor interface --------- Co-authored-by: Eugene * Fix list_delegates on non-archive nodes (#1232) * Change how pull of archival data is handled * fix for list_delegates too * . * use empty dict * fix spacing * specify exception * log out * add space in log message * use warning instead * Blacklist fixes + depreciation of old signatures (#1240) * fixes blacklist error message + remove * remove checks for parse signature * remove sign v1 tests * fix for the syanpse checks * fix tests and remove duplicate sign * [BIT-636] Change u16 weight normalization to max-upscaling (#1241) * Change u16 weight normalization to max-upscaling Use full u16 bitwidth so that max_weight=U16_MAX, then rely on subtensor epoch to properly normalize weights in I32F32. This means that weights submission extrinsic to subtensor does not have to be pre-normalized. * Skip zero sum in weight conversion * Round u16 weights * remove duplicate command #1228 (#1231) * remove duplicate command #1228 * Extract create_parser for cli testing * mark as private * use in tests and test for duplicates * fix test using mock prompt answers * test_forward_priority_2nd_request_timeout fix (#1276) fix * Remove btcli query and btcli set_weights (#1144) . --------- Co-authored-by: Eugene-hu <85906264+Eugene-hu@users.noreply.github.com> Co-authored-by: isabella618033 <49876827+isabella618033@users.noreply.github.com> Co-authored-by: joeylegere Co-authored-by: Eugene Co-authored-by: opentaco <93473497+opentaco@users.noreply.github.com> * Remove codecov (#1282) * Use alt new preseal (#1269) * use new preseal for reg * bump cubit req * fix arg order issue * cubit req back * use alt impl * fix typehint * use 512 * modify tests for new format * refactor functions to use helpers and remove useless * refactor functions * add test for CPU solver * modify tests for privitized module and methods * private register cuda * move formatting funcs * use powsolution * privitize most methods * fix test * fix perms * remove test script * remove debug * fix call * fix seal * fix combined hash * move to method * fix test using real example * update mock bins * use new builder * fix block update tests * fix some patching in tests * mock live display for some tests * fix chain mock * update linux bin * add mock network flag * set max diff at 0 for mock netuid 1 * set min diff too * add try catch for setup * add some logging during tests * don't submit on cli register * update test durations * fix test to use mock keypair * return mock wallet * should use subtensor instance during rereg * update node subtensor bins * use fixtures and multiple subtensor instances * changelog update * skip CLI tests (#1284) * skip tests * dont test mock functions * update test durations --------- Co-authored-by: Eduardo García Co-authored-by: Eugene-hu <85906264+Eugene-hu@users.noreply.github.com> Co-authored-by: isabella618033 <49876827+isabella618033@users.noreply.github.com> Co-authored-by: joeylegere Co-authored-by: Eugene Co-authored-by: opentaco <93473497+opentaco@users.noreply.github.com> * fix my delegates * fix perms on changelog script * update version * fix changelog script * Catch bad endpoint protocol (#1296) * catch protocol not good * add protocol 4 * catch assertion and return bool * catch assertion errors * changelog --------- Co-authored-by: Eduardo García Co-authored-by: Eugene-hu <85906264+Eugene-hu@users.noreply.github.com> Co-authored-by: isabella618033 <49876827+isabella618033@users.noreply.github.com> Co-authored-by: joeylegere Co-authored-by: Eugene Co-authored-by: opentaco <93473497+opentaco@users.noreply.github.com> * Update DEVELOPMENT_WORKFLOW.md * final fixes * staging updates and fixes (#1540) * fix cli test * fix double-counted hotkeys per subnet and non-iterable stake obj (#1539) * fix double-counted hotkeys per subnet and non-iterable stake obj * run black * Add root get_weights command to btcli (#1536) * Add root get_weights command to btcli * Use a percentage for viewing weights instead of float values * run black, fix cli test --------- Co-authored-by: ifrit98 * Fix typo (#1543) Co-authored-by: philanthrope * remove duplicated debug message in dendrite (#1544) * Cli fix (#1541) don't break on mismatched coldkey from local wallet <> chain * update faucet helpstr (#1542) * Added mechanism to sum all delegated tao (#1547) Co-authored-by: Ala Shaabana * Dict hash fix (#1548) * use dict() when hasing body objects to not convert arbitrary objects to str * recreate synapse in axon dependency to avoid duplicating code * black * Merge master (#1552) Release/6.1.0 (#1550) * Fix typo (#1543) * remove duplicated debug message in dendrite (#1544) * Cli fix (#1541) don't break on mismatched coldkey from local wallet <> chain * update faucet helpstr (#1542) * Added mechanism to sum all delegated tao (#1547) * Dict hash fix (#1548) * use dict() when hasing body objects to not convert arbitrary objects to str * recreate synapse in axon dependency to avoid duplicating code * black * update version * update changelog --------- Co-authored-by: Cameron Fairchild Co-authored-by: Eugene Co-authored-by: isabella618033 <49876827+isabella618033@users.noreply.github.com> Co-authored-by: Cameron Fairchild Co-authored-by: opentaco <93473497+opentaco@users.noreply.github.com> Co-authored-by: Eduardo García Co-authored-by: Ayden Brewer Co-authored-by: Steffen Cruz Co-authored-by: Ala Shaabana * Streaming fix (#1551) * yield chunks immediately in process_streaming_responss so clients can access * break streaming into separate call funcs * update docstrings, types * black * duplicate debug msg * add warning for mismatched streaming arg + subclass * Fix typos (#1553) * Release/6.1.0 (#1550) Co-authored-by: ifrit98 * Fix typo (#1543) Co-authored-by: philanthrope * remove duplicated debug message in dendrite (#1544) * Cli fix (#1541) don't break on mismatched coldkey from local wallet <> chain * update faucet helpstr (#1542) * Added mechanism to sum all delegated tao (#1547) Co-authored-by: Ala Shaabana * Dict hash fix (#1548) * use dict() when hasing body objects to not convert arbitrary objects to str * recreate synapse in axon dependency to avoid duplicating code * black * update version * update changelog --------- Co-authored-by: Cameron Fairchild Co-authored-by: Eugene Co-authored-by: isabella618033 <49876827+isabella618033@users.noreply.github.com> Co-authored-by: Cameron Fairchild Co-authored-by: opentaco <93473497+opentaco@users.noreply.github.com> Co-authored-by: Eduardo García Co-authored-by: Ayden Brewer Co-authored-by: Steffen Cruz Co-authored-by: Ala Shaabana * fix typos --------- Co-authored-by: philanthrope Co-authored-by: Cameron Fairchild Co-authored-by: Eugene Co-authored-by: isabella618033 <49876827+isabella618033@users.noreply.github.com> Co-authored-by: Cameron Fairchild Co-authored-by: opentaco <93473497+opentaco@users.noreply.github.com> Co-authored-by: Eduardo García Co-authored-by: Ayden Brewer Co-authored-by: Steffen Cruz Co-authored-by: Ala Shaabana * Normalize weights in r get weights table (#1556) * Normalize weights in r get weights table * use numpy and make table look nicer * apply black * add max 1 to prevent div0 --------- Co-authored-by: Cameron Fairchild * Dendrite & Synapse updates and fixes (#1555) * remove tensor header objects to not overflow headers * update tests to reflect removal of tensor headers * ensure consistent method for calling in synapse methods * fix dendrite UnClosesedSession error * fix docstring and add tests for close/aclose * rm extra delete * run black * add default synapse dict() consistency test * call del on session after close_session(), fix tests * update dendrite dummy clsname * add dendrite.query finally block * fix test * rm root flag in metagraph (#1558) * rm root flag in metagraph * run black * typo * Max Faucet Runs == 3 (#1560) add exceptions * replace unknown wallet params (chain mismatch) with key values (#1559) * replace unknown wallet params (chain mismatch) with key values * run black * rm debug prints * Remove PoW registration cli and associated extrinsic (#1557) * Remove PoW registration cli and associated extrinsic * run black * no mo pow, no mo pow tests * remove now deprecated PoW reregister routine * remove deprecated tests * more test fixes * remove _do_pow call * return PoW but still kill reregister (unused) * run black * return test to networks choices in btcli, fix chain_endpoint selection * fix pow args * Add btcli wallet balance (#1564) * begin adding balance command * skip validator that hasn't set weights yet on root * finish balances command * formatter * issue warning for nonexistent coldkeypub.txt rather than break * Dendrite fixes (#1561) * make sure a session exists before trying to close it * don't double iterate over async generator, simply return it * black * less DRY violations * fix typehints * add versioning * update changelog * remove unused registration utils * fix typos --------- Co-authored-by: Cameron Fairchild Co-authored-by: Eugene Co-authored-by: Eugene-hu <85906264+Eugene-hu@users.noreply.github.com> Co-authored-by: isabella618033 <49876827+isabella618033@users.noreply.github.com> Co-authored-by: joeylegere Co-authored-by: Cameron Fairchild Co-authored-by: “quac88” <“mac2@thrasher.com”> Co-authored-by: opentaco <93473497+opentaco@users.noreply.github.com> Co-authored-by: Eduardo García Co-authored-by: Ayden Brewer Co-authored-by: Steffen Cruz Co-authored-by: Ala Shaabana Co-authored-by: Ala Shaabana Co-authored-by: omahs <73983677+omahs@users.noreply.github.com> Co-authored-by: Cameron Fairchild Co-authored-by: Cameron Fairchild --- CHANGELOG.md | 43 + README.md | 28 +- VERSION | 2 +- bittensor/__init__.py | 4 +- bittensor/cli.py | 4 +- bittensor/commands/__init__.py | 5 +- bittensor/commands/overview.py | 7 +- bittensor/commands/register.py | 287 ++--- bittensor/commands/root.py | 67 +- bittensor/commands/utils.py | 14 +- bittensor/commands/wallets.py | 127 ++ bittensor/dendrite.py | 438 +++++-- bittensor/extrinsics/registration.py | 22 + bittensor/metagraph.py | 9 +- bittensor/subtensor.py | 3 +- bittensor/synapse.py | 69 +- bittensor/utils/__init__.py | 2 +- bittensor/utils/registration.py | 48 - bittensor/utils/registratrion_old.py | 1030 ----------------- tests/integration_tests/test_cli.py | 50 +- .../integration_tests/test_cli_no_network.py | 10 +- .../test_subtensor_integration.py | 9 +- tests/unit_tests/test_dendrite.py | 40 + tests/unit_tests/test_synapse.py | 111 +- tests/unit_tests/utils/test_utils.py | 217 ---- 25 files changed, 895 insertions(+), 1751 deletions(-) delete mode 100644 bittensor/utils/registratrion_old.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 83254fbd3d..0e606fd03c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,48 @@ # Changelog +## 6.2.0 / 2023-10-30 + +## What's Changed +* (un)Staking multiple avoid tx limit by @camfairchild in https://github.com/opentensor/bittensor/pull/1244 +* additional logging for prometheus by @Eugene-hu in https://github.com/opentensor/bittensor/pull/1246 +* Dataset fix by @isabella618033 in https://github.com/opentensor/bittensor/pull/1249 +* Grab delegates details from GitHub by @camfairchild in https://github.com/opentensor/bittensor/pull/1245 +* Add raw spec for local test and new bins by @camfairchild in https://github.com/opentensor/bittensor/pull/1243 +* Fix list_delegates on non-archive nodes by @camfairchild in https://github.com/opentensor/bittensor/pull/1232 +* Blacklist fixes + depreciation of old signatures by @Eugene-hu in https://github.com/opentensor/bittensor/pull/1240 +* [BIT-636] Change u16 weight normalization to max-upscaling by @opentaco in https://github.com/opentensor/bittensor/pull/1241 +* remove duplicate command #1228 by @camfairchild in https://github.com/opentensor/bittensor/pull/1231 +* test_forward_priority_2nd_request_timeout fix by @isabella618033 in https://github.com/opentensor/bittensor/pull/1276 +* Remove btcli query and btcli set_weights by @camfairchild in https://github.com/opentensor/bittensor/pull/1144 +* Merge releases 4.0.0 and 4.0.1 back to staging by @camfairchild in https://github.com/opentensor/bittensor/pull/1306 +* Improve development workflow documentation by @quac88 in https://github.com/opentensor/bittensor/pull/1262 +* staging updates and fixes by @ifrit98 in https://github.com/opentensor/bittensor/pull/1540 +* Add root get_weights command to btcli by @Rubberbandits in https://github.com/opentensor/bittensor/pull/1536 +* Fix typo by @steffencruz in https://github.com/opentensor/bittensor/pull/1543 +* remove duplicated debug message in dendrite by @ifrit98 in https://github.com/opentensor/bittensor/pull/1544 +* Cli fix by @ifrit98 in https://github.com/opentensor/bittensor/pull/1541 +* update faucet helpstr by @ifrit98 in https://github.com/opentensor/bittensor/pull/1542 +* Added mechanism to sum all delegated tao by @shibshib in https://github.com/opentensor/bittensor/pull/1547 +* Dict hash fix by @ifrit98 in https://github.com/opentensor/bittensor/pull/1548 +* Release/6.1.0 by @ifrit98 in https://github.com/opentensor/bittensor/pull/1550 +* Merge master by @ifrit98 in https://github.com/opentensor/bittensor/pull/1552 +* Streaming fix by @ifrit98 in https://github.com/opentensor/bittensor/pull/1551 +* Fix typos by @omahs in https://github.com/opentensor/bittensor/pull/1553 +* Normalize weights in r get weights table by @camfairchild in https://github.com/opentensor/bittensor/pull/1556 +* Dendrite & Synapse updates and fixes by @ifrit98 in https://github.com/opentensor/bittensor/pull/1555 +* rm root flag in metagraph by @ifrit98 in https://github.com/opentensor/bittensor/pull/1558 +* Max Faucet Runs == 3 by @ifrit98 in https://github.com/opentensor/bittensor/pull/1560 +* replace unknown wallet params (chain mismatch) with key values by @ifrit98 in https://github.com/opentensor/bittensor/pull/1559 +* Remove PoW registration cli and associated extrinsic by @ifrit98 in https://github.com/opentensor/bittensor/pull/1557 +* Add btcli wallet balance by @ifrit98 in https://github.com/opentensor/bittensor/pull/1564 +* Dendrite fixes by @ifrit98 in https://github.com/opentensor/bittensor/pull/1561 + +## New Contributors +* @omahs made their first contribution in https://github.com/opentensor/bittensor/pull/1553 + +**Full Changelog**: https://github.com/opentensor/bittensor/compare/v6.0.1...v6.2.0 + + ## 6.1.0 / 2023-10-17 ## What's Changed diff --git a/README.md b/README.md index c1552a29c2..5825b95a7d 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Bittensor is a mining network, similar to Bitcoin, that includes built-in incentives designed to encourage computers to provide access to machine learning models in an efficient and censorship-resistant manner. These models can be queried by users seeking outputs from the network, for instance; generating text, audio, and images, or for extracting numerical representations of these input types. Under the hood, Bittensor’s *economic market*, is facilitated by a blockchain token mechanism, through which producers (***miners***) and the verification of the work done by those miners (***validators***) are rewarded. Miners host, train or otherwise procure machine learning systems into the network as a means of fulfilling the verification problems defined by the validators, like the ability to generate responses from prompts i.e. “What is the capital of Texas?. -The token based mechanism under which the miners are incentivized ensures that they are constantly driven to make their knowledge output more useful, in terms of speed, intelligence and diversity. The value generated by the network is distributed directly to the individuals producing that value, without intermediarias. Anyone can participate in this endeavour, extract value from the network, and govern Bittensor. The network is open to all participants, and no individual or group has full control over what is learned, who can profit from it, or who can access it. +The token based mechanism under which the miners are incentivized ensures that they are constantly driven to make their knowledge output more useful, in terms of speed, intelligence and diversity. The value generated by the network is distributed directly to the individuals producing that value, without intermediaries. Anyone can participate in this endeavour, extract value from the network, and govern Bittensor. The network is open to all participants, and no individual or group has full control over what is learned, who can profit from it, or who can access it. To learn more about Bittensor, please read our [paper](https://bittensor.com/whitepaper). @@ -70,7 +70,7 @@ print (wallet) $ btcli wallet new_coldkey Enter wallet name (default): - IMPORTANT: Store this mnemonic in a secure (preferably offline place), as anyone who has possesion of this mnemonic can use it to regenerate the key and access your tokens. + IMPORTANT: Store this mnemonic in a secure (preferably offline place), as anyone who has possession of this mnemonic can use it to regenerate the key and access your tokens. The mnemonic to the new coldkey is: **** *** **** **** ***** **** *** **** **** **** ***** ***** You can use the mnemonic to recreate the key in case it gets lost. The command to use to regenerate the key using this mnemonic is: @@ -80,7 +80,7 @@ $ btcli wallet new_hotkey Enter wallet name (default): d1 Enter hotkey name (default): - IMPORTANT: Store this mnemonic in a secure (preferably offline place), as anyone who has possesion of this mnemonic can use it to regenerate the key and access your tokens. + IMPORTANT: Store this mnemonic in a secure (preferably offline place), as anyone who has possession of this mnemonic can use it to regenerate the key and access your tokens. The mnemonic to the new hotkey is: **** *** **** **** ***** **** *** **** **** **** ***** ***** You can use the mnemonic to recreate the key in case it gets lost. The command to use to regenerate the key using this mnemonic is: @@ -95,7 +95,7 @@ $ tree ~/.bittensor/ coldkey # You encrypted coldkey. coldkeypub.txt # Your coldkey public address hotkeys/ # The folder containing all of your hotkeys. - default # You unencrypeted hotkey information. + default # You unencrypted hotkey information. ``` Your default wallet ```Wallet (default, default, ~/.bittensor/wallets/)``` is always used unless you specify otherwise. Be sure to store your mnemonics safely. If you lose your password to your wallet, or the access to the machine where the wallet is stored, you can always regenerate the coldkey using the mnemonic you saved from above. ```bash @@ -183,18 +183,18 @@ For example: ```bash btcli subnets --help -usage: btcli subnets [-h] {list,metagraph,lock_cost,create,register,recycle_register,hyperparameters} ... +usage: btcli subnets [-h] {list,metagraph,lock_cost,create,register,pow_register,hyperparameters} ... positional arguments: - {list,metagraph,lock_cost,create,register,recycle_register,hyperparameters} + {list,metagraph,lock_cost,create,register,pow_register,hyperparameters} Commands for managing and viewing subnetworks. - list List all subnets on the network + list List all subnets on the network. metagraph View a subnet metagraph information. - lock_cost Return the lock cost to register a subnet + lock_cost Return the lock cost to register a subnet. create Create a new bittensor subnetwork on this chain. register Register a wallet to a network. - recycle_register Register a wallet to a network. - hyperparameters View subnet hyperparameters + register Register a wallet to a network using PoW. + hyperparameters View subnet hyperparameters. options: -h, --help show this help message and exit @@ -251,7 +251,7 @@ metagraph.save() metagraph.load() ``` -Synapse: Responsible for defining the protocol definition betwee axon servers and dendrite clients +Synapse: Responsible for defining the protocol definition between axon servers and dendrite clients ```python class Topk( bittensor.Synapse ): topk: int = 2 # Number of "top" elements to select @@ -289,12 +289,12 @@ def verify_my_synapse( synapse: MySyanpse ): # Apply custom verification logic to synapse # Optionally raise Exception -# Define a custom request blacklist fucntion +# Define a custom request blacklist function def blacklist_my_synapse( synapse: MySyanpse ) -> bool: # Apply custom blacklist # return False ( if non blacklisted ) or True ( if blacklisted ) -# Define a custom request priority fucntion +# Define a custom request priority function def prioritize_my_synape( synapse: MySyanpse ) -> float: # Apply custom priority return 1.0 @@ -312,7 +312,7 @@ my_axon.attach( ``` Dendrite: Inheriting from PyTorch's Module class, represents the abstracted implementation of a network client module designed -to send requests to those endpoint to recieve inputs. +to send requests to those endpoints to receive inputs. Example: ```python dendrite_obj = dendrite( wallet = bittensor.wallet() ) diff --git a/VERSION b/VERSION index 358e78e607..4ac4fded49 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.1.0 \ No newline at end of file +6.2.0 \ No newline at end of file diff --git a/bittensor/__init__.py b/bittensor/__init__.py index 600e5e8797..3215ce9155 100644 --- a/bittensor/__init__.py +++ b/bittensor/__init__.py @@ -27,7 +27,7 @@ nest_asyncio.apply() # Bittensor code and protocol version. -__version__ = "6.1.0" +__version__ = "6.2.0" version_split = __version__.split(".") __version_as_int__ = ( (100 * int(version_split[0])) @@ -87,7 +87,7 @@ def debug(on: bool = True): # Wallet ss58 address length __ss58_address_length__ = 48 -__networks__ = ["local", "finney"] +__networks__ = ["local", "finney", "test"] __finney_entrypoint__ = "wss://entrypoint-finney.opentensor.ai:443" diff --git a/bittensor/cli.py b/bittensor/cli.py index 673fa31419..a7ee579624 100644 --- a/bittensor/cli.py +++ b/bittensor/cli.py @@ -53,8 +53,8 @@ "metagraph": MetagraphCommand, "lock_cost": SubnetLockCostCommand, "create": RegisterSubnetworkCommand, + "pow_register": PowRegisterCommand, "register": RegisterCommand, - "recycle_register": RecycleRegisterCommand, "hyperparameters": SubnetHyperparamsCommand, }, }, @@ -85,7 +85,7 @@ "overview": OverviewCommand, "transfer": TransferCommand, "inspect": InspectCommand, - # "balance": None, + "balance": WalletBalanceCommand, "create": WalletCreateCommand, "new_hotkey": NewHotkeyCommand, "new_coldkey": NewColdkeyCommand, diff --git a/bittensor/commands/__init__.py b/bittensor/commands/__init__.py index b9e7721547..03822f9bf5 100644 --- a/bittensor/commands/__init__.py +++ b/bittensor/commands/__init__.py @@ -21,7 +21,7 @@ { "netuid": 1, "subtensor": {"network": "finney", "chain_endpoint": None, "_mock": False}, - "register": { + "pow_register": { "num_processes": None, "update_interval": 50000, "output_in_place": True, @@ -65,7 +65,7 @@ from .stake import StakeCommand, StakeShow from .unstake import UnStakeCommand from .overview import OverviewCommand -from .register import RegisterCommand, RecycleRegisterCommand, RunFaucetCommand +from .register import PowRegisterCommand, RegisterCommand, RunFaucetCommand from .delegates import ( NominateCommand, ListDelegatesCommand, @@ -81,6 +81,7 @@ RegenHotkeyCommand, UpdateWalletCommand, WalletCreateCommand, + WalletBalanceCommand, ) from .transfer import TransferCommand from .inspect import InspectCommand diff --git a/bittensor/commands/overview.py b/bittensor/commands/overview.py index 43e23552d2..733ec81d7c 100644 --- a/bittensor/commands/overview.py +++ b/bittensor/commands/overview.py @@ -121,7 +121,6 @@ def run(cli: "bittensor.cli"): ] = hotkey_wallet all_hotkey_addresses = list(hotkey_coldkey_to_hotkey_wallet.keys()) - with console.status( ":satellite: Syncing with chain: [white]{}[/white] ...".format( cli.config.subtensor.get( @@ -285,7 +284,11 @@ def run(cli: "bittensor.cli"): nn.coldkey, None ) if not hotwallet: - continue + # Indicates a mismatch between what the chain says the coldkey + # is for this hotkey and the local wallet coldkey-hotkey pair + hotwallet = argparse.Namespace() + hotwallet.name = nn.coldkey[:7] + hotwallet.hotkey_str = nn.hotkey[:7] nn: bittensor.NeuronInfoLite uid = nn.uid active = nn.active diff --git a/bittensor/commands/register.py b/bittensor/commands/register.py index 2fc615fd26..32c7f97fc4 100644 --- a/bittensor/commands/register.py +++ b/bittensor/commands/register.py @@ -27,6 +27,88 @@ class RegisterCommand: + @staticmethod + def run(cli): + r"""Register neuron by recycling some TAO.""" + wallet = bittensor.wallet(config=cli.config) + subtensor = bittensor.subtensor(config=cli.config) + + # Verify subnet exists + if not subtensor.subnet_exists(netuid=cli.config.netuid): + bittensor.__console__.print( + f"[red]Subnet {cli.config.netuid} does not exist[/red]" + ) + sys.exit(1) + + # Check current recycle amount + current_recycle = subtensor.burn(netuid=cli.config.netuid) + balance = subtensor.get_balance(address=wallet.coldkeypub.ss58_address) + + # Check balance is sufficient + if balance < current_recycle: + bittensor.__console__.print( + f"[red]Insufficient balance {balance} to register neuron. Current recycle is {current_recycle} TAO[/red]" + ) + sys.exit(1) + + if not cli.config.no_prompt: + if ( + Confirm.ask( + f"Your balance is: [bold green]{balance}[/bold green]\nThe cost to register by recycle is [bold red]{current_recycle}[/bold red]\nDo you want to continue?", + default=False, + ) + == False + ): + sys.exit(1) + + subtensor.burned_register( + wallet=wallet, netuid=cli.config.netuid, prompt=not cli.config.no_prompt + ) + + @staticmethod + def add_args(parser: argparse.ArgumentParser): + register_parser = parser.add_parser( + "register", help="""Register a wallet to a network.""" + ) + register_parser.add_argument( + "--netuid", + type=int, + help="netuid for subnet to serve this neuron on", + default=argparse.SUPPRESS, + ) + + bittensor.wallet.add_args(register_parser) + bittensor.subtensor.add_args(register_parser) + + @staticmethod + def check_config(config: "bittensor.config"): + if ( + not config.is_set("subtensor.network") + and not config.is_set("subtensor.chain_endpoint") + and not config.no_prompt + ): + config.subtensor.network = Prompt.ask( + "Enter subtensor network", + choices=bittensor.__networks__, + default=defaults.subtensor.network, + ) + _, endpoint = bittensor.subtensor.determine_chain_endpoint_and_network( + config.subtensor.network + ) + config.subtensor.chain_endpoint = endpoint + + check_netuid_set(config, subtensor=bittensor.subtensor(config=config)) + + if not config.is_set("wallet.name") and not config.no_prompt: + wallet_name = Prompt.ask("Enter wallet name", default=defaults.wallet.name) + config.wallet.name = str(wallet_name) + + if not config.is_set("wallet.hotkey") and not config.no_prompt: + hotkey = Prompt.ask("Enter hotkey name", default=defaults.wallet.hotkey) + config.wallet.hotkey = str(hotkey) + + +class PowRegisterCommand: @staticmethod def run(cli): r"""Register neuron.""" @@ -44,23 +126,25 @@ def run(cli): wallet=wallet, netuid=cli.config.netuid, prompt=not cli.config.no_prompt, - TPB=cli.config.register.cuda.get("TPB", None), - update_interval=cli.config.register.get("update_interval", None), - num_processes=cli.config.register.get("num_processes", None), - cuda=cli.config.register.cuda.get( - "use_cuda", defaults.register.cuda.use_cuda + TPB=cli.config.pow_register.cuda.get("TPB", None), + update_interval=cli.config.pow_register.get("update_interval", None), + num_processes=cli.config.pow_register.get("num_processes", None), + cuda=cli.config.pow_register.cuda.get( + "use_cuda", defaults.pow_register.cuda.use_cuda ), - dev_id=cli.config.register.cuda.get("dev_id", None), - output_in_place=cli.config.register.get( - "output_in_place", defaults.register.output_in_place + dev_id=cli.config.pow_register.cuda.get("dev_id", None), + output_in_place=cli.config.pow_register.get( + "output_in_place", defaults.pow_register.output_in_place + ), + log_verbose=cli.config.pow_register.get( + "verbose", defaults.pow_register.verbose ), - log_verbose=cli.config.register.get("verbose", defaults.register.verbose), ) @staticmethod def add_args(parser: argparse.ArgumentParser): register_parser = parser.add_parser( - "register", help="""Register a wallet to a network.""" + "pow_register", help="""Register a wallet to a network using PoW.""" ) register_parser.add_argument( "--netuid", @@ -69,75 +153,75 @@ def add_args(parser: argparse.ArgumentParser): default=argparse.SUPPRESS, ) register_parser.add_argument( - "--register.num_processes", + "--pow_register.num_processes", "-n", - dest="register.num_processes", + dest="pow_register.num_processes", help="Number of processors to use for POW registration", type=int, - default=defaults.register.num_processes, + default=defaults.pow_register.num_processes, ) register_parser.add_argument( - "--register.update_interval", - "--register.cuda.update_interval", + "--pow_register.update_interval", + "--pow_register.cuda.update_interval", "--cuda.update_interval", "-u", help="The number of nonces to process before checking for next block during registration", type=int, - default=defaults.register.update_interval, + default=defaults.pow_register.update_interval, ) register_parser.add_argument( - "--register.no_output_in_place", + "--pow_register.no_output_in_place", "--no_output_in_place", - dest="register.output_in_place", + dest="pow_register.output_in_place", help="Whether to not ouput the registration statistics in-place. Set flag to disable output in-place.", action="store_false", required=False, - default=defaults.register.output_in_place, + default=defaults.pow_register.output_in_place, ) register_parser.add_argument( - "--register.verbose", + "--pow_register.verbose", help="Whether to ouput the registration statistics verbosely.", action="store_true", required=False, - default=defaults.register.verbose, + default=defaults.pow_register.verbose, ) ## Registration args for CUDA registration. register_parser.add_argument( - "--register.cuda.use_cuda", + "--pow_register.cuda.use_cuda", "--cuda", "--cuda.use_cuda", - dest="register.cuda.use_cuda", - default=defaults.register.cuda.use_cuda, + dest="pow_register.cuda.use_cuda", + default=defaults.pow_register.cuda.use_cuda, help="""Set flag to use CUDA to register.""", action="store_true", required=False, ) register_parser.add_argument( - "--register.cuda.no_cuda", + "--pow_register.cuda.no_cuda", "--no_cuda", "--cuda.no_cuda", - dest="register.cuda.use_cuda", - default=not defaults.register.cuda.use_cuda, + dest="pow_register.cuda.use_cuda", + default=not defaults.pow_register.cuda.use_cuda, help="""Set flag to not use CUDA for registration""", action="store_false", required=False, ) register_parser.add_argument( - "--register.cuda.dev_id", + "--pow_register.cuda.dev_id", "--cuda.dev_id", type=int, nargs="+", - default=defaults.register.cuda.dev_id, + default=defaults.pow_register.cuda.dev_id, help="""Set the CUDA device id(s). Goes by the order of speed. (i.e. 0 is the fastest).""", required=False, ) register_parser.add_argument( - "--register.cuda.TPB", + "--pow_register.cuda.TPB", "--cuda.TPB", type=int, - default=defaults.register.cuda.TPB, + default=defaults.pow_register.cuda.TPB, help="""Set the number of Threads Per Block for CUDA.""", required=False, ) @@ -145,76 +229,6 @@ def add_args(parser: argparse.ArgumentParser): bittensor.wallet.add_args(register_parser) bittensor.subtensor.add_args(register_parser) - @staticmethod - def check_config(config: "bittensor.config"): - check_netuid_set(config, subtensor=bittensor.subtensor(config=config)) - - if not config.is_set("wallet.name") and not config.no_prompt: - wallet_name = Prompt.ask("Enter wallet name", default=defaults.wallet.name) - config.wallet.name = str(wallet_name) - - if not config.is_set("wallet.hotkey") and not config.no_prompt: - hotkey = Prompt.ask("Enter hotkey name", default=defaults.wallet.hotkey) - config.wallet.hotkey = str(hotkey) - - if not config.no_prompt: - check_for_cuda_reg_config(config) - - -class RecycleRegisterCommand: - @staticmethod - def run(cli): - r"""Register neuron by recycling some TAO.""" - wallet = bittensor.wallet(config=cli.config) - subtensor = bittensor.subtensor(config=cli.config) - - # Verify subnet exists - if not subtensor.subnet_exists(netuid=cli.config.netuid): - bittensor.__console__.print( - f"[red]Subnet {cli.config.netuid} does not exist[/red]" - ) - sys.exit(1) - - # Check current recycle amount - current_recycle = subtensor.burn(netuid=cli.config.netuid) - balance = subtensor.get_balance(address=wallet.coldkeypub.ss58_address) - - # Check balance is sufficient - if balance < current_recycle: - bittensor.__console__.print( - f"[red]Insufficient balance {balance} to register neuron. Current recycle is {current_recycle} TAO[/red]" - ) - sys.exit(1) - - if not cli.config.no_prompt: - if ( - Confirm.ask( - f"Your balance is: [bold green]{balance}[/bold green]\nThe cost to register by recycle is [bold red]{current_recycle}[/bold red]\nDo you want to continue?", - default=False, - ) - == False - ): - sys.exit(1) - - subtensor.burned_register( - wallet=wallet, netuid=cli.config.netuid, prompt=not cli.config.no_prompt - ) - - @staticmethod - def add_args(parser: argparse.ArgumentParser): - recycle_register_parser = parser.add_parser( - "recycle_register", help="""Register a wallet to a network.""" - ) - recycle_register_parser.add_argument( - "--netuid", - type=int, - help="netuid for subnet to serve this neuron on", - default=argparse.SUPPRESS, - ) - - bittensor.wallet.add_args(recycle_register_parser) - bittensor.subtensor.add_args(recycle_register_parser) - @staticmethod def check_config(config: "bittensor.config"): if ( @@ -227,6 +241,10 @@ def check_config(config: "bittensor.config"): choices=bittensor.__networks__, default=defaults.subtensor.network, ) + _, endpoint = bittensor.subtensor.determine_chain_endpoint_and_network( + config.subtensor.network + ) + config.subtensor.chain_endpoint = endpoint check_netuid_set(config, subtensor=bittensor.subtensor(config=config)) @@ -238,6 +256,9 @@ def check_config(config: "bittensor.config"): hotkey = Prompt.ask("Enter hotkey name", default=defaults.wallet.hotkey) config.wallet.hotkey = str(hotkey) + if not config.no_prompt: + check_for_cuda_reg_config(config) + class RunFaucetCommand: @staticmethod @@ -248,17 +269,19 @@ def run(cli): subtensor.run_faucet( wallet=wallet, prompt=not cli.config.no_prompt, - TPB=cli.config.register.cuda.get("TPB", None), - update_interval=cli.config.register.get("update_interval", None), - num_processes=cli.config.register.get("num_processes", None), - cuda=cli.config.register.cuda.get( - "use_cuda", defaults.register.cuda.use_cuda + TPB=cli.config.pow_register.cuda.get("TPB", None), + update_interval=cli.config.pow_register.get("update_interval", None), + num_processes=cli.config.pow_register.get("num_processes", None), + cuda=cli.config.pow_register.cuda.get( + "use_cuda", defaults.pow_register.cuda.use_cuda ), - dev_id=cli.config.register.cuda.get("dev_id", None), - output_in_place=cli.config.register.get( - "output_in_place", defaults.register.output_in_place + dev_id=cli.config.pow_register.cuda.get("dev_id", None), + output_in_place=cli.config.pow_register.get( + "output_in_place", defaults.pow_register.output_in_place + ), + log_verbose=cli.config.pow_register.get( + "verbose", defaults.pow_register.verbose ), - log_verbose=cli.config.register.get("verbose", defaults.register.verbose), ) @staticmethod @@ -267,74 +290,74 @@ def add_args(parser: argparse.ArgumentParser): "faucet", help="""Perform PoW to receieve test TAO in your wallet.""" ) run_faucet_parser.add_argument( - "--register.num_processes", + "--faucet.num_processes", "-n", - dest="register.num_processes", + dest="pow_register.num_processes", help="Number of processors to use for POW registration", type=int, - default=defaults.register.num_processes, + default=defaults.pow_register.num_processes, ) run_faucet_parser.add_argument( - "--register.update_interval", - "--register.cuda.update_interval", + "--faucet.update_interval", + "--faucet.cuda.update_interval", "--cuda.update_interval", "-u", help="The number of nonces to process before checking for next block during registration", type=int, - default=defaults.register.update_interval, + default=defaults.pow_register.update_interval, ) run_faucet_parser.add_argument( - "--register.no_output_in_place", + "--faucet.no_output_in_place", "--no_output_in_place", - dest="register.output_in_place", + dest="pow_register.output_in_place", help="Whether to not ouput the registration statistics in-place. Set flag to disable output in-place.", action="store_false", required=False, - default=defaults.register.output_in_place, + default=defaults.pow_register.output_in_place, ) run_faucet_parser.add_argument( - "--register.verbose", + "--faucet.verbose", help="Whether to ouput the registration statistics verbosely.", action="store_true", required=False, - default=defaults.register.verbose, + default=defaults.pow_register.verbose, ) ## Registration args for CUDA registration. run_faucet_parser.add_argument( - "--register.cuda.use_cuda", + "--faucet.cuda.use_cuda", "--cuda", "--cuda.use_cuda", - dest="register.cuda.use_cuda", - default=defaults.register.cuda.use_cuda, - help="""Set flag to use CUDA to register.""", + dest="pow_register.cuda.use_cuda", + default=defaults.pow_register.cuda.use_cuda, + help="""Set flag to use CUDA to pow_register.""", action="store_true", required=False, ) run_faucet_parser.add_argument( - "--register.cuda.no_cuda", + "--faucet.cuda.no_cuda", "--no_cuda", "--cuda.no_cuda", - dest="register.cuda.use_cuda", - default=not defaults.register.cuda.use_cuda, + dest="pow_register.cuda.use_cuda", + default=not defaults.pow_register.cuda.use_cuda, help="""Set flag to not use CUDA for registration""", action="store_false", required=False, ) run_faucet_parser.add_argument( - "--register.cuda.dev_id", + "--faucet.cuda.dev_id", "--cuda.dev_id", type=int, nargs="+", - default=defaults.register.cuda.dev_id, + default=defaults.pow_register.cuda.dev_id, help="""Set the CUDA device id(s). Goes by the order of speed. (i.e. 0 is the fastest).""", required=False, ) run_faucet_parser.add_argument( - "--register.cuda.TPB", + "--faucet.cuda.TPB", "--cuda.TPB", type=int, - default=defaults.register.cuda.TPB, + default=defaults.pow_register.cuda.TPB, help="""Set the number of Threads Per Block for CUDA.""", required=False, ) diff --git a/bittensor/commands/root.py b/bittensor/commands/root.py index 0886a032de..78c31fa3d7 100644 --- a/bittensor/commands/root.py +++ b/bittensor/commands/root.py @@ -20,6 +20,7 @@ import torch import typing import argparse +import numpy as np import bittensor from typing import List, Optional, Dict from rich.prompt import Prompt, Confirm @@ -222,36 +223,58 @@ def run(cli): table = Table(show_footer=False) table.title = "[white]Root Network Weights" table.add_column( - "[overline white]UID", + "[white]UID", + header_style="overline white", footer_style="overline white", style="rgb(50,163,219)", no_wrap=True, ) - table.add_column( - "[overline white]NETUID", - footer_style="overline white", - justify="right", - style="green", - no_wrap=True, - ) - table.add_column( - "[overline white]WEIGHT", - footer_style="overline white", - justify="right", - style="green", - no_wrap=True, - ) - table.show_footer = True + uid_to_weights = {} + netuids = set() for matrix in weights: - uid = matrix[0] - for weight_data in matrix[1]: - table.add_row( - str(uid), - str(weight_data[0]), - "{:0.2f}%".format((weight_data[1] / 65535) * 100), + [uid, weights_data] = matrix + + if not len(weights_data): + uid_to_weights[uid] = {} + normalized_weights = [] + else: + normalized_weights = np.array(weights_data)[:, 1] / max( + np.sum(weights_data, axis=0)[1], 1 ) + for weight_data, normalized_weight in zip(weights_data, normalized_weights): + [netuid, _] = weight_data + netuids.add(netuid) + if uid not in uid_to_weights: + uid_to_weights[uid] = {} + + uid_to_weights[uid][netuid] = normalized_weight + + for netuid in netuids: + table.add_column( + f"[white]{netuid}", + header_style="overline white", + footer_style="overline white", + justify="right", + style="green", + no_wrap=True, + ) + + for uid in uid_to_weights: + row = [str(uid)] + + uid_weights = uid_to_weights[uid] + for netuid in netuids: + if netuid in uid_weights: + normalized_weight = uid_weights[netuid] + row.append("{:0.2f}%".format(normalized_weight * 100)) + else: + row.append("-") + table.add_row(*row) + + table.show_footer = True + table.box = None table.pad_edge = False table.width = None diff --git a/bittensor/commands/utils.py b/bittensor/commands/utils.py index b54053622a..732d43d68a 100644 --- a/bittensor/commands/utils.py +++ b/bittensor/commands/utils.py @@ -80,15 +80,15 @@ def check_for_cuda_reg_config(config: "bittensor.config") -> None: """Checks, when CUDA is available, if the user would like to register with their CUDA device.""" if torch.cuda.is_available(): if not config.no_prompt: - if config.register.cuda.get("use_cuda") == None: # flag not set + if config.pow_register.cuda.get("use_cuda") == None: # flag not set # Ask about cuda registration only if a CUDA device is available. cuda = Confirm.ask("Detected CUDA device, use CUDA for registration?\n") - config.register.cuda.use_cuda = cuda + config.pow_register.cuda.use_cuda = cuda # Only ask about which CUDA device if the user has more than one CUDA device. if ( - config.register.cuda.use_cuda - and config.register.cuda.get("dev_id") is None + config.pow_register.cuda.use_cuda + and config.pow_register.cuda.get("dev_id") is None ): devices: List[str] = [str(x) for x in range(torch.cuda.device_count())] device_names: List[str] = [ @@ -122,11 +122,11 @@ def check_for_cuda_reg_config(config: "bittensor.config") -> None: ) ) sys.exit(1) - config.register.cuda.dev_id = dev_id + config.pow_register.cuda.dev_id = dev_id else: # flag was not set, use default value. - if config.register.cuda.get("use_cuda") is None: - config.register.cuda.use_cuda = defaults.register.cuda.use_cuda + if config.pow_register.cuda.get("use_cuda") is None: + config.pow_register.cuda.use_cuda = defaults.pow_register.cuda.use_cuda def get_hotkey_wallets_for_wallet(wallet) -> List["bittensor.wallet"]: diff --git a/bittensor/commands/wallets.py b/bittensor/commands/wallets.py index 5417aa5496..9e892b5524 100644 --- a/bittensor/commands/wallets.py +++ b/bittensor/commands/wallets.py @@ -20,6 +20,7 @@ import os import sys from rich.prompt import Prompt, Confirm +from rich.table import Table from typing import Optional, List from . import defaults @@ -527,3 +528,129 @@ def check_config(config: "bittensor.Config"): "Enter wallet name", default=bittensor.defaults.wallet.name ) config.wallet.name = str(wallet_name) + + +def _get_coldkey_ss58_addresses_for_path(path: str) -> List[str]: + """Get all coldkey ss58 addresses from path.""" + + def list_coldkeypub_files(dir_path): + abspath = os.path.abspath(os.path.expanduser(dir_path)) + coldkey_files = [] + + for file in os.listdir(abspath): + coldkey_path = os.path.join(abspath, file, "coldkeypub.txt") + if os.path.exists(coldkey_path): + coldkey_files.append(coldkey_path) + else: + bittensor.logging.warning( + f"{coldkey_path} does not exist. Excluding..." + ) + return coldkey_files + + return [ + bittensor.keyfile(file).keypair.ss58_address + for file in list_coldkeypub_files(path) + ] + + +class WalletBalanceCommand: + @staticmethod + def run(cli): + """Check the balance of the wallet.""" + wallet_names = os.listdir(os.path.expanduser(cli.config.wallet.path)) + coldkeys = _get_coldkey_ss58_addresses_for_path(cli.config.wallet.path) + subtensor = bittensor.subtensor(config=cli.config) + + free_balances = [ + subtensor.get_balance(coldkeys[i]) for i in range(len(coldkeys)) + ] + staked_balances = [ + subtensor.get_total_stake_for_coldkey(coldkeys[i]) + for i in range(len(coldkeys)) + ] + total_free_balance = sum(free_balances) + total_staked_balance = sum(staked_balances) + + balances = { + name: (coldkey, free, staked) + for name, coldkey, free, staked in sorted( + zip(wallet_names, coldkeys, free_balances, staked_balances) + ) + } + + table = Table(show_footer=False) + table.title = "[white]Wallet Coldkey Balances" + table.add_column( + "[white]Wallet Name", + header_style="overline white", + footer_style="overline white", + style="rgb(50,163,219)", + no_wrap=True, + ) + + table.add_column( + "[white]Coldkey Address", + header_style="overline white", + footer_style="overline white", + style="rgb(50,163,219)", + no_wrap=True, + ) + + for typestr in ["Free", "Staked", "Total"]: + table.add_column( + f"[white]{typestr} Balance", + header_style="overline white", + footer_style="overline white", + justify="right", + style="green", + no_wrap=True, + ) + + for name, (coldkey, free, staked) in balances.items(): + table.add_row( + name, + coldkey, + str(free), + str(staked), + str(free + staked), + ) + table.add_row() + table.add_row( + "Total Balance Across All Coldkeys", + "", + str(total_free_balance), + str(total_staked_balance), + str(total_free_balance + total_staked_balance), + ) + table.show_footer = True + + table.box = None + table.pad_edge = False + table.width = None + bittensor.__console__.print(table) + + @staticmethod + def add_args(parser: argparse.ArgumentParser): + balance_parser = parser.add_parser( + "balance", help="""Checks the balance of the wallet.""" + ) + bittensor.wallet.add_args(balance_parser) + bittensor.subtensor.add_args(balance_parser) + + @staticmethod + def check_config(config: "bittensor.config"): + if not config.is_set("wallet.path") and not config.no_prompt: + path = Prompt.ask("Enter wallets path", default=defaults.wallet.path) + config.wallet.path = str(path) + + if not config.is_set("subtensor.network") and not config.no_prompt: + network = Prompt.ask( + "Enter network", + default=defaults.subtensor.network, + choices=bittensor.__networks__, + ) + config.subtensor.network = str(network) + ( + _, + config.subtensor.chain_endpoint, + ) = bittensor.subtensor.determine_chain_endpoint_and_network(str(network)) diff --git a/bittensor/dendrite.py b/bittensor/dendrite.py index 9e1cd0a5d9..a99b28e300 100644 --- a/bittensor/dendrite.py +++ b/bittensor/dendrite.py @@ -26,7 +26,7 @@ import aiohttp import bittensor from fastapi import Response -from typing import Union, Optional, List, Union +from typing import Union, Optional, List, Union, AsyncGenerator, Any class dendrite(torch.nn.Module): @@ -42,15 +42,52 @@ class dendrite(torch.nn.Module): Attributes: keypair: The wallet or keypair used for signing messages. + external_ip (str): The external IP address of the local system. + synapse_history (list): A list of Synapse objects representing the historical responses. Methods: __str__(): Returns a string representation of the Dendrite object. __repr__(): Returns a string representation of the Dendrite object, acting as a fallback for __str__(). + query(self, *args, **kwargs) -> Union[bittensor.Synapse, List[bittensor.Synapse]]: + Makes synchronous requests to one or multiple target Axons and returns responses. - Example: - >>> dendrite_obj = dendrite(wallet = bittensor.wallet() ) - >>> print(dendrite_obj) + forward(self, axons, synapse=bittensor.Synapse(), timeout=12, deserialize=True, run_async=True, streaming=False) -> bittensor.Synapse: + Asynchronously sends requests to one or multiple Axons and collates their responses. + + call(self, target_axon, synapse=bittensor.Synapse(), timeout=12.0, deserialize=True) -> bittensor.Synapse: + Asynchronously sends a request to a specified Axon and processes the response. + + call_stream(self, target_axon, synapse=bittensor.Synapse(), timeout=12.0, deserialize=True) -> AsyncGenerator[bittensor.Synapse, None]: + Sends a request to a specified Axon and yields an AsyncGenerator that contains streaming + response chunks before finally yielding the filled Synapse as the final element. + + preprocess_synapse_for_request(self, target_axon_info, synapse, timeout=12.0) -> bittensor.Synapse: + Preprocesses the synapse for making a request, including building headers and signing. + + process_server_response(self, server_response, json_response, local_synapse): + Processes the server response, updates the local synapse state, and merges headers. + + close_session(self): + Synchronously closes the internal aiohttp client session. + + aclose_session(self): + Asynchronously closes the internal aiohttp client session. + + NOTE: When working with async aiohttp client sessions, it is recommended to use a context manager. + + Example with a context manager: + >>> aysnc with dendrite(wallet = bittensor.wallet()) as d: + >>> print(d) + >>> d( ) # ping axon + >>> d( [] ) # ping multiple + >>> d( bittensor.axon(), bittensor.Synapse ) + + However, you are able to safely call dendrite.query() without a context manager in a synchronous setting. + + Example without a context manager: + >>> d = dendrite(wallet = bittensor.wallet() ) + >>> print(d) >>> d( ) # ping axon >>> d( [] ) # ping multiple >>> d( bittensor.axon(), bittensor.Synapse ) @@ -87,21 +124,94 @@ def __init__( @property async def session(self) -> aiohttp.ClientSession: + """ + Asynchronous property that provides access to the internal aiohttp client session. + + If the session is not already initialized, this property will instantiate a new + aiohttp.ClientSession and return it. + + Returns: + aiohttp.ClientSession: The aiohttp client session instance. + """ if self._session is None: self._session = aiohttp.ClientSession() return self._session - async def close_session(self): + def close_session(self): + """ + Closes the internal aiohttp client session in a synchronous manner. + + This method ensures that the resources tied with the aiohttp client session are released. + It should be called when the session is no longer needed, typically during the cleanup phase. + + Usage: + dendrite_instance.close_session() + """ + if self._session: + loop = asyncio.get_event_loop() + loop.run_until_complete(self._session.close()) + self._session = None + + async def aclose_session(self): + """ + Asynchronously closes the internal aiohttp client session. + + Similar to the synchronous `close_session` method but designed to be used within + asynchronous contexts. This method ensures that all related resources are released. + + Usage: + await dendrite_instance.aclose_session() + """ if self._session: await self._session.close() self._session = None + def _get_endpoint_url(self, target_axon, request_name): + endpoint = ( + f"0.0.0.0:{str(target_axon.port)}" + if target_axon.ip == str(self.external_ip) + else f"{target_axon.ip}:{str(target_axon.port)}" + ) + return f"http://{endpoint}/{request_name}" + + def _handle_request_errors(self, synapse, request_name, exception): + if isinstance(exception, aiohttp.ClientConnectorError): + synapse.dendrite.status_code = "503" + synapse.dendrite.status_message = f"Service at {synapse.axon.ip}:{str(synapse.axon.port)}/{request_name} unavailable." + elif isinstance(exception, asyncio.TimeoutError): + synapse.dendrite.status_code = "408" + synapse.dendrite.status_message = ( + f"Timedout after {synapse.timeout} seconds." + ) + else: + synapse.dendrite.status_code = "422" + synapse.dendrite.status_message = ( + f"Failed to parse response object with error: {str(exception)}" + ) + + def _log_outgoing_request(self, synapse): + bittensor.logging.debug( + f"dendrite | --> | {synapse.get_total_size()} B | {synapse.name} | {synapse.axon.hotkey} | {synapse.axon.ip}:{str(synapse.axon.port)} | 0 | Success" + ) + + def _log_incoming_response(self, synapse): + bittensor.logging.debug( + f"dendrite | <-- | {synapse.get_total_size()} B | {synapse.name} | {synapse.axon.hotkey} | {synapse.axon.ip}:{str(synapse.axon.port)} | {synapse.dendrite.status_code} | {synapse.dendrite.status_message}" + ) + def query( self, *args, **kwargs - ) -> Union[bittensor.Synapse, List[bittensor.Synapse]]: + ) -> Union[ + bittensor.Synapse, + List[bittensor.Synapse], + bittensor.StreamingSynapse, + List[bittensor.StreamingSynapse], + ]: """ Makes a synchronous request to multiple target Axons and returns the server responses. + Cleanup is automatically handled and sessions are closed upon completed requests. + Args: axons (Union[List[Union['bittensor.AxonInfo', 'bittensor.axon']], Union['bittensor.AxonInfo', 'bittensor.axon']]): The list of target Axon information. @@ -115,13 +225,16 @@ def query( """ try: loop = asyncio.get_event_loop() - return loop.run_until_complete(self.forward(*args, **kwargs)) + result = loop.run_until_complete(self.forward(*args, **kwargs)) except: new_loop = asyncio.new_event_loop() asyncio.set_event_loop(new_loop) result = loop.run_until_complete(self.forward(*args, **kwargs)) new_loop.close() return result + finally: + self.close_session() + return result async def forward( self, @@ -133,21 +246,39 @@ async def forward( timeout: float = 12, deserialize: bool = True, run_async: bool = True, - ) -> bittensor.Synapse: + streaming: bool = False, + ) -> List[Union[AsyncGenerator[Any], bittenst.Synapse, bittensor.StreamingSynapse]]: """ - Makes asynchronous requests to multiple target Axons and returns the server responses. + Asynchronously sends requests to one or multiple Axons and collates their responses. + + This function acts as a bridge for sending multiple requests concurrently or sequentially + based on the provided parameters. It checks the type of the target Axons, preprocesses + the requests, and then sends them off. After getting the responses, it processes and + collates them into a unified format. + + When querying an Axon that sends back data in chunks using the Dendrite, this function + returns an AsyncGenerator that yields each chunk as it is received. The generator can be + iterated over to process each chunk individually. + + For example: + >>> ... + >>> dendrte = bittensor.dendrite(wallet = wallet) + >>> async for chunk in dendrite.forward(axons, synapse, timeout, deserialize, run_async, streaming): + >>> # Process each chunk here + >>> print(chunk) Args: axons (Union[List[Union['bittensor.AxonInfo', 'bittensor.axon']], Union['bittensor.AxonInfo', 'bittensor.axon']]): - The list of target Axon information. - synapse (bittensor.Synapse, optional): The Synapse object. Defaults to bittensor.Synapse(). - timeout (float, optional): The request timeout duration in seconds. - Defaults to 12.0 seconds. + The target Axons to send requests to. Can be a single Axon or a list of Axons. + synapse (bittensor.Synapse, optional): The Synapse object encapsulating the data. Defaults to a new bittensor.Synapse instance. + timeout (float, optional): Maximum duration to wait for a response from an Axon in seconds. Defaults to 12.0. + deserialize (bool, optional): Determines if the received response should be deserialized. Defaults to True. + run_async (bool, optional): If True, sends requests concurrently. Otherwise, sends requests sequentially. Defaults to True. + streaming (bool, optional): Indicates if the response is expected to be in streaming format. Defaults to False. Returns: - Union[bittensor.Synapse, List[bittensor.Synapse]]: If a single target axon is provided, - returns the response from that axon. If multiple target axons are provided, - returns a list of responses from all target axons. + Union[AsyncGenerator, bittensor.Synapse, List[bittensor.Synapse]]: If a single Axon is targeted, returns its response. + If multiple Axons are targeted, returns a list of their responses. """ is_list = True # If a single axon is provided, wrap it in a list for uniform processing @@ -155,44 +286,72 @@ async def forward( is_list = False axons = [axons] - # This asynchronous function is used to send queries to all axons. - async def query_all_axons() -> List[bittensor.Synapse]: - # If the 'run_async' flag is not set, the code runs synchronously. - if not run_async: - # Create an empty list to hold the responses from all axons. - all_responses = [] - # Loop through each axon in the 'axons' list. - for target_axon in axons: - # The response from each axon is then appended to the 'all_responses' list. - all_responses.append( - await self.call( - target_axon=target_axon, - synapse=synapse.copy(), - timeout=timeout, - deserialize=deserialize, - ) + # Check if synapse is an instance of the StreamingSynapse class or if streaming flag is set. + is_streaming_subclass = issubclass( + synapse.__class__, bittensor.StreamingSynapse + ) + if streaming != is_streaming_subclass: + bittensor.logging.warning( + f"Argument streaming is {streaming} while issubclass(synapse, StreamingSynapse) is {synapse.__class__.__name__}. This may cause unexpected behavior." + ) + streaming = is_streaming_subclass or streaming + + async def query_all_axons( + is_stream: bool, + ) -> Union[AsyncGenerator[Any], bittenst.Synapse, bittensor.StreamingSynapse]: + """ + Handles requests for all axons, either in streaming or non-streaming mode. + + Args: + is_stream: If True, handles the axons in streaming mode. + + Returns: + List of Synapse objects with responses. + """ + + async def single_axon_response( + target_axon, + ) -> Union[ + AsyncGenerator[Any], bittenst.Synapse, bittensor.StreamingSynapse + ]: + """ + Retrieve response for a single axon, either in streaming or non-streaming mode. + + Args: + target_axon: The target axon to send request to. + + Returns: + A Synapse object with the response. + """ + if is_stream: + # If in streaming mode, return the async_generator + return self.call_stream( + target_axon=target_axon, + synapse=synapse.copy(), + timeout=timeout, + deserialize=deserialize, ) - # The function then returns a list of responses from all axons. - return all_responses - else: - # Here we build a list of coroutines without awaiting them. - coroutines = [ - self.call( + else: + # If not in streaming mode, simply call the axon and get the response. + return await self.call( target_axon=target_axon, synapse=synapse.copy(), timeout=timeout, deserialize=deserialize, ) - for target_axon in axons - ] - # 'asyncio.gather' is a method which takes multiple coroutines and runs them in parallel. - all_responses = await asyncio.gather(*coroutines) - # The function then returns a list of responses from all axons. - return all_responses - # Run all requests concurrently and get the responses - responses = await query_all_axons() + # If run_async flag is False, get responses one by one. + if not run_async: + return [ + await single_axon_response(target_axon) for target_axon in axons + ] + # If run_async flag is True, get responses concurrently using asyncio.gather(). + return await asyncio.gather( + *(single_axon_response(target_axon) for target_axon in axons) + ) + # Get responses for all axons. + responses = await query_all_axons(streaming) # Return the single response if only one axon was targeted, else return all responses if len(responses) == 1 and not is_list: return responses[0] @@ -207,19 +366,97 @@ async def call( deserialize: bool = True, ) -> bittensor.Synapse: """ - Makes an asynchronous request to the target Axon, processes the server - response and returns the updated Synapse. + Asynchronously sends a request to a specified Axon and processes the response. + + This function establishes a connection with a specified Axon, sends the encapsulated + data through the Synapse object, waits for a response, processes it, and then + returns the updated Synapse object. Args: - target_axon (Union['bittensor.AxonInfo', 'bittensor.axon']): The target Axon information. - synapse (bittensor.Synapse, optional): The Synapse object. Defaults to bittensor.Synapse(). - timeout (float, optional): The request timeout duration in seconds. - Defaults to 12.0 seconds. - deserialize (bool, optional): Whether to deserialize the returned Synapse. - Defaults to True. + target_axon (Union['bittensor.AxonInfo', 'bittensor.axon']): The target Axon to send the request to. + synapse (bittensor.Synapse, optional): The Synapse object encapsulating the data. Defaults to a new bittensor.Synapse instance. + timeout (float, optional): Maximum duration to wait for a response from the Axon in seconds. Defaults to 12.0. + deserialize (bool, optional): Determines if the received response should be deserialized. Defaults to True. Returns: - bittensor.Synapse: The updated Synapse object after processing server response. + bittensor.Synapse: The Synapse object, updated with the response data from the Axon. + """ + + # Record start time + start_time = time.time() + target_axon = ( + target_axon.info() + if isinstance(target_axon, bittensor.axon) + else target_axon + ) + + # Build request endpoint from the synapse class + request_name = synapse.__class__.__name__ + url = self._get_endpoint_url(target_axon, request_name=request_name) + + # Preprocess synapse for making a request + synapse = self.preprocess_synapse_for_request(target_axon, synapse, timeout) + + try: + # Log outgoing request + self._log_outgoing_request(synapse) + + # Make the HTTP POST request + async with (await self.session).post( + url, + headers=synapse.to_headers(), + json=synapse.dict(), + timeout=timeout, + ) as response: + # Extract the JSON response from the server + json_response = await response.json() + # Process the server response and fill synapse + self.process_server_response(response, json_response, synapse) + + # Set process time and log the response + synapse.dendrite.process_time = str(time.time() - start_time) + + except Exception as e: + self._handle_request_errors(synapse, request_name, e) + + finally: + self._log_incoming_response(synapse) + + # Log synapse event history + self.synapse_history.append( + bittensor.Synapse.from_headers(synapse.to_headers()) + ) + + # Return the updated synapse object after deserializing if requested + if deserialize: + return synapse.deserialize() + else: + return synapse + + async def call_stream( + self, + target_axon: Union[bittensor.AxonInfo, bittensor.axon], + synapse: bittensor.Synapse = bittensor.Synapse(), + timeout: float = 12.0, + deserialize: bool = True, + ) -> AsyncGenerator[Any]: + """ + Sends a request to a specified Axon and yields streaming responses. + + Similar to `call`, but designed for scenarios where the Axon sends back data in + multiple chunks or streams. The function yields each chunk as it is received. This is + useful for processing large responses piece by piece without waiting for the entire + data to be transmitted. + + Args: + target_axon (Union['bittensor.AxonInfo', 'bittensor.axon']): The target Axon to send the request to. + synapse (bittensor.Synapse, optional): The Synapse object encapsulating the data. Defaults to a new bittensor.Synapse instance. + timeout (float, optional): Maximum duration to wait for a response (or a chunk of the response) from the Axon in seconds. Defaults to 12.0. + deserialize (bool, optional): Determines if each received chunk should be deserialized. Defaults to True. + + Yields: + object: Each yielded object contains a chunk of the arbitrary response data from the Axon. + bittensor.Synapse: After the AsyncGenerator has been exhausted, yields the final filled Synapse. """ # Record start time @@ -244,9 +481,7 @@ async def call( try: # Log outgoing request - bittensor.logging.debug( - f"dendrite | --> | {synapse.get_total_size()} B | {synapse.name} | {synapse.axon.hotkey} | {synapse.axon.ip}:{str(synapse.axon.port)} | 0 | Success" - ) + self._log_outgoing_request(synapse) # Make the HTTP POST request async with (await self.session).post( @@ -255,16 +490,10 @@ async def call( json=synapse.dict(), timeout=timeout, ) as response: - if ( - response.headers.get("Content-Type", "").lower() - == "text/event-stream".lower() - ): # identify streaming response - await synapse.process_streaming_response( - response - ) # process the entire streaming response - json_response = synapse.extract_response_json(response) - else: - json_response = await response.json() + # Use synapse subclass' process_streaming_response method to yield the response chunks + async for chunk in synapse.process_streaming_response(response): + yield chunk # Yield each chunk as it's processed + json_response = synapse.extract_response_json(response) # Process the server response self.process_server_response(response, json_response, synapse) @@ -272,24 +501,11 @@ async def call( # Set process time and log the response synapse.dendrite.process_time = str(time.time() - start_time) - except aiohttp.ClientConnectorError as e: - synapse.dendrite.status_code = "503" - synapse.dendrite.status_message = f"Service at {synapse.axon.ip}:{str(synapse.axon.port)}/{request_name} unavailable." - - except asyncio.TimeoutError as e: - synapse.dendrite.status_code = "408" - synapse.dendrite.status_message = f"Timedout after {timeout} seconds." - except Exception as e: - synapse.dendrite.status_code = "422" - synapse.dendrite.status_message = ( - f"Failed to parse response object with error: {str(e)}" - ) + self._handle_request_errors(synapse, request_name, e) finally: - bittensor.logging.debug( - f"dendrite | <-- | {synapse.get_total_size()} B | {synapse.name} | {synapse.axon.hotkey} | {synapse.axon.ip}:{str(synapse.axon.port)} | {synapse.dendrite.status_code} | {synapse.dendrite.status_message}" - ) + self._log_incoming_response(synapse) # Log synapse event history self.synapse_history.append( @@ -298,9 +514,9 @@ async def call( # Return the updated synapse object after deserializing if requested if deserialize: - return synapse.deserialize() + yield synapse.deserialize() else: - return synapse + yield synapse def preprocess_synapse_for_request( self, @@ -423,3 +639,55 @@ def __repr__(self) -> str: str: The string representation of the Dendrite object in the format "dendrite()". """ return self.__str__() + + async def __aenter__(self): + """ + Asynchronous context manager entry method. + + Enables the use of the `async with` statement with the Dendrite instance. When entering the context, + the current instance of the class is returned, making it accessible within the asynchronous context. + + Returns: + Dendrite: The current instance of the Dendrite class. + + Usage: + async with Dendrite() as dendrite: + await dendrite.some_async_method() + """ + return self + + async def __aexit__(self, exc_type, exc_value, traceback): + """ + Asynchronous context manager exit method. + + Ensures proper cleanup when exiting the `async with` context. This method will close the aiohttp client session + asynchronously, releasing any tied resources. + + Args: + exc_type (Type[BaseException], optional): The type of exception that was raised. + exc_value (BaseException, optional): The instance of exception that was raised. + traceback (TracebackType, optional): A traceback object encapsulating the call stack at the point + where the exception was raised. + + Usage: + async with Dendrite() as dendrite: + await dendrite.some_async_method() + """ + await self.aclose_session() + + def __del__(self): + """ + Dendrite destructor. + + This method is invoked when the Dendrite instance is about to be destroyed. The destructor ensures that the + aiohttp client session is closed before the instance is fully destroyed, releasing any remaining resources. + + Note: Relying on the destructor for cleanup can be unpredictable. It's recommended to explicitly close sessions + using the provided methods or the `async with` context manager. + + Usage: + dendrite = Dendrite() + # ... some operations ... + del dendrite # This will implicitly invoke the __del__ method. + """ + asyncio.run(self.aclose_session()) diff --git a/bittensor/extrinsics/registration.py b/bittensor/extrinsics/registration.py index 9799234363..c759fbcb58 100644 --- a/bittensor/extrinsics/registration.py +++ b/bittensor/extrinsics/registration.py @@ -321,6 +321,14 @@ def burned_register_extrinsic( ) +class MaxSuccessException(Exception): + pass + + +class MaxAttemptsException(Exception): + pass + + def run_faucet_extrinsic( subtensor: "bittensor.subtensor", wallet: "bittensor.wallet", @@ -384,6 +392,7 @@ def run_faucet_extrinsic( # Attempt rolling registration. attempts = 1 + successes = 1 while True: try: pow_result = None @@ -441,6 +450,9 @@ def run_faucet_extrinsic( bittensor.__console__.print( f":cross_mark: [red]Failed[/red]: Error: {response.error_message}" ) + if attempts == max_allowed_attempts: + raise MaxAttemptsException + attempts += 1 # Successful registration else: @@ -450,5 +462,15 @@ def run_faucet_extrinsic( ) old_balance = new_balance + if successes == 3: + raise MaxSuccessException + successes += 1 + except KeyboardInterrupt: return True, "Done" + + except MaxSuccessException: + return True, f"Max successes reached: {3}" + + except MaxAttemptedException: + return False, f"Max attempts reached: {max_allowed_attempts}" diff --git a/bittensor/metagraph.py b/bittensor/metagraph.py index 2c31ccf6b1..789fb3b7a8 100644 --- a/bittensor/metagraph.py +++ b/bittensor/metagraph.py @@ -340,7 +340,6 @@ def sync( block: Optional[int] = None, lite: bool = True, subtensor: Optional["bittensor.subtensor"] = None, - root: bool = False, ) -> "metagraph": """ Initiates the synchronization process of the metagraph. @@ -364,7 +363,7 @@ def sync( # If not a 'lite' version, compute and set weights and bonds for each neuron if not lite: - self._set_weights_and_bonds(root=root, subtensor=subtensor) + self._set_weights_and_bonds(subtensor=subtensor) def _initialize_subtensor(self, subtensor): """ @@ -474,9 +473,7 @@ def _create_tensor(self, data, dtype) -> torch.nn.Parameter: # TODO: Check and test the creation of tensor return torch.nn.Parameter(torch.tensor(data, dtype=dtype), requires_grad=False) - def _set_weights_and_bonds( - self, root: bool = False, subtensor: bittensor.subtensor = None - ): + def _set_weights_and_bonds(self, subtensor: bittensor.subtensor = None): """ Computes and sets weights and bonds for each neuron. @@ -484,7 +481,7 @@ def _set_weights_and_bonds( None. """ # TODO: Check and test the computation of weights and bonds - if root: + if self.netuid == 0: self.weights = self._process_root_weights( [neuron.weights for neuron in self.neurons], "weights", subtensor ) diff --git a/bittensor/subtensor.py b/bittensor/subtensor.py index 99b15d5060..9eda2fada1 100644 --- a/bittensor/subtensor.py +++ b/bittensor/subtensor.py @@ -2310,7 +2310,6 @@ def metagraph( netuid: int, lite: bool = True, block: Optional[int] = None, - root: bool = False, ) -> "bittensor.Metagraph": r"""Returns a synced metagraph for the subnet. Args: @@ -2327,7 +2326,7 @@ def metagraph( metagraph_ = bittensor.metagraph( network=self.network, netuid=netuid, lite=lite, sync=False ) - metagraph_.sync(block=block, lite=lite, subtensor=self, root=root) + metagraph_.sync(block=block, lite=lite, subtensor=self) return metagraph_ diff --git a/bittensor/synapse.py b/bittensor/synapse.py index 0cb3a118b9..cb139d6648 100644 --- a/bittensor/synapse.py +++ b/bittensor/synapse.py @@ -410,7 +410,6 @@ def to_headers(self) -> dict: Headers for 'name' and 'timeout' are directly taken from the instance. Further headers are constructed from the properties 'axon' and 'dendrite'. - If the object is a tensor, its shape and data type are added to the headers. For non-optional objects, these are serialized and encoded before adding to the headers. Finally, the function adds the sizes of the headers and the total size to the headers. @@ -441,7 +440,7 @@ def to_headers(self) -> dict: property_type_hints = typing.get_type_hints(self) # Getting the fields of the instance - instance_fields = self.__dict__ + instance_fields = self.dict() # Iterating over the fields of the instance for field, value in instance_fields.items(): @@ -454,28 +453,6 @@ def to_headers(self) -> dict: if field in headers or value is None: continue - # Adding the tensor shape and data type to the headers if the object is a tensor - if isinstance(value, bittensor.Tensor): - headers[f"bt_header_tensor_{field}"] = f"{value.shape}-{value.dtype}" - - elif isinstance(value, list) and all( - isinstance(elem, bittensor.Tensor) for elem in value - ): - serialized_list_tensor = [] - for i, tensor in enumerate(value): - serialized_list_tensor.append(f"{tensor.shape}-{tensor.dtype}") - headers[f"bt_header_list_tensor_{field}"] = str(serialized_list_tensor) - - elif isinstance(value, dict) and all( - isinstance(elem, bittensor.Tensor) for elem in value.values() - ): - serialized_dict_tensor = [] - for key, tensor in value.items(): - serialized_dict_tensor.append( - f"{key}-{tensor.shape}-{tensor.dtype}" - ) - headers[f"bt_header_dict_tensor_{field}"] = str(serialized_dict_tensor) - elif required and field in required: try: # create an empty (dummy) instance of type(value) to pass pydantic validation on the axon side @@ -563,50 +540,6 @@ def parse_headers_to_inputs(cls, headers: dict) -> dict: f"Error while parsing 'dendrite' header {key}: {e}" ) continue - # Handle 'tensor' headers - elif "bt_header_tensor_" in key: - try: - new_key = key.split("bt_header_tensor_")[1] - shape, dtype = value.split("-") - # TODO: Verify if the shape and dtype values need to be converted before being used - inputs_dict[new_key] = bittensor.Tensor(shape=shape, dtype=dtype) - except Exception as e: - bittensor.logging.error( - f"Error while parsing 'tensor' header {key}: {e}" - ) - continue - elif "bt_header_list_tensor_" in key: - try: - new_key = key.split("bt_header_list_tensor_")[1] - deserialized_tensors = [] - stensors = ast.literal_eval(value) - for value in stensors: - shape, dtype = value.split("-") - deserialized_tensors.append( - bittensor.Tensor(shape=shape, dtype=dtype) - ) - inputs_dict[new_key] = deserialized_tensors - except Exception as e: - bittensor.logging.error( - f"Error while parsing 'tensor' header {key}: {e}" - ) - continue - elif "bt_header_dict_tensor_" in key: - try: - new_key = key.split("bt_header_dict_tensor_")[1] - deserialized_dict_tensors = {} - stensors = ast.literal_eval(value) - for value in stensors: - key, shape, dtype = value.split("-") - deserialized_dict_tensors[key] = bittensor.Tensor( - shape=shape, dtype=dtype - ) - inputs_dict[new_key] = deserialized_dict_tensors - except Exception as e: - bittensor.logging.error( - f"Error while parsing 'tensor' header {key}: {e}" - ) - continue # Handle 'input_obj' headers elif "bt_header_input_obj" in key: try: diff --git a/bittensor/utils/__init__.py b/bittensor/utils/__init__.py index 6324461965..dea833d72b 100644 --- a/bittensor/utils/__init__.py +++ b/bittensor/utils/__init__.py @@ -26,7 +26,7 @@ from substrateinterface.utils import ss58 as ss58 from .wallet_utils import * -from .registration import create_pow as create_pow, __reregister_wallet as reregister +from .registration import create_pow as create_pow RAOPERTAO = 1e9 U16_MAX = 65535 diff --git a/bittensor/utils/registration.py b/bittensor/utils/registration.py index d86f7d85fc..29a1d5d7f3 100644 --- a/bittensor/utils/registration.py +++ b/bittensor/utils/registration.py @@ -1087,51 +1087,3 @@ def create_pow( ) return solution - - -def __reregister_wallet( - netuid: int, - wallet: "bittensor.wallet", - subtensor: "bittensor.subtensor", - reregister: bool = False, - prompt: bool = False, - **registration_args: Any, -) -> Optional["bittensor.wallet"]: - """Re-register this a wallet on the chain, or exits. - Exits if the wallet is not registered on the chain AND - reregister is set to False. - Args: - netuid (int): - The network uid of the subnet to register on. - wallet( 'bittensor.wallet' ): - Bittensor wallet to re-register - reregister (bool, default=False): - If true, re-registers the wallet on the chain. - Exits if False and the wallet is not registered on the chain. - prompt (bool): - If true, the call waits for confirmation from the user before proceeding. - **registration_args (Any): - The registration arguments to pass to the subtensor register function. - Return: - wallet (bittensor.wallet): - The wallet - - Raises: - SytemExit(0): - If the wallet is not registered on the chain AND - the config.subtensor.reregister flag is set to False. - """ - wallet.hotkey - - if not subtensor.is_hotkey_registered_on_subnet( - hotkey_ss58=wallet.hotkey.ss58_address, netuid=netuid - ): - # Check if the wallet should reregister - if not reregister: - sys.exit(0) - - subtensor.register( - wallet=wallet, netuid=netuid, prompt=prompt, **registration_args - ) - - return wallet diff --git a/bittensor/utils/registratrion_old.py b/bittensor/utils/registratrion_old.py deleted file mode 100644 index 453772d649..0000000000 --- a/bittensor/utils/registratrion_old.py +++ /dev/null @@ -1,1030 +0,0 @@ -import binascii -import hashlib -import math -import multiprocessing -import os -import random -import time -from dataclasses import dataclass -from datetime import timedelta -from queue import Empty, Full -from typing import Any, Callable, Dict, List, Optional, Tuple, Union - -import backoff -import bittensor -import torch -from Crypto.Hash import keccak -from rich import console as rich_console -from rich import status as rich_status - -from ._register_cuda import solve_cuda - - -class CUDAException(Exception): - """An exception raised when an error occurs in the CUDA environment.""" - - pass - - -def hex_bytes_to_u8_list(hex_bytes: bytes): - hex_chunks = [int(hex_bytes[i : i + 2], 16) for i in range(0, len(hex_bytes), 2)] - return hex_chunks - - -def u8_list_to_hex(values: list): - total = 0 - for val in reversed(values): - total = (total << 8) + val - return total - - -def create_seal_hash(block_hash: bytes, nonce: int) -> bytes: - block_bytes = block_hash.encode("utf-8")[2:] - nonce_bytes = binascii.hexlify(nonce.to_bytes(8, "little")) - pre_seal = nonce_bytes + block_bytes - seal_sh256 = hashlib.sha256(bytearray(hex_bytes_to_u8_list(pre_seal))).digest() - kec = keccak.new(digest_bits=256) - seal = kec.update(seal_sh256).digest() - return seal - - -def seal_meets_difficulty(seal: bytes, difficulty: int): - seal_number = int.from_bytes(seal, "big") - product = seal_number * difficulty - limit = int(math.pow(2, 256)) - 1 - if product > limit: - return False - else: - return True - - -def solve_for_difficulty(block_hash, difficulty): - meets = False - nonce = -1 - while not meets: - nonce += 1 - seal = create_seal_hash(block_hash, nonce) - meets = seal_meets_difficulty(seal, difficulty) - if nonce > 1: - break - return nonce, seal - - -def get_human_readable(num, suffix="H"): - for unit in ["", "K", "M", "G", "T", "P", "E", "Z"]: - if abs(num) < 1000.0: - return f"{num:3.1f}{unit}{suffix}" - num /= 1000.0 - return f"{num:.1f}Y{suffix}" - - -def millify(n: int): - millnames = ["", " K", " M", " B", " T"] - n = float(n) - millidx = max( - 0, - min( - len(millnames) - 1, int(math.floor(0 if n == 0 else math.log10(abs(n)) / 3)) - ), - ) - - return "{:.2f}{}".format(n / 10 ** (3 * millidx), millnames[millidx]) - - -def POWNotStale(subtensor: "bittensor.subtensor", pow_result: Dict) -> bool: - """Returns True if the POW is not stale. - This means the block the POW is solved for is within 3 blocks of the current block. - """ - return pow_result["block_number"] >= subtensor.get_current_block() - 3 - - -@dataclass -class POWSolution: - """A solution to the registration PoW problem.""" - - nonce: int - block_number: int - difficulty: int - seal: bytes - - -class SolverBase(multiprocessing.Process): - """ - A process that solves the registration PoW problem. - Args: - proc_num: int - The number of the process being created. - num_proc: int - The total number of processes running. - update_interval: int - The number of nonces to try to solve before checking for a new block. - finished_queue: multiprocessing.Queue - The queue to put the process number when a process finishes each update_interval. - Used for calculating the average time per update_interval across all processes. - solution_queue: multiprocessing.Queue - The queue to put the solution the process has found during the pow solve. - newBlockEvent: multiprocessing.Event - The event to set by the main process when a new block is finalized in the network. - The solver process will check for the event after each update_interval. - The solver process will get the new block hash and difficulty and start solving for a new nonce. - stopEvent: multiprocessing.Event - The event to set by the main process when all the solver processes should stop. - The solver process will check for the event after each update_interval. - The solver process will stop when the event is set. - Used to stop the solver processes when a solution is found. - curr_block: multiprocessing.Array - The array containing this process's current block hash. - The main process will set the array to the new block hash when a new block is finalized in the network. - The solver process will get the new block hash from this array when newBlockEvent is set. - curr_block_num: multiprocessing.Value - The value containing this process's current block number. - The main process will set the value to the new block number when a new block is finalized in the network. - The solver process will get the new block number from this value when newBlockEvent is set. - curr_diff: multiprocessing.Array - The array containing this process's current difficulty. - The main process will set the array to the new difficulty when a new block is finalized in the network. - The solver process will get the new difficulty from this array when newBlockEvent is set. - check_block: multiprocessing.Lock - The lock to prevent this process from getting the new block data while the main process is updating the data. - limit: int - The limit of the pow solve for a valid solution. - """ - - proc_num: int - num_proc: int - update_interval: int - finished_queue: multiprocessing.Queue - solution_queue: multiprocessing.Queue - newBlockEvent: multiprocessing.Event - stopEvent: multiprocessing.Event - curr_block: multiprocessing.Array - curr_block_num: multiprocessing.Value - curr_diff: multiprocessing.Array - check_block: multiprocessing.Lock - limit: int - - def __init__( - self, - proc_num, - num_proc, - update_interval, - finished_queue, - solution_queue, - stopEvent, - curr_block, - curr_block_num, - curr_diff, - check_block, - limit, - ): - multiprocessing.Process.__init__(self, daemon=True) - self.proc_num = proc_num - self.num_proc = num_proc - self.update_interval = update_interval - self.finished_queue = finished_queue - self.solution_queue = solution_queue - self.newBlockEvent = multiprocessing.Event() - self.newBlockEvent.clear() - self.curr_block = curr_block - self.curr_block_num = curr_block_num - self.curr_diff = curr_diff - self.check_block = check_block - self.stopEvent = stopEvent - self.limit = limit - - def run(self): - raise NotImplementedError("SolverBase is an abstract class") - - -class Solver(SolverBase): - def run(self): - block_number: int - block_bytes: bytes - block_difficulty: int - nonce_limit = int(math.pow(2, 64)) - 1 - - # Start at random nonce - nonce_start = random.randint(0, nonce_limit) - nonce_end = nonce_start + self.update_interval - while not self.stopEvent.is_set(): - if self.newBlockEvent.is_set(): - with self.check_block: - block_number = self.curr_block_num.value - block_bytes = bytes(self.curr_block) - block_difficulty = registration_diff_unpack(self.curr_diff) - - self.newBlockEvent.clear() - - # Do a block of nonces - solution = solve_for_nonce_block( - self, - nonce_start, - nonce_end, - block_bytes, - block_difficulty, - self.limit, - block_number, - ) - if solution is not None: - self.solution_queue.put(solution) - - try: - # Send time - self.finished_queue.put_nowait(self.proc_num) - except Full: - pass - - nonce_start = random.randint(0, nonce_limit) - nonce_start = nonce_start % nonce_limit - nonce_end = nonce_start + self.update_interval - - -class CUDASolver(SolverBase): - dev_id: int - TPB: int - - def __init__( - self, - proc_num, - num_proc, - update_interval, - finished_queue, - solution_queue, - stopEvent, - curr_block, - curr_block_num, - curr_diff, - check_block, - limit, - dev_id: int, - TPB: int, - ): - super().__init__( - proc_num, - num_proc, - update_interval, - finished_queue, - solution_queue, - stopEvent, - curr_block, - curr_block_num, - curr_diff, - check_block, - limit, - ) - self.dev_id = dev_id - self.TPB = TPB - - def run(self): - block_number: int = 0 # dummy value - block_bytes: bytes = b"0" * 32 # dummy value - block_difficulty: int = int(math.pow(2, 64)) - 1 # dummy value - nonce_limit = int(math.pow(2, 64)) - 1 # U64MAX - - # Start at random nonce - nonce_start = random.randint(0, nonce_limit) - while not self.stopEvent.is_set(): - if self.newBlockEvent.is_set(): - with self.check_block: - block_number = self.curr_block_num.value - block_bytes = bytes(self.curr_block) - block_difficulty = registration_diff_unpack(self.curr_diff) - - self.newBlockEvent.clear() - - # Do a block of nonces - solution = solve_for_nonce_block_cuda( - self, - nonce_start, - self.update_interval, - block_bytes, - block_difficulty, - self.limit, - block_number, - self.dev_id, - self.TPB, - ) - if solution is not None: - self.solution_queue.put(solution) - - try: - # Signal that a nonce_block was finished using queue - # send our proc_num - self.finished_queue.put(self.proc_num) - except Full: - pass - - # increase nonce by number of nonces processed - nonce_start += self.update_interval * self.TPB - nonce_start = nonce_start % nonce_limit - - -def solve_for_nonce_block_cuda( - solver: CUDASolver, - nonce_start: int, - update_interval: int, - block_bytes: bytes, - difficulty: int, - limit: int, - block_number: int, - dev_id: int, - TPB: int, -) -> Optional[POWSolution]: - """Tries to solve the POW on a CUDA device for a block of nonces (nonce_start, nonce_start + update_interval * TPB""" - solution, seal = solve_cuda( - nonce_start, update_interval, TPB, block_bytes, difficulty, limit, dev_id - ) - - if solution != -1: - # Check if solution is valid (i.e. not -1) - return POWSolution(solution, block_number, difficulty, seal) - - return None - - -def solve_for_nonce_block( - solver: Solver, - nonce_start: int, - nonce_end: int, - block_bytes: bytes, - difficulty: int, - limit: int, - block_number: int, -) -> Optional[POWSolution]: - """Tries to solve the POW for a block of nonces (nonce_start, nonce_end)""" - for nonce in range(nonce_start, nonce_end): - # Create seal. - nonce_bytes = binascii.hexlify(nonce.to_bytes(8, "little")) - pre_seal = nonce_bytes + block_bytes - seal_sh256 = hashlib.sha256(bytearray(hex_bytes_to_u8_list(pre_seal))).digest() - kec = keccak.new(digest_bits=256) - seal = kec.update(seal_sh256).digest() - seal_number = int.from_bytes(seal, "big") - - # Check if seal meets difficulty - product = seal_number * difficulty - if product < limit: - # Found a solution, save it. - return POWSolution(nonce, block_number, difficulty, seal) - - return None - - -def registration_diff_unpack(packed_diff: multiprocessing.Array) -> int: - """Unpacks the packed two 32-bit integers into one 64-bit integer. Little endian.""" - return int(packed_diff[0] << 32 | packed_diff[1]) - - -def registration_diff_pack(diff: int, packed_diff: multiprocessing.Array): - """Packs the difficulty into two 32-bit integers. Little endian.""" - packed_diff[0] = diff >> 32 - packed_diff[1] = diff & 0xFFFFFFFF # low 32 bits - - -def update_curr_block( - curr_diff: multiprocessing.Array, - curr_block: multiprocessing.Array, - curr_block_num: multiprocessing.Value, - block_number: int, - block_bytes: bytes, - diff: int, - lock: multiprocessing.Lock, -): - with lock: - curr_block_num.value = block_number - for i in range(64): - curr_block[i] = block_bytes[i] - registration_diff_pack(diff, curr_diff) - - -def get_cpu_count(): - try: - return len(os.sched_getaffinity(0)) - except AttributeError: - # OSX does not have sched_getaffinity - return os.cpu_count() - - -@dataclass -class RegistrationStatistics: - """Statistics for a registration.""" - - time_spent_total: float - rounds_total: int - time_average: float - time_spent: float - hash_rate_perpetual: float - hash_rate: float - difficulty: int - block_number: int - block_hash: bytes - - -class RegistrationStatisticsLogger: - """Logs statistics for a registration.""" - - console: rich_console.Console - status: Optional[rich_status.Status] - - def __init__( - self, console: rich_console.Console, output_in_place: bool = True - ) -> None: - self.console = console - - if output_in_place: - self.status = self.console.status("Solving") - else: - self.status = None - - def start(self) -> None: - if self.status is not None: - self.status.start() - - def stop(self) -> None: - if self.status is not None: - self.status.stop() - - def get_status_message( - cls, stats: RegistrationStatistics, verbose: bool = False - ) -> str: - message = ( - "Solving\n" - + f"Time Spent (total): [bold white]{timedelta(seconds=stats.time_spent_total)}[/bold white]\n" - + ( - f"Time Spent This Round: {timedelta(seconds=stats.time_spent)}\n" - + f"Time Spent Average: {timedelta(seconds=stats.time_average)}\n" - if verbose - else "" - ) - + f"Registration Difficulty: [bold white]{millify(stats.difficulty)}[/bold white]\n" - + f"Iters (Inst/Perp): [bold white]{get_human_readable(stats.hash_rate, 'H')}/s / " - + f"{get_human_readable(stats.hash_rate_perpetual, 'H')}/s[/bold white]\n" - + f"Block Number: [bold white]{stats.block_number}[/bold white]\n" - + f"Block Hash: [bold white]{stats.block_hash.encode('utf-8')}[/bold white]\n" - ) - return message - - def update(self, stats: RegistrationStatistics, verbose: bool = False) -> None: - if self.status is not None: - self.status.update(self.get_status_message(stats, verbose=verbose)) - else: - self.console.log(self.get_status_message(stats, verbose=verbose)) - - -def solve_for_difficulty_fast( - subtensor: "bittensor.subtensor", - wallet, - output_in_place: bool = True, - num_processes: Optional[int] = None, - update_interval: Optional[int] = None, - n_samples: int = 10, - alpha_: float = 0.80, - log_verbose: bool = False, -) -> Optional[POWSolution]: - """ - Solves the POW for registration using multiprocessing. - Args: - subtensor - Subtensor to connect to for block information and to submit. - wallet: - Wallet to use for registration. - output_in_place: bool - If true, prints the status in place. Otherwise, prints the status on a new line. - num_processes: int - Number of processes to use. - update_interval: int - Number of nonces to solve before updating block information. - n_samples: int - The number of samples of the hash_rate to keep for the EWMA - alpha_: float - The alpha for the EWMA for the hash_rate calculation - log_verbose: bool - If true, prints more verbose logging of the registration metrics. - Note: The hash rate is calculated as an exponentially weighted moving average in order to make the measure more robust. - Note: - - We can also modify the update interval to do smaller blocks of work, - while still updating the block information after a different number of nonces, - to increase the transparency of the process while still keeping the speed. - """ - if num_processes == None: - # get the number of allowed processes for this process - num_processes = min(1, get_cpu_count()) - - if update_interval is None: - update_interval = 50_000 - - limit = int(math.pow(2, 256)) - 1 - - curr_block = multiprocessing.Array("h", 64, lock=True) # byte array - curr_block_num = multiprocessing.Value("i", 0, lock=True) # int - curr_diff = multiprocessing.Array("Q", [0, 0], lock=True) # [high, low] - - # Establish communication queues - ## See the Solver class for more information on the queues. - stopEvent = multiprocessing.Event() - stopEvent.clear() - - solution_queue = multiprocessing.Queue() - finished_queues = [multiprocessing.Queue() for _ in range(num_processes)] - check_block = multiprocessing.Lock() - - # Start consumers - solvers = [ - Solver( - i, - num_processes, - update_interval, - finished_queues[i], - solution_queue, - stopEvent, - curr_block, - curr_block_num, - curr_diff, - check_block, - limit, - ) - for i in range(num_processes) - ] - - # Get first block - block_number = subtensor.get_current_block() - difficulty = subtensor.difficulty - block_hash = subtensor.substrate.get_block_hash(block_number) - while block_hash == None: - block_hash = subtensor.substrate.get_block_hash(block_number) - block_bytes = block_hash.encode("utf-8")[2:] - old_block_number = block_number - # Set to current block - update_curr_block( - curr_diff, - curr_block, - curr_block_num, - block_number, - block_bytes, - difficulty, - check_block, - ) - - # Set new block events for each solver to start at the initial block - for worker in solvers: - worker.newBlockEvent.set() - - for worker in solvers: - worker.start() # start the solver processes - - start_time = time.time() # time that the registration started - time_last = start_time # time that the last work blocks completed - - curr_stats = RegistrationStatistics( - time_spent_total=0.0, - time_average=0.0, - rounds_total=0, - time_spent=0.0, - hash_rate_perpetual=0.0, - hash_rate=0.0, - difficulty=difficulty, - block_number=block_number, - block_hash=block_hash, - ) - - start_time_perpetual = time.time() - - console = bittensor.__console__ - logger = RegistrationStatisticsLogger(console, output_in_place) - logger.start() - - solution = None - - hash_rates = [0] * n_samples # The last n true hash_rates - weights = [alpha_**i for i in range(n_samples)] # weights decay by alpha - - while not subtensor.is_hotkey_registered(hotkey_ss58=wallet.hotkey.ss58_address): - # Wait until a solver finds a solution - try: - solution = solution_queue.get(block=True, timeout=0.25) - if solution is not None: - break - except Empty: - # No solution found, try again - pass - - # check for new block - old_block_number = check_for_newest_block_and_update( - subtensor=subtensor, - old_block_number=old_block_number, - curr_diff=curr_diff, - curr_block=curr_block, - curr_block_num=curr_block_num, - curr_stats=curr_stats, - update_curr_block=update_curr_block, - check_block=check_block, - solvers=solvers, - ) - - num_time = 0 - for finished_queue in finished_queues: - try: - proc_num = finished_queue.get(timeout=0.1) - num_time += 1 - - except Empty: - continue - - time_now = time.time() # get current time - time_since_last = time_now - time_last # get time since last work block(s) - if num_time > 0 and time_since_last > 0.0: - # create EWMA of the hash_rate to make measure more robust - - hash_rate_ = (num_time * update_interval) / time_since_last - hash_rates.append(hash_rate_) - hash_rates.pop(0) # remove the 0th data point - curr_stats.hash_rate = sum( - [hash_rates[i] * weights[i] for i in range(n_samples)] - ) / (sum(weights)) - - # update time last to now - time_last = time_now - - curr_stats.time_average = ( - curr_stats.time_average * curr_stats.rounds_total - + curr_stats.time_spent - ) / (curr_stats.rounds_total + num_time) - curr_stats.rounds_total += num_time - - # Update stats - curr_stats.time_spent = time_since_last - new_time_spent_total = time_now - start_time_perpetual - curr_stats.hash_rate_perpetual = ( - curr_stats.rounds_total * update_interval - ) / new_time_spent_total - curr_stats.time_spent_total = new_time_spent_total - - # Update the logger - logger.update(curr_stats, verbose=log_verbose) - - # exited while, solution contains the nonce or wallet is registered - stopEvent.set() # stop all other processes - logger.stop() - - # terminate and wait for all solvers to exit - terminate_workers_and_wait_for_exit(solvers) - - return solution - - -@backoff.on_exception(backoff.constant, Exception, interval=1, max_tries=3) -def get_block_with_retry(subtensor: "bittensor.subtensor") -> Tuple[int, int, bytes]: - block_number = subtensor.get_current_block() - difficulty = subtensor.difficulty - block_hash = subtensor.substrate.get_block_hash(block_number) - if block_hash is None: - raise Exception( - "Network error. Could not connect to substrate to get block hash" - ) - return block_number, difficulty, block_hash - - -class UsingSpawnStartMethod: - def __init__(self, force: bool = False): - self._old_start_method = None - self._force = force - - def __enter__(self): - self._old_start_method = multiprocessing.get_start_method(allow_none=True) - if self._old_start_method == None: - self._old_start_method = "spawn" # default to spawn - - multiprocessing.set_start_method("spawn", force=self._force) - - def __exit__(self, *args): - # restore the old start method - multiprocessing.set_start_method(self._old_start_method, force=True) - - -def check_for_newest_block_and_update( - subtensor: "bittensor.subtensor", - old_block_number: int, - curr_diff: multiprocessing.Array, - curr_block: multiprocessing.Array, - curr_block_num: multiprocessing.Value, - update_curr_block: Callable, - check_block: "multiprocessing.Lock", - solvers: List[Solver], - curr_stats: RegistrationStatistics, -) -> int: - """ - Checks for a new block and updates the current block information if a new block is found. - Args: - subtensor (:obj:`bittensor.subtensor`, `required`): - The subtensor object to use for getting the current block. - old_block_number (:obj:`int`, `required`): - The old block number to check against. - curr_diff (:obj:`multiprocessing.Array`, `required`): - The current difficulty as a multiprocessing array. - curr_block (:obj:`multiprocessing.Array`, `required`): - Where the current block is stored as a multiprocessing array. - curr_block_num (:obj:`multiprocessing.Value`, `required`): - Where the current block number is stored as a multiprocessing value. - update_curr_block (:obj:`Callable`, `required`): - A function that updates the current block. - check_block (:obj:`multiprocessing.Lock`, `required`): - A mp lock that is used to check for a new block. - solvers (:obj:`List[Solver]`, `required`): - A list of solvers to update the current block for. - curr_stats (:obj:`RegistrationStatistics`, `required`): - The current registration statistics to update. - Returns: - (int) The current block number. - """ - block_number = subtensor.get_current_block() - if block_number != old_block_number: - old_block_number = block_number - # update block information - block_hash = subtensor.substrate.get_block_hash(block_number) - while block_hash == None: - block_hash = subtensor.substrate.get_block_hash(block_number) - block_bytes = block_hash.encode("utf-8")[2:] - difficulty = subtensor.difficulty - - update_curr_block( - curr_diff, - curr_block, - curr_block_num, - block_number, - block_bytes, - difficulty, - check_block, - ) - # Set new block events for each solver - - for worker in solvers: - worker.newBlockEvent.set() - - # update stats - curr_stats.block_number = block_number - curr_stats.block_hash = block_hash - curr_stats.difficulty = difficulty - - return old_block_number - - -def solve_for_difficulty_fast_cuda( - subtensor: "bittensor.subtensor", - wallet: "bittensor.wallet", - output_in_place: bool = True, - update_interval: int = 50_000, - TPB: int = 512, - dev_id: Union[List[int], int] = 0, - n_samples: int = 10, - alpha_: float = 0.80, - log_verbose: bool = False, -) -> Optional[POWSolution]: - """ - Solves the registration fast using CUDA - Args: - subtensor: bittensor.subtensor - The subtensor node to grab blocks - wallet: bittensor.wallet - The wallet to register - output_in_place: bool - If true, prints the output in place, otherwise prints to new lines - update_interval: int - The number of nonces to try before checking for more blocks - TPB: int - The number of threads per block. CUDA param that should match the GPU capability - dev_id: Union[List[int], int] - The CUDA device IDs to execute the registration on, either a single device or a list of devices - n_samples: int - The number of samples of the hash_rate to keep for the EWMA - alpha_: float - The alpha for the EWMA for the hash_rate calculation - log_verbose: bool - If true, prints more verbose logging of the registration metrics. - Note: The hash rate is calculated as an exponentially weighted moving average in order to make the measure more robust. - """ - if isinstance(dev_id, int): - dev_id = [dev_id] - elif dev_id is None: - dev_id = [0] - - if update_interval is None: - update_interval = 50_000 - - if not torch.cuda.is_available(): - raise Exception("CUDA not available") - - limit = int(math.pow(2, 256)) - 1 - - # Set mp start to use spawn so CUDA doesn't complain - with UsingSpawnStartMethod(force=True): - curr_block = multiprocessing.Array("h", 64, lock=True) # byte array - curr_block_num = multiprocessing.Value("i", 0, lock=True) # int - curr_diff = multiprocessing.Array("Q", [0, 0], lock=True) # [high, low] - - ## Create a worker per CUDA device - num_processes = len(dev_id) - - # Establish communication queues - stopEvent = multiprocessing.Event() - stopEvent.clear() - solution_queue = multiprocessing.Queue() - finished_queues = [multiprocessing.Queue() for _ in range(num_processes)] - check_block = multiprocessing.Lock() - - # Start workers - solvers = [ - CUDASolver( - i, - num_processes, - update_interval, - finished_queues[i], - solution_queue, - stopEvent, - curr_block, - curr_block_num, - curr_diff, - check_block, - limit, - dev_id[i], - TPB, - ) - for i in range(num_processes) - ] - - # Get first block - block_number = subtensor.get_current_block() - difficulty = subtensor.difficulty - block_hash = subtensor.substrate.get_block_hash(block_number) - while block_hash == None: - block_hash = subtensor.substrate.get_block_hash(block_number) - block_bytes = block_hash.encode("utf-8")[2:] - old_block_number = block_number - - # Set to current block - update_curr_block( - curr_diff, - curr_block, - curr_block_num, - block_number, - block_bytes, - difficulty, - check_block, - ) - - # Set new block events for each solver to start at the initial block - for worker in solvers: - worker.newBlockEvent.set() - - for worker in solvers: - worker.start() # start the solver processes - - start_time = time.time() # time that the registration started - time_last = start_time # time that the last work blocks completed - - curr_stats = RegistrationStatistics( - time_spent_total=0.0, - time_average=0.0, - rounds_total=0, - time_spent=0.0, - hash_rate_perpetual=0.0, - hash_rate=0.0, # EWMA hash_rate (H/s) - difficulty=difficulty, - block_number=block_number, - block_hash=block_hash, - ) - - start_time_perpetual = time.time() - - console = bittensor.__console__ - logger = RegistrationStatisticsLogger(console, output_in_place) - logger.start() - - hash_rates = [0] * n_samples # The last n true hash_rates - weights = [alpha_**i for i in range(n_samples)] # weights decay by alpha - - solution = None - while not subtensor.is_hotkey_registered( - hotkey_ss58=wallet.hotkey.ss58_address - ): - # Wait until a solver finds a solution - try: - solution = solution_queue.get(block=True, timeout=0.15) - if solution is not None: - break - except Empty: - # No solution found, try again - pass - - # check for new block - old_block_number = check_for_newest_block_and_update( - subtensor=subtensor, - curr_diff=curr_diff, - curr_block=curr_block, - curr_block_num=curr_block_num, - old_block_number=old_block_number, - curr_stats=curr_stats, - update_curr_block=update_curr_block, - check_block=check_block, - solvers=solvers, - ) - - num_time = 0 - # Get times for each solver - for finished_queue in finished_queues: - try: - proc_num = finished_queue.get(timeout=0.1) - num_time += 1 - - except Empty: - continue - - time_now = time.time() # get current time - time_since_last = time_now - time_last # get time since last work block(s) - if num_time > 0 and time_since_last > 0.0: - # create EWMA of the hash_rate to make measure more robust - - hash_rate_ = (num_time * TPB * update_interval) / time_since_last - hash_rates.append(hash_rate_) - hash_rates.pop(0) # remove the 0th data point - curr_stats.hash_rate = sum( - [hash_rates[i] * weights[i] for i in range(n_samples)] - ) / (sum(weights)) - - # update time last to now - time_last = time_now - - curr_stats.time_average = ( - curr_stats.time_average * curr_stats.rounds_total - + curr_stats.time_spent - ) / (curr_stats.rounds_total + num_time) - curr_stats.rounds_total += num_time - - # Update stats - curr_stats.time_spent = time_since_last - new_time_spent_total = time_now - start_time_perpetual - curr_stats.hash_rate_perpetual = ( - curr_stats.rounds_total * (TPB * update_interval) - ) / new_time_spent_total - curr_stats.time_spent_total = new_time_spent_total - - # Update the logger - logger.update(curr_stats, verbose=log_verbose) - - # exited while, found_solution contains the nonce or wallet is registered - - stopEvent.set() # stop all other processes - logger.stop() - - # terminate and wait for all solvers to exit - terminate_workers_and_wait_for_exit(solvers) - - return solution - - -def terminate_workers_and_wait_for_exit(workers: List[multiprocessing.Process]) -> None: - for worker in workers: - worker.terminate() - worker.join() - - -def create_pow( - subtensor, - wallet, - output_in_place: bool = True, - cuda: bool = False, - dev_id: Union[List[int], int] = 0, - tpb: int = 256, - num_processes: int = None, - update_interval: int = None, - log_verbose: bool = False, -) -> Optional[Dict[str, Any]]: - if cuda: - solution: POWSolution = solve_for_difficulty_fast_cuda( - subtensor, - wallet, - output_in_place=output_in_place, - dev_id=dev_id, - TPB=tpb, - update_interval=update_interval, - log_verbose=log_verbose, - ) - else: - solution: POWSolution = solve_for_difficulty_fast( - subtensor, - wallet, - output_in_place=output_in_place, - num_processes=num_processes, - update_interval=update_interval, - log_verbose=log_verbose, - ) - - return ( - None - if solution is None - else { - "nonce": solution.nonce, - "difficulty": solution.difficulty, - "block_number": solution.block_number, - "work": binascii.hexlify(solution.seal), - } - ) diff --git a/tests/integration_tests/test_cli.py b/tests/integration_tests/test_cli.py index ffc973d78c..4dfb4caba9 100644 --- a/tests/integration_tests/test_cli.py +++ b/tests/integration_tests/test_cli.py @@ -1913,31 +1913,6 @@ def test_register(self, _): config = self.config config.command = "subnets" config.subcommand = "register" - config.register.num_processes = 1 - config.register.update_interval = 50_000 - config.no_prompt = True - - mock_wallet = generate_wallet(hotkey=_get_mock_keypair(100, self.id())) - - class MockException(Exception): - pass - - with patch("bittensor.wallet", return_value=mock_wallet) as mock_create_wallet: - with patch( - "bittensor.extrinsics.registration.POWSolution.is_stale", - side_effect=MockException, - ) as mock_is_stale: - with pytest.raises(MockException): - cli = bittensor.cli(config) - cli.run() - mock_create_wallet.assert_called_once() - - self.assertEqual(mock_is_stale.call_count, 1) - - def test_recycle_register(self, _): - config = self.config - config.command = "subnets" - config.subcommand = "recycle_register" config.no_prompt = True mock_wallet = generate_wallet(hotkey=_get_mock_keypair(100, self.id())) @@ -1961,6 +1936,31 @@ def test_recycle_register(self, _): self.assertTrue(registered) + def test_pow_register(self, _): + config = self.config + config.command = "subnets" + config.subcommand = "pow_register" + config.pow_register.num_processes = 1 + config.pow_register.update_interval = 50_000 + config.no_prompt = True + + mock_wallet = generate_wallet(hotkey=_get_mock_keypair(100, self.id())) + + class MockException(Exception): + pass + + with patch("bittensor.wallet", return_value=mock_wallet) as mock_create_wallet: + with patch( + "bittensor.extrinsics.registration.POWSolution.is_stale", + side_effect=MockException, + ) as mock_is_stale: + with pytest.raises(MockException): + cli = bittensor.cli(config) + cli.run() + mock_create_wallet.assert_called_once() + + self.assertEqual(mock_is_stale.call_count, 1) + def test_stake(self, _): amount_to_stake: Balance = Balance.from_tao(0.5) config = self.config diff --git a/tests/integration_tests/test_cli_no_network.py b/tests/integration_tests/test_cli_no_network.py index 5aa8571211..8fc551cde3 100644 --- a/tests/integration_tests/test_cli_no_network.py +++ b/tests/integration_tests/test_cli_no_network.py @@ -338,7 +338,7 @@ def test_btcli_help(self, _, __): def test_register_cuda_use_cuda_flag(self, _, __, patched_sub): base_args = [ "subnets", - "register", + "pow_register", "--wallet.path", "tmp/walletpath", "--wallet.name", @@ -358,24 +358,24 @@ def test_register_cuda_use_cuda_flag(self, _, __, patched_sub): # Should be able to set true without argument args = base_args + [ - "--register.cuda.use_cuda", # should be True without any arugment + "--pow_register.cuda.use_cuda", # should be True without any arugment ] with pytest.raises(MockException): cli = bittensor.cli(args=args) cli.run() - self.assertEqual(cli.config.register.cuda.get("use_cuda"), True) + self.assertEqual(cli.config.pow_register.cuda.get("use_cuda"), True) # Should be able to set to false with no argument args = base_args + [ - "--register.cuda.no_cuda", + "--pow_register.cuda.no_cuda", ] with pytest.raises(MockException): cli = bittensor.cli(args=args) cli.run() - self.assertEqual(cli.config.register.cuda.get("use_cuda"), False) + self.assertEqual(cli.config.pow_register.cuda.get("use_cuda"), False) def return_mock_sub_2(*args, **kwargs): diff --git a/tests/integration_tests/test_subtensor_integration.py b/tests/integration_tests/test_subtensor_integration.py index 8448f9098b..e0c633e3b2 100644 --- a/tests/integration_tests/test_subtensor_integration.py +++ b/tests/integration_tests/test_subtensor_integration.py @@ -586,12 +586,11 @@ class ExitEarly(Exception): msg="only tries to submit once, then exits", ) + def test_defaults_to_finney(self): + sub = bittensor.subtensor() + assert sub.network == "finney" + assert sub.chain_endpoint == bittensor.__finney_entrypoint__ -# # This test was flaking, please check to_defaults before reactiving the test -# def _test_defaults_to_finney(): -# sub = bittensor.subtensor() -# assert sub.network == 'finney' -# assert sub.chain_endpoint == bittensor.__finney_entrypoint__ if __name__ == "__main__": unittest.main() diff --git a/tests/unit_tests/test_dendrite.py b/tests/unit_tests/test_dendrite.py index d61a9faf79..299fcbc424 100644 --- a/tests/unit_tests/test_dendrite.py +++ b/tests/unit_tests/test_dendrite.py @@ -18,11 +18,22 @@ # DEALINGS IN THE SOFTWARE. import pytest +import typing import bittensor from unittest.mock import MagicMock, Mock, patch from tests.helpers import _get_mock_wallet +class SynapseDummy(bittensor.Synapse): + input: int + output: typing.Optional[int] = None + + +def dummy(synapse: SynapseDummy) -> SynapseDummy: + synapse.output = synapse.input + 1 + return synapse + + @pytest.fixture def setup_dendrite(): user_wallet = ( @@ -32,6 +43,15 @@ def setup_dendrite(): return dendrite_obj +@pytest.fixture(scope="session") +def setup_axon(): + axon = bittensor.axon() + axon.attach(forward_fn=dummy) + axon.start() + yield axon + del axon + + def test_init(setup_dendrite): dendrite_obj = setup_dendrite assert isinstance(dendrite_obj, bittensor.dendrite) @@ -50,6 +70,26 @@ def test_repr(setup_dendrite): assert repr(dendrite_obj) == expected_string +def test_close(setup_dendrite, setup_axon): + axon = setup_axon + dendrite_obj = setup_dendrite + # Query the axon to open a session + dendrite_obj.query(axon, SynapseDummy(input=1)) + # Session should be automatically closed after query + assert dendrite_obj._session == None + + +@pytest.mark.asyncio +async def test_aclose(setup_dendrite, setup_axon): + axon = setup_axon + dendrite_obj = setup_dendrite + # Use context manager to open an async session + async with dendrite_obj: + resp = await dendrite_obj([axon], SynapseDummy(input=1), deserialize=False) + # Close should automatically be called on the session after context manager scope + assert dendrite_obj._session == None + + class AsyncMock(Mock): def __call__(self, *args, **kwargs): sup = super(AsyncMock, self) diff --git a/tests/unit_tests/test_synapse.py b/tests/unit_tests/test_synapse.py index c2900511e1..70dd88db76 100644 --- a/tests/unit_tests/test_synapse.py +++ b/tests/unit_tests/test_synapse.py @@ -25,7 +25,6 @@ def test_parse_headers_to_inputs(): class Test(bittensor.Synapse): key1: typing.List[int] - key2: bittensor.Tensor # Define a mock headers dictionary to use for testing headers = { @@ -34,7 +33,6 @@ class Test(bittensor.Synapse): "bt_header_input_obj_key1": base64.b64encode( json.dumps([1, 2, 3, 4]).encode("utf-8") ).decode("utf-8"), - "bt_header_tensor_key2": "[3]-torch.float32", "timeout": "12", "name": "Test", "header_size": "111", @@ -51,7 +49,6 @@ class Test(bittensor.Synapse): "axon": {"nonce": "111"}, "dendrite": {"ip": "12.1.1.2"}, "key1": [1, 2, 3, 4], - "key2": bittensor.Tensor(dtype="torch.float32", shape=[3]), "timeout": "12", "name": "Test", "header_size": "111", @@ -63,7 +60,6 @@ class Test(bittensor.Synapse): def test_from_headers(): class Test(bittensor.Synapse): key1: typing.List[int] - key2: bittensor.Tensor # Define a mock headers dictionary to use for testing headers = { @@ -72,7 +68,6 @@ class Test(bittensor.Synapse): "bt_header_input_obj_key1": base64.b64encode( json.dumps([1, 2, 3, 4]).encode("utf-8") ).decode("utf-8"), - "bt_header_tensor_key2": "[3]-torch.float32", "timeout": "12", "name": "Test", "header_size": "111", @@ -91,8 +86,6 @@ class Test(bittensor.Synapse): assert synapse.axon.nonce == 111 assert synapse.dendrite.ip == "12.1.1.2" assert synapse.key1 == [1, 2, 3, 4] - assert synapse.key2.shape == [3] - assert synapse.key2.dtype == "torch.float32" assert synapse.timeout == 12 assert synapse.name == "Test" assert synapse.header_size == 111 @@ -139,7 +132,6 @@ class Test(bittensor.Synapse): c: typing.Optional[int] # Not carried through headers d: typing.Optional[typing.List[int]] # Not carried through headers e: typing.List[int] # Carried through headers - f: bittensor.Tensor # Carried through headers, but not buffer. # Create an instance of the custom Synapse subclass synapse = Test( @@ -147,7 +139,6 @@ class Test(bittensor.Synapse): c=3, d=[1, 2, 3, 4], e=[1, 2, 3, 4], - f=bittensor.Tensor.serialize(torch.randn(10)), ) # Ensure the instance created is of type Test and has the expected properties @@ -158,7 +149,6 @@ class Test(bittensor.Synapse): assert synapse.c == 3 assert synapse.d == [1, 2, 3, 4] assert synapse.e == [1, 2, 3, 4] - assert synapse.f.shape == [10] # Convert the Test instance to a headers dictionary headers = synapse.to_headers() @@ -174,71 +164,6 @@ class Test(bittensor.Synapse): assert next_synapse.c == None assert next_synapse.d == None assert next_synapse.e == [] # Empty list is default for list types - assert next_synapse.f.shape == [10] # Shape is passed through - assert next_synapse.f.dtype == "torch.float32" # Type is passed through - assert next_synapse.f.buffer == None # Buffer is not passed through - - -def test_list_tensors(): - class Test(bittensor.Synapse): - a: typing.List[bittensor.Tensor] - - synapse = Test( - a=[bittensor.Tensor.serialize(torch.randn(10))], - ) - headers = synapse.to_headers() - assert "bt_header_list_tensor_a" in headers - assert headers["bt_header_list_tensor_a"] == "['[10]-torch.float32']" - next_synapse = synapse.from_headers(synapse.to_headers()) - assert next_synapse.a[0].dtype == "torch.float32" - assert next_synapse.a[0].shape == [10] - - class Test(bittensor.Synapse): - a: typing.List[bittensor.Tensor] - - synapse = Test( - a=[ - bittensor.Tensor.serialize(torch.randn(10)), - bittensor.Tensor.serialize(torch.randn(11)), - bittensor.Tensor.serialize(torch.randn(12)), - ], - ) - headers = synapse.to_headers() - assert "bt_header_list_tensor_a" in headers - assert ( - headers["bt_header_list_tensor_a"] - == "['[10]-torch.float32', '[11]-torch.float32', '[12]-torch.float32']" - ) - next_synapse = synapse.from_headers(synapse.to_headers()) - assert next_synapse.a[0].dtype == "torch.float32" - assert next_synapse.a[0].shape == [10] - assert next_synapse.a[1].dtype == "torch.float32" - assert next_synapse.a[1].shape == [11] - assert next_synapse.a[2].dtype == "torch.float32" - assert next_synapse.a[2].shape == [12] - - -def test_dict_tensors(): - class Test(bittensor.Synapse): - a: typing.Dict[str, bittensor.Tensor] - - synapse = Test( - a={ - "cat": bittensor.tensor(torch.randn(10)), - "dog": bittensor.tensor(torch.randn(11)), - }, - ) - headers = synapse.to_headers() - assert "bt_header_dict_tensor_a" in headers - assert ( - headers["bt_header_dict_tensor_a"] - == "['cat-[10]-torch.float32', 'dog-[11]-torch.float32']" - ) - next_synapse = synapse.from_headers(synapse.to_headers()) - assert next_synapse.a["cat"].dtype == "torch.float32" - assert next_synapse.a["cat"].shape == [10] - assert next_synapse.a["dog"].dtype == "torch.float32" - assert next_synapse.a["dog"].shape == [11] def test_body_hash_override(): @@ -263,3 +188,39 @@ def test_required_fields_override(): match='"required_hash_fields" has allow_mutation set to False and cannot be assigned', ): synapse_instance.required_hash_fields = [] + + +def test_default_instance_fields_dict_consistency(): + synapse_instance = bittensor.Synapse() + assert synapse_instance.dict() == { + "name": "Synapse", + "timeout": 12.0, + "total_size": 0, + "header_size": 0, + "dendrite": { + "status_code": None, + "status_message": None, + "process_time": None, + "ip": None, + "port": None, + "version": None, + "nonce": None, + "uuid": None, + "hotkey": None, + "signature": None, + }, + "axon": { + "status_code": None, + "status_message": None, + "process_time": None, + "ip": None, + "port": None, + "version": None, + "nonce": None, + "uuid": None, + "hotkey": None, + "signature": None, + }, + "computed_body_hash": "", + "required_hash_fields": [], + } diff --git a/tests/unit_tests/utils/test_utils.py b/tests/unit_tests/utils/test_utils.py index 307891a13c..cad54216c4 100644 --- a/tests/unit_tests/utils/test_utils.py +++ b/tests/unit_tests/utils/test_utils.py @@ -762,222 +762,5 @@ def test_get_explorer_url_for_network_by_network_and_block_hash( ) -class TestWalletReregister(unittest.TestCase): - _mock_subtensor: MockSubtensor - - def setUp(self): - self.subtensor = MockSubtensor() # own instance per test - - @classmethod - def setUpClass(cls) -> None: - # Keeps the same mock network for all tests. This stops the network from being re-setup for each test. - cls._mock_subtensor = MockSubtensor() - - cls._do_setup_subnet() - - @classmethod - def _do_setup_subnet(cls): - # reset the mock subtensor - cls._mock_subtensor.reset() - # Setup the mock subnet 3 - cls._mock_subtensor.create_subnet(netuid=3) - - def test_wallet_reregister_reregister_false(self): - mock_wallet = _generate_wallet(hotkey=_get_mock_keypair(100, self.id())) - - class MockException(Exception): - pass - - # Determine the correct string for patch based on Python version - if sys.version_info >= (3, 11): - patch_string = "bittensor.subtensor.subtensor.register" - else: - patch_string = "bittensor.subtensor.register" - - with patch(patch_string, side_effect=MockException) as mock_register: - with pytest.raises(SystemExit): # should exit because it's not registered - bittensor.utils.reregister( - wallet=mock_wallet, - subtensor=self._mock_subtensor, - netuid=3, - reregister=False, - ) - - mock_register.assert_not_called() # should not call register - - def test_wallet_reregister_reregister_false_and_registered_already(self): - mock_wallet = _generate_wallet(hotkey=_get_mock_keypair(100, self.id())) - - class MockException(Exception): - pass - - self._mock_subtensor.force_register_neuron( - netuid=3, - hotkey=mock_wallet.hotkey.ss58_address, - coldkey=mock_wallet.coldkeypub.ss58_address, - ) - self.assertTrue( - self._mock_subtensor.is_hotkey_registered_on_subnet( - netuid=3, - hotkey_ss58=mock_wallet.hotkey.ss58_address, - ) - ) - - # Determine the correct string for patch based on Python version - if sys.version_info >= (3, 11): - patch_string = "bittensor.subtensor.subtensor.register" - else: - patch_string = "bittensor.subtensor.register" - - with patch(patch_string, side_effect=MockException) as mock_register: - bittensor.utils.reregister( - wallet=mock_wallet, - subtensor=self._mock_subtensor, - netuid=3, - reregister=False, - ) # Should not exit because it's registered - - mock_register.assert_not_called() # should not call register - - def test_wallet_reregister_reregister_true_and_registered_already(self): - mock_wallet = _generate_wallet(hotkey=_get_mock_keypair(100, self.id())) - - class MockException(Exception): - pass - - self._mock_subtensor.force_register_neuron( - netuid=3, - hotkey=mock_wallet.hotkey.ss58_address, - coldkey=mock_wallet.coldkeypub.ss58_address, - ) - self.assertTrue( - self._mock_subtensor.is_hotkey_registered_on_subnet( - netuid=3, - hotkey_ss58=mock_wallet.hotkey.ss58_address, - ) - ) - - # Determine the correct string for patch based on Python version - if sys.version_info >= (3, 11): - patch_string = "bittensor.subtensor.subtensor.register" - else: - patch_string = "bittensor.subtensor.register" - - with patch(patch_string, side_effect=MockException) as mock_register: - bittensor.utils.reregister( - wallet=mock_wallet, - subtensor=self._mock_subtensor, - netuid=3, - reregister=True, - ) # Should not exit because it's registered - - mock_register.assert_not_called() # should not call register - - def test_wallet_reregister_no_params(self): - mock_wallet = _generate_wallet(hotkey=_get_mock_keypair(100, self.id())) - - class MockException(Exception): - pass - - # Determine the correct string for patch based on Python version - if sys.version_info >= (3, 11): - patch_string = "bittensor.subtensor.subtensor.register" - else: - patch_string = "bittensor.subtensor.register" - - with patch(patch_string, side_effect=MockException) as mock_register: - # Should be able to set without argument - with pytest.raises(MockException): - bittensor.utils.reregister( - wallet=mock_wallet, - subtensor=self._mock_subtensor, - netuid=3, - reregister=True, - # didn't pass any register params - ) - - mock_register.assert_called_once() # should call register once - - def test_wallet_reregister_use_cuda_flag_true(self): - mock_wallet = _generate_wallet(hotkey=_get_mock_keypair(100, self.id())) - - class MockException(Exception): - pass - - with patch("torch.cuda.is_available", return_value=True) as mock_cuda_available: - with patch( - "bittensor.extrinsics.registration.create_pow", - side_effect=MockException, - ) as mock_create_pow: - # Should be able to set without argument - with pytest.raises(MockException): - bittensor.utils.reregister( - wallet=mock_wallet, - subtensor=self._mock_subtensor, - netuid=3, - dev_id=0, - cuda=True, - reregister=True, - ) - - call_args = mock_create_pow.call_args - _, kwargs = call_args - - mock_create_pow.assert_called_once() - self.assertIn("cuda", kwargs) - self.assertEqual(kwargs["cuda"], True) - - def test_wallet_reregister_use_cuda_flag_false(self): - mock_wallet = _generate_wallet(hotkey=_get_mock_keypair(100, self.id())) - - class MockException(Exception): - pass - - with patch( - "bittensor.extrinsics.registration.create_pow", side_effect=MockException - ) as mock_create_pow: - # Should be able to set without argument - with pytest.raises(MockException): - bittensor.utils.reregister( - wallet=mock_wallet, - subtensor=self._mock_subtensor, - netuid=3, - dev_id=0, - cuda=False, - reregister=True, - ) - - call_args = mock_create_pow.call_args - _, kwargs = call_args - - mock_create_pow.assert_called_once() - self.assertEqual(kwargs["cuda"], False) - - def test_wallet_reregister_cuda_arg_not_specified_should_be_false(self): - mock_wallet = _generate_wallet(hotkey=_get_mock_keypair(100, self.id())) - - class MockException(Exception): - pass - - with patch( - "bittensor.extrinsics.registration.create_pow", side_effect=MockException - ) as mock_create_pow: - # Should be able to set without argument - with pytest.raises(MockException): - bittensor.utils.reregister( - wallet=mock_wallet, - subtensor=self._mock_subtensor, - netuid=3, - dev_id=0, - reregister=True, - ) - - call_args = mock_create_pow.call_args - _, kwargs = call_args - - mock_create_pow.assert_called_once() - self.assertEqual(kwargs["cuda"], False) # should be False by default - - if __name__ == "__main__": unittest.main() From 2f879971255a879027f732b19b36f6532dda32e5 Mon Sep 17 00:00:00 2001 From: philanthrope Date: Mon, 20 Nov 2023 15:27:27 -0500 Subject: [PATCH 2/8] Release/6.3.0 (#1582) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * (un)Staking multiple avoid tx limit (#1244) * add tx rate limit * wait for tx limit if not done multi stake/unstake * dont "decrypt" hotkey * additional logging for prometheus * additional logging for prometheus (#1246) * Dataset fix (#1249) * fix * added try except * Grab delegates details from GitHub (#1245) * add url to init * add dataclass and util functions * use in cli * remove delegates json --------- Co-authored-by: joeylegere * Add raw spec for local test and new bins (#1243) * add spec and new bins * fix config netuid * use dot get * check if config netuid is list * add start to mockstatus * add attr to mock neuron * add info to mock from neurons * change ordering of neuron dict to namespace * remove test for wandb for axon * use regex for looser match * fix blacklist metagraph mock * use real mock netuid * use mock network and netuid in constructor * fix patch * patch delegate check * use mock network and netuid * remove check for wallet hotkey * fix tests for subtensor init * dont set netuid for overview test * typo in docstring * add mock status stop * add low mock tx limit * oops typo * use dot get * add wait for final and incl args * use args during setup * update bins and use 100ms blocktime * pass block arg * remove bittensor.logging and a old test * use random port * backward fix * fix block time to 1s * compile no symb on linux * compile no symb mac * remove useless init on var * use dot get for new flags * update test durations * update test durations * use dot get for config * output error msg * mock to_default * remove to defaults in help * reduce neruons, remove flaky test * deactivate test * mvoe key pair tests out of the subtensor interface --------- Co-authored-by: Eugene * Fix list_delegates on non-archive nodes (#1232) * Change how pull of archival data is handled * fix for list_delegates too * . * use empty dict * fix spacing * specify exception * log out * add space in log message * use warning instead * Blacklist fixes + depreciation of old signatures (#1240) * fixes blacklist error message + remove * remove checks for parse signature * remove sign v1 tests * fix for the syanpse checks * fix tests and remove duplicate sign * Improve development workflow documentation * [BIT-636] Change u16 weight normalization to max-upscaling (#1241) * Change u16 weight normalization to max-upscaling Use full u16 bitwidth so that max_weight=U16_MAX, then rely on subtensor epoch to properly normalize weights in I32F32. This means that weights submission extrinsic to subtensor does not have to be pre-normalized. * Skip zero sum in weight conversion * Round u16 weights * remove duplicate command #1228 (#1231) * remove duplicate command #1228 * Extract create_parser for cli testing * mark as private * use in tests and test for duplicates * fix test using mock prompt answers * test_forward_priority_2nd_request_timeout fix (#1276) fix * Remove btcli query and btcli set_weights (#1144) . * Merge releases 4.0.0 and 4.0.1 back to staging (#1306) * bump version * Fix permissions for release github script (#1224) Co-authored-by: Cameron Fairchild * should be 4.1.0 * Revert "should be 4.1.0" This reverts commit 3db08ea24f4fc4775bd46858e6c77cfa165d85ed. * Staging into Release branch (#1275) * (un)Staking multiple avoid tx limit (#1244) * add tx rate limit * wait for tx limit if not done multi stake/unstake * dont "decrypt" hotkey * additional logging for prometheus (#1246) * Dataset fix (#1249) * fix * added try except * Grab delegates details from GitHub (#1245) * add url to init * add dataclass and util functions * use in cli * remove delegates json --------- Co-authored-by: joeylegere * Add raw spec for local test and new bins (#1243) * add spec and new bins * fix config netuid * use dot get * check if config netuid is list * add start to mockstatus * add attr to mock neuron * add info to mock from neurons * change ordering of neuron dict to namespace * remove test for wandb for axon * use regex for looser match * fix blacklist metagraph mock * use real mock netuid * use mock network and netuid in constructor * fix patch * patch delegate check * use mock network and netuid * remove check for wallet hotkey * fix tests for subtensor init * dont set netuid for overview test * typo in docstring * add mock status stop * add low mock tx limit * oops typo * use dot get * add wait for final and incl args * use args during setup * update bins and use 100ms blocktime * pass block arg * remove bittensor.logging and a old test * use random port * backward fix * fix block time to 1s * compile no symb on linux * compile no symb mac * remove useless init on var * use dot get for new flags * update test durations * update test durations * use dot get for config * output error msg * mock to_default * remove to defaults in help * reduce neruons, remove flaky test * deactivate test * mvoe key pair tests out of the subtensor interface --------- Co-authored-by: Eugene * Fix list_delegates on non-archive nodes (#1232) * Change how pull of archival data is handled * fix for list_delegates too * . * use empty dict * fix spacing * specify exception * log out * add space in log message * use warning instead * Blacklist fixes + depreciation of old signatures (#1240) * fixes blacklist error message + remove * remove checks for parse signature * remove sign v1 tests * fix for the syanpse checks * fix tests and remove duplicate sign * [BIT-636] Change u16 weight normalization to max-upscaling (#1241) * Change u16 weight normalization to max-upscaling Use full u16 bitwidth so that max_weight=U16_MAX, then rely on subtensor epoch to properly normalize weights in I32F32. This means that weights submission extrinsic to subtensor does not have to be pre-normalized. * Skip zero sum in weight conversion * Round u16 weights * remove duplicate command #1228 (#1231) * remove duplicate command #1228 * Extract create_parser for cli testing * mark as private * use in tests and test for duplicates * fix test using mock prompt answers * test_forward_priority_2nd_request_timeout fix (#1276) fix * Remove btcli query and btcli set_weights (#1144) . --------- Co-authored-by: Eugene-hu <85906264+Eugene-hu@users.noreply.github.com> Co-authored-by: isabella618033 <49876827+isabella618033@users.noreply.github.com> Co-authored-by: joeylegere Co-authored-by: Eugene Co-authored-by: opentaco <93473497+opentaco@users.noreply.github.com> * Remove codecov (#1282) * Use alt new preseal (#1269) * use new preseal for reg * bump cubit req * fix arg order issue * cubit req back * use alt impl * fix typehint * use 512 * modify tests for new format * refactor functions to use helpers and remove useless * refactor functions * add test for CPU solver * modify tests for privitized module and methods * private register cuda * move formatting funcs * use powsolution * privitize most methods * fix test * fix perms * remove test script * remove debug * fix call * fix seal * fix combined hash * move to method * fix test using real example * update mock bins * use new builder * fix block update tests * fix some patching in tests * mock live display for some tests * fix chain mock * update linux bin * add mock network flag * set max diff at 0 for mock netuid 1 * set min diff too * add try catch for setup * add some logging during tests * don't submit on cli register * update test durations * fix test to use mock keypair * return mock wallet * should use subtensor instance during rereg * update node subtensor bins * use fixtures and multiple subtensor instances * changelog update * skip CLI tests (#1284) * skip tests * dont test mock functions * update test durations * [Release] v4.0.0 (#1271) * bump version * Fix permissions for release github script (#1224) Co-authored-by: Cameron Fairchild * should be 4.1.0 * Revert "should be 4.1.0" This reverts commit 3db08ea24f4fc4775bd46858e6c77cfa165d85ed. * Staging into Release branch (#1275) * (un)Staking multiple avoid tx limit (#1244) * add tx rate limit * wait for tx limit if not done multi stake/unstake * dont "decrypt" hotkey * additional logging for prometheus (#1246) * Dataset fix (#1249) * fix * added try except * Grab delegates details from GitHub (#1245) * add url to init * add dataclass and util functions * use in cli * remove delegates json --------- Co-authored-by: joeylegere * Add raw spec for local test and new bins (#1243) * add spec and new bins * fix config netuid * use dot get * check if config netuid is list * add start to mockstatus * add attr to mock neuron * add info to mock from neurons * change ordering of neuron dict to namespace * remove test for wandb for axon * use regex for looser match * fix blacklist metagraph mock * use real mock netuid * use mock network and netuid in constructor * fix patch * patch delegate check * use mock network and netuid * remove check for wallet hotkey * fix tests for subtensor init * dont set netuid for overview test * typo in docstring * add mock status stop * add low mock tx limit * oops typo * use dot get * add wait for final and incl args * use args during setup * update bins and use 100ms blocktime * pass block arg * remove bittensor.logging and a old test * use random port * backward fix * fix block time to 1s * compile no symb on linux * compile no symb mac * remove useless init on var * use dot get for new flags * update test durations * update test durations * use dot get for config * output error msg * mock to_default * remove to defaults in help * reduce neruons, remove flaky test * deactivate test * mvoe key pair tests out of the subtensor interface --------- Co-authored-by: Eugene * Fix list_delegates on non-archive nodes (#1232) * Change how pull of archival data is handled * fix for list_delegates too * . * use empty dict * fix spacing * specify exception * log out * add space in log message * use warning instead * Blacklist fixes + depreciation of old signatures (#1240) * fixes blacklist error message + remove * remove checks for parse signature * remove sign v1 tests * fix for the syanpse checks * fix tests and remove duplicate sign * [BIT-636] Change u16 weight normalization to max-upscaling (#1241) * Change u16 weight normalization to max-upscaling Use full u16 bitwidth so that max_weight=U16_MAX, then rely on subtensor epoch to properly normalize weights in I32F32. This means that weights submission extrinsic to subtensor does not have to be pre-normalized. * Skip zero sum in weight conversion * Round u16 weights * remove duplicate command #1228 (#1231) * remove duplicate command #1228 * Extract create_parser for cli testing * mark as private * use in tests and test for duplicates * fix test using mock prompt answers * test_forward_priority_2nd_request_timeout fix (#1276) fix * Remove btcli query and btcli set_weights (#1144) . --------- Co-authored-by: Eugene-hu <85906264+Eugene-hu@users.noreply.github.com> Co-authored-by: isabella618033 <49876827+isabella618033@users.noreply.github.com> Co-authored-by: joeylegere Co-authored-by: Eugene Co-authored-by: opentaco <93473497+opentaco@users.noreply.github.com> * Remove codecov (#1282) * Use alt new preseal (#1269) * use new preseal for reg * bump cubit req * fix arg order issue * cubit req back * use alt impl * fix typehint * use 512 * modify tests for new format * refactor functions to use helpers and remove useless * refactor functions * add test for CPU solver * modify tests for privitized module and methods * private register cuda * move formatting funcs * use powsolution * privitize most methods * fix test * fix perms * remove test script * remove debug * fix call * fix seal * fix combined hash * move to method * fix test using real example * update mock bins * use new builder * fix block update tests * fix some patching in tests * mock live display for some tests * fix chain mock * update linux bin * add mock network flag * set max diff at 0 for mock netuid 1 * set min diff too * add try catch for setup * add some logging during tests * don't submit on cli register * update test durations * fix test to use mock keypair * return mock wallet * should use subtensor instance during rereg * update node subtensor bins * use fixtures and multiple subtensor instances * changelog update * skip CLI tests (#1284) * skip tests * dont test mock functions * update test durations --------- Co-authored-by: Eduardo García Co-authored-by: Eugene-hu <85906264+Eugene-hu@users.noreply.github.com> Co-authored-by: isabella618033 <49876827+isabella618033@users.noreply.github.com> Co-authored-by: joeylegere Co-authored-by: Eugene Co-authored-by: opentaco <93473497+opentaco@users.noreply.github.com> * fix my delegates * fix perms on changelog script * update version * fix changelog script * Catch bad endpoint protocol (#1296) * catch protocol not good * add protocol 4 * catch assertion and return bool * catch assertion errors * changelog --------- Co-authored-by: Eduardo García Co-authored-by: Eugene-hu <85906264+Eugene-hu@users.noreply.github.com> Co-authored-by: isabella618033 <49876827+isabella618033@users.noreply.github.com> Co-authored-by: joeylegere Co-authored-by: Eugene Co-authored-by: opentaco <93473497+opentaco@users.noreply.github.com> * Update DEVELOPMENT_WORKFLOW.md * final fixes * staging updates and fixes (#1540) * fix cli test * fix double-counted hotkeys per subnet and non-iterable stake obj (#1539) * fix double-counted hotkeys per subnet and non-iterable stake obj * run black * Add root get_weights command to btcli (#1536) * Add root get_weights command to btcli * Use a percentage for viewing weights instead of float values * run black, fix cli test --------- Co-authored-by: ifrit98 * Fix typo (#1543) Co-authored-by: philanthrope * remove duplicated debug message in dendrite (#1544) * Cli fix (#1541) don't break on mismatched coldkey from local wallet <> chain * update faucet helpstr (#1542) * Added mechanism to sum all delegated tao (#1547) Co-authored-by: Ala Shaabana * Dict hash fix (#1548) * use dict() when hasing body objects to not convert arbitrary objects to str * recreate synapse in axon dependency to avoid duplicating code * black * Merge master (#1552) Release/6.1.0 (#1550) * Fix typo (#1543) * remove duplicated debug message in dendrite (#1544) * Cli fix (#1541) don't break on mismatched coldkey from local wallet <> chain * update faucet helpstr (#1542) * Added mechanism to sum all delegated tao (#1547) * Dict hash fix (#1548) * use dict() when hasing body objects to not convert arbitrary objects to str * recreate synapse in axon dependency to avoid duplicating code * black * update version * update changelog --------- Co-authored-by: Cameron Fairchild Co-authored-by: Eugene Co-authored-by: isabella618033 <49876827+isabella618033@users.noreply.github.com> Co-authored-by: Cameron Fairchild Co-authored-by: opentaco <93473497+opentaco@users.noreply.github.com> Co-authored-by: Eduardo García Co-authored-by: Ayden Brewer Co-authored-by: Steffen Cruz Co-authored-by: Ala Shaabana * Streaming fix (#1551) * yield chunks immediately in process_streaming_responss so clients can access * break streaming into separate call funcs * update docstrings, types * black * duplicate debug msg * add warning for mismatched streaming arg + subclass * Fix typos (#1553) * Release/6.1.0 (#1550) Co-authored-by: ifrit98 * Fix typo (#1543) Co-authored-by: philanthrope * remove duplicated debug message in dendrite (#1544) * Cli fix (#1541) don't break on mismatched coldkey from local wallet <> chain * update faucet helpstr (#1542) * Added mechanism to sum all delegated tao (#1547) Co-authored-by: Ala Shaabana * Dict hash fix (#1548) * use dict() when hasing body objects to not convert arbitrary objects to str * recreate synapse in axon dependency to avoid duplicating code * black * update version * update changelog --------- Co-authored-by: Cameron Fairchild Co-authored-by: Eugene Co-authored-by: isabella618033 <49876827+isabella618033@users.noreply.github.com> Co-authored-by: Cameron Fairchild Co-authored-by: opentaco <93473497+opentaco@users.noreply.github.com> Co-authored-by: Eduardo García Co-authored-by: Ayden Brewer Co-authored-by: Steffen Cruz Co-authored-by: Ala Shaabana * fix typos --------- Co-authored-by: philanthrope Co-authored-by: Cameron Fairchild Co-authored-by: Eugene Co-authored-by: isabella618033 <49876827+isabella618033@users.noreply.github.com> Co-authored-by: Cameron Fairchild Co-authored-by: opentaco <93473497+opentaco@users.noreply.github.com> Co-authored-by: Eduardo García Co-authored-by: Ayden Brewer Co-authored-by: Steffen Cruz Co-authored-by: Ala Shaabana * Normalize weights in r get weights table (#1556) * Normalize weights in r get weights table * use numpy and make table look nicer * apply black * add max 1 to prevent div0 --------- Co-authored-by: Cameron Fairchild * Dendrite & Synapse updates and fixes (#1555) * remove tensor header objects to not overflow headers * update tests to reflect removal of tensor headers * ensure consistent method for calling in synapse methods * fix dendrite UnClosesedSession error * fix docstring and add tests for close/aclose * rm extra delete * run black * add default synapse dict() consistency test * call del on session after close_session(), fix tests * update dendrite dummy clsname * add dendrite.query finally block * fix test * rm root flag in metagraph (#1558) * rm root flag in metagraph * run black * typo * Max Faucet Runs == 3 (#1560) add exceptions * replace unknown wallet params (chain mismatch) with key values (#1559) * replace unknown wallet params (chain mismatch) with key values * run black * rm debug prints * Remove PoW registration cli and associated extrinsic (#1557) * Remove PoW registration cli and associated extrinsic * run black * no mo pow, no mo pow tests * remove now deprecated PoW reregister routine * remove deprecated tests * more test fixes * remove _do_pow call * return PoW but still kill reregister (unused) * run black * return test to networks choices in btcli, fix chain_endpoint selection * fix pow args * Add btcli wallet balance (#1564) * begin adding balance command * skip validator that hasn't set weights yet on root * finish balances command * formatter * issue warning for nonexistent coldkeypub.txt rather than break * Dendrite fixes (#1561) * make sure a session exists before trying to close it * don't double iterate over async generator, simply return it * black * less DRY violations * fix typehints * Master into staging (#1570) Release/6.2.0 (#1567) * (un)Staking multiple avoid tx limit (#1244) * add tx rate limit * wait for tx limit if not done multi stake/unstake * dont "decrypt" hotkey * additional logging for prometheus * additional logging for prometheus (#1246) * Dataset fix (#1249) * fix * added try except * Grab delegates details from GitHub (#1245) * add url to init * add dataclass and util functions * use in cli * remove delegates json --------- * Add raw spec for local test and new bins (#1243) * add spec and new bins * fix config netuid * use dot get * check if config netuid is list * add start to mockstatus * add attr to mock neuron * add info to mock from neurons * change ordering of neuron dict to namespace * remove test for wandb for axon * use regex for looser match * fix blacklist metagraph mock * use real mock netuid * use mock network and netuid in constructor * fix patch * patch delegate check * use mock network and netuid * remove check for wallet hotkey * fix tests for subtensor init * dont set netuid for overview test * typo in docstring * add mock status stop * add low mock tx limit * oops typo * use dot get * add wait for final and incl args * use args during setup * update bins and use 100ms blocktime * pass block arg * remove bittensor.logging and a old test * use random port * backward fix * fix block time to 1s * compile no symb on linux * compile no symb mac * remove useless init on var * use dot get for new flags * update test durations * update test durations * use dot get for config * output error msg * mock to_default * remove to defaults in help * reduce neruons, remove flaky test * deactivate test * mvoe key pair tests out of the subtensor interface --------- * Fix list_delegates on non-archive nodes (#1232) * Change how pull of archival data is handled * fix for list_delegates too * . * use empty dict * fix spacing * specify exception * log out * add space in log message * use warning instead * Blacklist fixes + depreciation of old signatures (#1240) * fixes blacklist error message + remove * remove checks for parse signature * remove sign v1 tests * fix for the syanpse checks * fix tests and remove duplicate sign * Improve development workflow documentation * [BIT-636] Change u16 weight normalization to max-upscaling (#1241) * Change u16 weight normalization to max-upscaling Use full u16 bitwidth so that max_weight=U16_MAX, then rely on subtensor epoch to properly normalize weights in I32F32. This means that weights submission extrinsic to subtensor does not have to be pre-normalized. * Skip zero sum in weight conversion * Round u16 weights * remove duplicate command #1228 (#1231) * remove duplicate command #1228 * Extract create_parser for cli testing * mark as private * use in tests and test for duplicates * fix test using mock prompt answers * test_forward_priority_2nd_request_timeout fix (#1276) fix * Remove btcli query and btcli set_weights (#1144) . * Merge releases 4.0.0 and 4.0.1 back to staging (#1306) * bump version * Fix permissions for release github script (#1224) * should be 4.1.0 * Revert "should be 4.1.0" This reverts commit 3db08ea24f4fc4775bd46858e6c77cfa165d85ed. * Staging into Release branch (#1275) * (un)Staking multiple avoid tx limit (#1244) * add tx rate limit * wait for tx limit if not done multi stake/unstake * dont "decrypt" hotkey * additional logging for prometheus (#1246) * Dataset fix (#1249) * fix * added try except * Grab delegates details from GitHub (#1245) * add url to init * add dataclass and util functions * use in cli * remove delegates json --------- * Add raw spec for local test and new bins (#1243) * add spec and new bins * fix config netuid * use dot get * check if config netuid is list * add start to mockstatus * add attr to mock neuron * add info to mock from neurons * change ordering of neuron dict to namespace * remove test for wandb for axon * use regex for looser match * fix blacklist metagraph mock * use real mock netuid * use mock network and netuid in constructor * fix patch * patch delegate check * use mock network and netuid * remove check for wallet hotkey * fix tests for subtensor init * dont set netuid for overview test * typo in docstring * add mock status stop * add low mock tx limit * oops typo * use dot get * add wait for final and incl args * use args during setup * update bins and use 100ms blocktime * pass block arg * remove bittensor.logging and a old test * use random port * backward fix * fix block time to 1s * compile no symb on linux * compile no symb mac * remove useless init on var * use dot get for new flags * update test durations * update test durations * use dot get for config * output error msg * mock to_default * remove to defaults in help * reduce neruons, remove flaky test * deactivate test * mvoe key pair tests out of the subtensor interface --------- * Fix list_delegates on non-archive nodes (#1232) * Change how pull of archival data is handled * fix for list_delegates too * . * use empty dict * fix spacing * specify exception * log out * add space in log message * use warning instead * Blacklist fixes + depreciation of old signatures (#1240) * fixes blacklist error message + remove * remove checks for parse signature * remove sign v1 tests * fix for the syanpse checks * fix tests and remove duplicate sign * [BIT-636] Change u16 weight normalization to max-upscaling (#1241) * Change u16 weight normalization to max-upscaling Use full u16 bitwidth so that max_weight=U16_MAX, then rely on subtensor epoch to properly normalize weights in I32F32. This means that weights submission extrinsic to subtensor does not have to be pre-normalized. * Skip zero sum in weight conversion * Round u16 weights * remove duplicate command #1228 (#1231) * remove duplicate command #1228 * Extract create_parser for cli testing * mark as private * use in tests and test for duplicates * fix test using mock prompt answers * test_forward_priority_2nd_request_timeout fix (#1276) fix * Remove btcli query and btcli set_weights (#1144) . --------- * Remove codecov (#1282) * Use alt new preseal (#1269) * use new preseal for reg * bump cubit req * fix arg order issue * cubit req back * use alt impl * fix typehint * use 512 * modify tests for new format * refactor functions to use helpers and remove useless * refactor functions * add test for CPU solver * modify tests for privitized module and methods * private register cuda * move formatting funcs * use powsolution * privitize most methods * fix test * fix perms * remove test script * remove debug * fix call * fix seal * fix combined hash * move to method * fix test using real example * update mock bins * use new builder * fix block update tests * fix some patching in tests * mock live display for some tests * fix chain mock * update linux bin * add mock network flag * set max diff at 0 for mock netuid 1 * set min diff too * add try catch for setup * add some logging during tests * don't submit on cli register * update test durations * fix test to use mock keypair * return mock wallet * should use subtensor instance during rereg * update node subtensor bins * use fixtures and multiple subtensor instances * changelog update * skip CLI tests (#1284) * skip tests * dont test mock functions * update test durations * [Release] v4.0.0 (#1271) * bump version * Fix permissions for release github script (#1224) * should be 4.1.0 * Revert "should be 4.1.0" This reverts commit 3db08ea24f4fc4775bd46858e6c77cfa165d85ed. * Staging into Release branch (#1275) * (un)Staking multiple avoid tx limit (#1244) * add tx rate limit * wait for tx limit if not done multi stake/unstake * dont "decrypt" hotkey * additional logging for prometheus (#1246) * Dataset fix (#1249) * fix * added try except * Grab delegates details from GitHub (#1245) * add url to init * add dataclass and util functions * use in cli * remove delegates json --------- * Add raw spec for local test and new bins (#1243) * add spec and new bins * fix config netuid * use dot get * check if config netuid is list * add start to mockstatus * add attr to mock neuron * add info to mock from neurons * change ordering of neuron dict to namespace * remove test for wandb for axon * use regex for looser match * fix blacklist metagraph mock * use real mock netuid * use mock network and netuid in constructor * fix patch * patch delegate check * use mock network and netuid * remove check for wallet hotkey * fix tests for subtensor init * dont set netuid for overview test * typo in docstring * add mock status stop * add low mock tx limit * oops typo * use dot get * add wait for final and incl args * use args during setup * update bins and use 100ms blocktime * pass block arg * remove bittensor.logging and a old test * use random port * backward fix * fix block time to 1s * compile no symb on linux * compile no symb mac * remove useless init on var * use dot get for new flags * update test durations * update test durations * use dot get for config * output error msg * mock to_default * remove to defaults in help * reduce neruons, remove flaky test * deactivate test * mvoe key pair tests out of the subtensor interface --------- * Fix list_delegates on non-archive nodes (#1232) * Change how pull of archival data is handled * fix for list_delegates too * . * use empty dict * fix spacing * specify exception * log out * add space in log message * use warning instead * Blacklist fixes + depreciation of old signatures (#1240) * fixes blacklist error message + remove * remove checks for parse signature * remove sign v1 tests * fix for the syanpse checks * fix tests and remove duplicate sign * [BIT-636] Change u16 weight normalization to max-upscaling (#1241) * Change u16 weight normalization to max-upscaling Use full u16 bitwidth so that max_weight=U16_MAX, then rely on subtensor epoch to properly normalize weights in I32F32. This means that weights submission extrinsic to subtensor does not have to be pre-normalized. * Skip zero sum in weight conversion * Round u16 weights * remove duplicate command #1228 (#1231) * remove duplicate command #1228 * Extract create_parser for cli testing * mark as private * use in tests and test for duplicates * fix test using mock prompt answers * test_forward_priority_2nd_request_timeout fix (#1276) fix * Remove btcli query and btcli set_weights (#1144) . --------- * Remove codecov (#1282) * Use alt new preseal (#1269) * use new preseal for reg * bump cubit req * fix arg order issue * cubit req back * use alt impl * fix typehint * use 512 * modify tests for new format * refactor functions to use helpers and remove useless * refactor functions * add test for CPU solver * modify tests for privitized module and methods * private register cuda * move formatting funcs * use powsolution * privitize most methods * fix test * fix perms * remove test script * remove debug * fix call * fix seal * fix combined hash * move to method * fix test using real example * update mock bins * use new builder * fix block update tests * fix some patching in tests * mock live display for some tests * fix chain mock * update linux bin * add mock network flag * set max diff at 0 for mock netuid 1 * set min diff too * add try catch for setup * add some logging during tests * don't submit on cli register * update test durations * fix test to use mock keypair * return mock wallet * should use subtensor instance during rereg * update node subtensor bins * use fixtures and multiple subtensor instances * changelog update * skip CLI tests (#1284) * skip tests * dont test mock functions * update test durations --------- * fix my delegates * fix perms on changelog script * update version * fix changelog script * Catch bad endpoint protocol (#1296) * catch protocol not good * add protocol 4 * catch assertion and return bool * catch assertion errors * changelog --------- * Update DEVELOPMENT_WORKFLOW.md * final fixes * staging updates and fixes (#1540) * fix cli test * fix double-counted hotkeys per subnet and non-iterable stake obj (#1539) * fix double-counted hotkeys per subnet and non-iterable stake obj * run black * Add root get_weights command to btcli (#1536) * Add root get_weights command to btcli * Use a percentage for viewing weights instead of float values * run black, fix cli test --------- * Fix typo (#1543) * remove duplicated debug message in dendrite (#1544) * Cli fix (#1541) don't break on mismatched coldkey from local wallet <> chain * update faucet helpstr (#1542) * Added mechanism to sum all delegated tao (#1547) * Dict hash fix (#1548) * use dict() when hasing body objects to not convert arbitrary objects to str * recreate synapse in axon dependency to avoid duplicating code * black * Merge master (#1552) Release/6.1.0 (#1550) * Fix typo (#1543) * remove duplicated debug message in dendrite (#1544) * Cli fix (#1541) don't break on mismatched coldkey from local wallet <> chain * update faucet helpstr (#1542) * Added mechanism to sum all delegated tao (#1547) * Dict hash fix (#1548) * use dict() when hasing body objects to not convert arbitrary objects to str * recreate synapse in axon dependency to avoid duplicating code * black * update version * update changelog --------- * Streaming fix (#1551) * yield chunks immediately in process_streaming_responss so clients can access * break streaming into separate call funcs * update docstrings, types * black * duplicate debug msg * add warning for mismatched streaming arg + subclass * Fix typos (#1553) * Release/6.1.0 (#1550) * Fix typo (#1543) * remove duplicated debug message in dendrite (#1544) * Cli fix (#1541) don't break on mismatched coldkey from local wallet <> chain * update faucet helpstr (#1542) * Added mechanism to sum all delegated tao (#1547) * Dict hash fix (#1548) * use dict() when hasing body objects to not convert arbitrary objects to str * recreate synapse in axon dependency to avoid duplicating code * black * update version * update changelog --------- * fix typos --------- * Normalize weights in r get weights table (#1556) * Normalize weights in r get weights table * use numpy and make table look nicer * apply black * add max 1 to prevent div0 --------- * Dendrite & Synapse updates and fixes (#1555) * remove tensor header objects to not overflow headers * update tests to reflect removal of tensor headers * ensure consistent method for calling in synapse methods * fix dendrite UnClosesedSession error * fix docstring and add tests for close/aclose * rm extra delete * run black * add default synapse dict() consistency test * call del on session after close_session(), fix tests * update dendrite dummy clsname * add dendrite.query finally block * fix test * rm root flag in metagraph (#1558) * rm root flag in metagraph * run black * typo * Max Faucet Runs == 3 (#1560) add exceptions * replace unknown wallet params (chain mismatch) with key values (#1559) * replace unknown wallet params (chain mismatch) with key values * run black * rm debug prints * Remove PoW registration cli and associated extrinsic (#1557) * Remove PoW registration cli and associated extrinsic * run black * no mo pow, no mo pow tests * remove now deprecated PoW reregister routine * remove deprecated tests * more test fixes * remove _do_pow call * return PoW but still kill reregister (unused) * run black * return test to networks choices in btcli, fix chain_endpoint selection * fix pow args * Add btcli wallet balance (#1564) * begin adding balance command * skip validator that hasn't set weights yet on root * finish balances command * formatter * issue warning for nonexistent coldkeypub.txt rather than break * Dendrite fixes (#1561) * make sure a session exists before trying to close it * don't double iterate over async generator, simply return it * black * less DRY violations * fix typehints * add versioning * update changelog * remove unused registration utils * fix typos --------- Co-authored-by: Cameron Fairchild Co-authored-by: Eugene Co-authored-by: Eugene-hu <85906264+Eugene-hu@users.noreply.github.com> Co-authored-by: isabella618033 <49876827+isabella618033@users.noreply.github.com> Co-authored-by: joeylegere Co-authored-by: Cameron Fairchild Co-authored-by: “quac88” <“mac2@thrasher.com”> Co-authored-by: opentaco <93473497+opentaco@users.noreply.github.com> Co-authored-by: Eduardo García Co-authored-by: Ayden Brewer Co-authored-by: Steffen Cruz Co-authored-by: Ala Shaabana Co-authored-by: Ala Shaabana Co-authored-by: omahs <73983677+omahs@users.noreply.github.com> Co-authored-by: Cameron Fairchild Co-authored-by: Cameron Fairchild * adding logging.exception (#1569) * adding logging.exception * Update network.py (#1568) * Release/6.2.0 (#1567) * (un)Staking multiple avoid tx limit (#1244) * add tx rate limit * wait for tx limit if not done multi stake/unstake * dont "decrypt" hotkey * additional logging for prometheus * additional logging for prometheus (#1246) * Dataset fix (#1249) * fix * added try except * Grab delegates details from GitHub (#1245) * add url to init * add dataclass and util functions * use in cli * remove delegates json --------- Co-authored-by: joeylegere * Add raw spec for local test and new bins (#1243) * add spec and new bins * fix config netuid * use dot get * check if config netuid is list * add start to mockstatus * add attr to mock neuron * add info to mock from neurons * change ordering of neuron dict to namespace * remove test for wandb for axon * use regex for looser match * fix blacklist metagraph mock * use real mock netuid * use mock network and netuid in constructor * fix patch * patch delegate check * use mock network and netuid * remove check for wallet hotkey * fix tests for subtensor init * dont set netuid for overview test * typo in docstring * add mock status stop * add low mock tx limit * oops typo * use dot get * add wait for final and incl args * use args during setup * update bins and use 100ms blocktime * pass block arg * remove bittensor.logging and a old test * use random port * backward fix * fix block time to 1s * compile no symb on linux * compile no symb mac * remove useless init on var * use dot get for new flags * update test durations * update test durations * use dot get for config * output error msg * mock to_default * remove to defaults in help * reduce neruons, remove flaky test * deactivate test * mvoe key pair tests out of the subtensor interface --------- Co-authored-by: Eugene * Fix list_delegates on non-archive nodes (#1232) * Change how pull of archival data is handled * fix for list_delegates too * . * use empty dict * fix spacing * specify exception * log out * add space in log message * use warning instead * Blacklist fixes + depreciation of old signatures (#1240) * fixes blacklist error message + remove * remove checks for parse signature * remove sign v1 tests * fix for the syanpse checks * fix tests and remove duplicate sign * Improve development workflow documentation * [BIT-636] Change u16 weight normalization to max-upscaling (#1241) * Change u16 weight normalization to max-upscaling Use full u16 bitwidth so that max_weight=U16_MAX, then rely on subtensor epoch to properly normalize weights in I32F32. This means that weights submission extrinsic to subtensor does not have to be pre-normalized. * Skip zero sum in weight conversion * Round u16 weights * remove duplicate command #1228 (#1231) * remove duplicate command #1228 * Extract create_parser for cli testing * mark as private * use in tests and test for duplicates * fix test using mock prompt answers * test_forward_priority_2nd_request_timeout fix (#1276) fix * Remove btcli query and btcli set_weights (#1144) . * Merge releases 4.0.0 and 4.0.1 back to staging (#1306) * bump version * Fix permissions for release github script (#1224) Co-authored-by: Cameron Fairchild * should be 4.1.0 * Revert "should be 4.1.0" This reverts commit 3db08ea24f4fc4775bd46858e6c77cfa165d85ed. * Staging into Release branch (#1275) * (un)Staking multiple avoid tx limit (#1244) * add tx rate limit * wait for tx limit if not done multi stake/unstake * dont "decrypt" hotkey * additional logging for prometheus (#1246) * Dataset fix (#1249) * fix * added try except * Grab delegates details from GitHub (#1245) * add url to init * add dataclass and util functions * use in cli * remove delegates json --------- Co-authored-by: joeylegere * Add raw spec for local test and new bins (#1243) * add spec and new bins * fix config netuid * use dot get * check if config netuid is list * add start to mockstatus * add attr to mock neuron * add info to mock from neurons * change ordering of neuron dict to namespace * remove test for wandb for axon * use regex for looser match * fix blacklist metagraph mock * use real mock netuid * use mock network and netuid in constructor * fix patch * patch delegate check * use mock network and netuid * remove check for wallet hotkey * fix tests for subtensor init * dont set netuid for overview test * typo in docstring * add mock status stop * add low mock tx limit * oops typo * use dot get * add wait for final and incl args * use args during setup * update bins and use 100ms blocktime * pass block arg * remove bittensor.logging and a old test * use random port * backward fix * fix block time to 1s * compile no symb on linux * compile no symb mac * remove useless init on var * use dot get for new flags * update test durations * update test durations * use dot get for config * output error msg * mock to_default * remove to defaults in help * reduce neruons, remove flaky test * deactivate test * mvoe key pair tests out of the subtensor interface --------- Co-authored-by: Eugene * Fix list_delegates on non-archive nodes (#1232) * Change how pull of archival data is handled * fix for list_delegates too * . * use empty dict * fix spacing * specify exception * log out * add space in log message * use warning instead * Blacklist fixes + depreciation of old signatures (#1240) * fixes blacklist error message + remove * remove checks for parse signature * remove sign v1 tests * fix for the syanpse checks * fix tests and remove duplicate sign * [BIT-636] Change u16 weight normalization to max-upscaling (#1241) * Change u16 weight normalization to max-upscaling Use full u16 bitwidth so that max_weight=U16_MAX, then rely on subtensor epoch to properly normalize weights in I32F32. This means that weights submission extrinsic to subtensor does not have to be pre-normalized. * Skip zero sum in weight conversion * Round u16 weights * remove duplicate command #1228 (#1231) * remove duplicate command #1228 * Extract create_parser for cli testing * mark as private * use in tests and test for duplicates * fix test using mock prompt answers * test_forward_priority_2nd_request_timeout fix (#1276) fix * Remove btcli query and btcli set_weights (#1144) . --------- Co-authored-by: Eugene-hu <85906264+Eugene-hu@users.noreply.github.com> Co-authored-by: isabella618033 <49876827+isabella618033@users.noreply.github.com> Co-authored-by: joeylegere Co-authored-by: Eugene Co-authored-by: opentaco <93473497+opentaco@users.noreply.github.com> * Remove codecov (#1282) * Use alt new preseal (#1269) * use new preseal for reg * bump cubit req * fix arg order issue * cubit req back * use alt impl * fix typehint * use 512 * modify tests for new format * refactor functions to use helpers and remove useless * refactor functions * add test for CPU solver * modify tests for privitized module and methods * private register cuda * move formatting funcs * use powsolution * privitize most methods * fix test * fix perms * remove test script * remove debug * fix call * fix seal * fix combined hash * move to method * fix test using real example * update mock bins * use new builder * fix block update tests * fix some patching in tests * mock live display for some tests * fix chain mock * update linux bin * add mock network flag * set max diff at 0 for mock netuid 1 * set min diff too * add try catch for setup * add some logging during tests * don't submit on cli register * update test durations * fix test to use mock keypair * return mock wallet * should use subtensor instance during rereg * update node subtensor bins * use fixtures and multiple subtensor instances * changelog update * skip CLI tests (#1284) * skip tests * dont test mock functions * update test durations * [Release] v4.0.0 (#1271) * bump version * Fix permissions for release github script (#1224) Co-authored-by: Cameron Fairchild * should be 4.1.0 * Revert "should be 4.1.0" This reverts commit 3db08ea24f4fc4775bd46858e6c77cfa165d85ed. * Staging into Release branch (#1275) * (un)Staking multiple avoid tx limit (#1244) * add tx rate limit * wait for tx limit if not done multi stake/unstake * dont "decrypt" hotkey * additional logging for prometheus (#1246) * Dataset fix (#1249) * fix * added try except * Grab delegates details from GitHub (#1245) * add url to init * add dataclass and util functions * use in cli * remove delegates json --------- Co-authored-by: joeylegere * Add raw spec for local test and new bins (#1243) * add spec and new bins * fix config netuid * use dot get * check if config netuid is list * add start to mockstatus * add attr to mock neuron * add info to mock from neurons * change ordering of neuron dict to namespace * remove test for wandb for axon * use regex for looser match * fix blacklist metagraph mock * use real mock netuid * use mock network and netuid in constructor * fix patch * patch delegate check * use mock network and netuid * remove check for wallet hotkey * fix tests for subtensor init * dont set netuid for overview test * typo in docstring * add mock status stop * add low mock tx limit * oops typo * use dot get * add wait for final and incl args * use args during setup * update bins and use 100ms blocktime * pass block arg * remove bittensor.logging and a old test * use random port * backward fix * fix block time to 1s * compile no symb on linux * compile no symb mac * remove useless init on var * use dot get for new flags * update test durations * update test durations * use dot get for config * output error msg * mock to_default * remove to defaults in help * reduce neruons, remove flaky test * deactivate test * mvoe key pair tests out of the subtensor interface --------- Co-authored-by: Eugene * Fix list_delegates on non-archive nodes (#1232) * Change how pull of archival data is handled * fix for list_delegates too * . * use empty dict * fix spacing * specify exception * log out * add space in log message * use warning instead * Blacklist fixes + depreciation of old signatures (#1240) * fixes blacklist error message + remove * remove checks for parse signature * remove sign v1 tests * fix for the syanpse checks * fix tests and remove duplicate sign * [BIT-636] Change u16 weight normalization to max-upscaling (#1241) * Change u16 weight normalization to max-upscaling Use full u16 bitwidth so that max_weight=U16_MAX, then rely on subtensor epoch to properly normalize weights in I32F32. This means that weights submission extrinsic to subtensor does not have to be pre-normalized. * Skip zero sum in weight conversion * Round u16 weights * remove duplicate command #1228 (#1231) * remove duplicate command #1228 * Extract create_parser for cli testing * mark as private * use in tests and test for duplicates * fix test using mock prompt answers * test_forward_priority_2nd_request_timeout fix (#1276) fix * Remove btcli query and btcli set_weights (#1144) . --------- Co-authored-by: Eugene-hu <85906264+Eugene-hu@users.noreply.github.com> Co-authored-by: isabella618033 <49876827+isabella618033@users.noreply.github.com> Co-authored-by: joeylegere Co-authored-by: Eugene Co-authored-by: opentaco <93473497+opentaco@users.noreply.github.com> * Remove codecov (#1282) * Use alt new preseal (#1269) * use new preseal for reg * bump cubit req * fix arg order issue * cubit req back * use alt impl * fix typehint * use 512 * modify tests for new format * refactor functions to use helpers and remove useless * refactor functions * add test for CPU solver * modify tests for privitized module and methods * private register cuda * move formatting funcs * use powsolution * privitize most methods * fix test * fix perms * remove test script * remove debug * fix call * fix seal * fix combined hash * move to method * fix test using real example * update mock bins * use new builder * fix block update tests * fix some patching in tests * mock live display for some tests * fix chain mock * update linux bin * add mock network flag * set max diff at 0 for mock netuid 1 * set min diff too * add try catch for setup * add some logging during tests * don't submit on cli register * update test durations * fix test to use mock keypair * return mock wallet * should use subtensor instance during rereg * update node subtensor bins * use fixtures and multiple subtensor instances * changelog update * skip CLI tests (#1284) * skip tests * dont test mock functions * update test durations --------- Co-authored-by: Eduardo García Co-authored-by: Eugene-hu <85906264+Eugene-hu@users.noreply.github.com> Co-authored-by: isabella618033 <49876827+isabella618033@users.noreply.github.com> Co-authored-by: joeylegere Co-authored-by: Eugene Co-authored-by: opentaco <93473497+opentaco@users.noreply.github.com> * fix my delegates * fix perms on changelog script * update version * fix changelog script * Catch bad endpoint protocol (#1296) * catch protocol not good * add protocol 4 * catch assertion and return bool * catch assertion errors * changelog --------- Co-authored-by: Eduardo García Co-authored-by: Eugene-hu <85906264+Eugene-hu@users.noreply.github.com> Co-authored-by: isabella618033 <49876827+isabella618033@users.noreply.github.com> Co-authored-by: joeylegere Co-authored-by: Eugene Co-authored-by: opentaco <93473497+opentaco@users.noreply.github.com> * Update DEVELOPMENT_WORKFLOW.md * final fixes * staging updates and fixes (#1540) * fix cli test * fix double-counted hotkeys per subnet and non-iterable stake obj (#1539) * fix double-counted hotkeys per subnet and non-iterable stake obj * run black * Add root get_weights command to btcli (#1536) * Add root get_weights command to btcli * Use a percentage for viewing weights instead of float values * run black, fix cli test --------- Co-authored-by: ifrit98 * Fix typo (#1543) Co-authored-by: philanthrope * remove duplicated debug message in dendrite (#1544) * Cli fix (#1541) don't break on mismatched coldkey from local wallet <> chain * update faucet helpstr (#1542) * Added mechanism to sum all delegated tao (#1547) Co-authored-by: Ala Shaabana * Dict hash fix (#1548) * use dict() when hasing body objects to not convert arbitrary objects to str * recreate synapse in axon dependency to avoid duplicating code * black * Merge master (#1552) Release/6.1.0 (#1550) * Fix typo (#1543) * remove duplicated debug message in dendrite (#1544) * Cli fix (#1541) don't break on mismatched coldkey from local wallet <> chain * update faucet helpstr (#1542) * Added mechanism to sum all delegated tao (#1547) * Dict hash fix (#1548) * use dict() when hasing body objects to not convert arbitrary objects to str * recreate synapse in axon dependency to avoid duplicating code * black * update version * update changelog --------- Co-authored-by: Cameron Fairchild Co-authored-by: Eugene Co-authored-by: isabella618033 <49876827+isabella618033@users.noreply.github.com> Co-authored-by: Cameron Fairchild Co-authored-by: opentaco <93473497+opentaco@users.noreply.github.com> Co-authored-by: Eduardo García Co-authored-by: Ayden Brewer Co-authored-by: Steffen Cruz Co-authored-by: Ala Shaabana * Streaming fix (#1551) * yield chunks immediately in process_streaming_responss so clients can access * break streaming into separate call funcs * update docstrings, types * black * duplicate debug msg * add warning for mismatched streaming arg + subclass * Fix typos (#1553) * Release/6.1.0 (#1550) Co-authored-by: ifrit98 * Fix typo (#1543) Co-authored-by: philanthrope * remove duplicated debug message in dendrite (#1544) * Cli fix (#1541) don't break on mismatched coldkey from local wallet <> chain * update faucet helpstr (#1542) * Added mechanism to sum all delegated tao (#1547) Co-authored-by: Ala Shaabana * Dict hash fix (#1548) * use dict() when hasing body objects to not convert arbitrary objects to str * recreate synapse in axon dependency to avoid duplicating code * black * update version * update changelog --------- Co-authored-by: Cameron Fairchild Co-authored-by: Eugene Co-authored-by: isabella618033 <49876827+isabella618033@users.noreply.github.com> Co-authored-by: Cameron Fairchild Co-authored-by: opentaco <93473497+opentaco@users.noreply.github.com> Co-authored-by: Eduardo García Co-authored-by: Ayden Brewer Co-authored-by: Steffen Cruz Co-authored-by: Ala Shaabana * fix typos --------- Co-authored-by: philanthrope Co-authored-by: Cameron Fairchild Co-authored-by: Eugene Co-authored-by: isabella618033 <49876827+isabella618033@users.noreply.github.com> Co-authored-by: Cameron Fairchild Co-authored-by: opentaco <93473497+opentaco@users.noreply.github.com> Co-authored-by: Eduardo García Co-authored-by: Ayden Brewer Co-authored-by: Steffen Cruz Co-authored-by: Ala Shaabana * Normalize weights in r get weights table (#1556) * Normalize weights in r get weights table * use numpy and make table look nicer * apply black * add max 1 to prevent div0 --------- Co-authored-by: Cameron Fairchild * Dendrite & Synapse updates and fixes (#1555) * remove tensor header objects to not overflow headers * update tests to reflect removal of tensor headers * ensure consistent method for calling in synapse methods * fix dendrite UnClosesedSession error * fix docstring and add tests for close/aclose * rm extra delete * run black * add default synapse dict() consistency test * call del on session after close_session(), fix tests * update dendrite dummy clsname * add dendrite.query finally block * fix test * rm root flag in metagraph (#1558) * rm root flag in metagraph * run black * typo * Max Faucet Runs == 3 (#1560) add exceptions * replace unknown wallet params (chain mismatch) with key values (#1559) * replace unknown wallet params (chain mismatch) with key values * run black * rm debug prints * Remove PoW registration cli and associated extrinsic (#1557) * Remove PoW registration cli and associated extrinsic * run black * no mo pow, no mo pow tests * remove now deprecated PoW reregister routine * remove deprecated tests * more test fixes * remove _do_pow call * return PoW but still kill reregister (unused) * run black * return test to networks choices in btcli, fix chain_endpoint selection * fix pow args * Add btcli wallet balance (#1564) * begin adding balance command * skip validator that hasn't set weights yet on root * finish balances command * formatter * issue warning for nonexistent coldkeypub.txt rather than break * Dendrite fixes (#1561) * make sure a session exists before trying to close it * don't double iterate over async generator, simply return it * black * less DRY violations * fix typehints * add versioning * update changelog * remove unused registration utils * fix typos --------- Co-authored-by: Cameron Fairchild Co-authored-by: Eugene Co-authored-by: Eugene-hu <85906264+Eugene-hu@users.noreply.github.com> Co-authored-by: isabella618033 <49876827+isabella618033@users.noreply.github.com> Co-authored-by: joeylegere Co-authored-by: Cameron Fairchild Co-authored-by: “quac88” <“mac2@thrasher.com”> Co-authored-by: opentaco <93473497+opentaco@users.noreply.github.com> Co-authored-by: Eduardo García Co-authored-by: Ayden Brewer Co-authored-by: Steffen Cruz Co-authored-by: Ala Shaabana Co-authored-by: Ala Shaabana Co-authored-by: omahs <73983677+omahs@users.noreply.github.com> Co-authored-by: Cameron Fairchild Co-authored-by: Cameron Fairchild * Update network.py small spelling fix `weights_rate_Limit` -> `weights_rate_limit` * Reformat --------- Co-authored-by: philanthrope Co-authored-by: Cameron Fairchild Co-authored-by: Eugene Co-authored-by: Eugene-hu <85906264+Eugene-hu@users.noreply.github.com> Co-authored-by: isabella618033 <49876827+isabella618033@users.noreply.github.com> Co-authored-by: joeylegere Co-authored-by: Cameron Fairchild Co-authored-by: “quac88” <“mac2@thrasher.com”> Co-authored-by: opentaco <93473497+opentaco@users.noreply.github.com> Co-authored-by: Eduardo García Co-authored-by: Ayden Brewer Co-authored-by: Steffen Cruz Co-authored-by: Ala Shaabana Co-authored-by: Ala Shaabana Co-authored-by: omahs <73983677+omahs@users.noreply.github.com> Co-authored-by: Cameron Fairchild Co-authored-by: Cameron Fairchild * Subtensor Registry (#1562) * subtensor identity query * formatter * adds identified field * black and additional requirement * black --------- Co-authored-by: ifrit98 * add instructions for upgrading bittensor with outdated version check (#1571) * add instructions for upgrading bittensor with outdated version check * run black * Add identity commands to btcli (#1566) * subtensor identity query * formatter * add get/set identity btcli command * check for values > 64 bytes max on chain * run black * update identity funcs for hotkey option * fix incorrect bytes limit for idendity * run black * add check for 1 tao cost --------- Co-authored-by: Eugene * Add set_delegate_take command to btcli (#1563) * Add set_delegate_take command * formatter * string info update --------- Co-authored-by: ifrit98 * Subtensor archive (#1575) * add archive network option to subtensor * add / * fix archive resolution * Bugfix/list delegates (#1577) * make this legible/visible * run black * don't return result twice in query() (#1574) * rename logging.py so doesn't circ import (#1572) * add AxonInfo._string() (#1565) * add AxonInfo._string() * add error handling * don't print __is_set for recursive objects (#1573) * fix merge duplication in dendrite, config print error * update version * update changelog * Adds docstrings for CLI for Sphynx documentation (#1579) * Release/6.2.0 (#1567) * (un)Staking multiple avoid tx limit (#1244) * add tx rate limit * wait for tx limit if not done multi stake/unstake * dont "decrypt" hotkey * additional logging for prometheus * additional logging for prometheus (#1246) * Dataset fix (#1249) * fix * added try except * Grab delegates details from GitHub (#1245) * add url to init * add dataclass and util functions * use in cli * remove delegates json --------- Co-authored-by: joeylegere * Add raw spec for local test and new bins (#1243) * add spec and new bins * fix config netuid * use dot get * check if config netuid is list * add start to mockstatus * add attr to mock neuron * add info to mock from neurons * change ordering of neuron dict to namespace * remove test for wandb for axon * use regex for looser match * fix blacklist metagraph mock * use real mock netuid * use mock network and netuid in constructor * fix patch * patch delegate check * use mock network and netuid * remove check for wallet hotkey * fix tests for subtensor init * dont set netuid for overview test * typo in docstring * add mock status stop * add low mock tx limit * oops typo * use dot get * add wait for final and incl args * use args during setup * update bins and use 100ms blocktime * pass block arg * remove bittensor.logging and a old test * use random port * backward fix * fix block time to 1s * compile no symb on linux * compile no symb mac * remove useless init on var * use dot get for new flags * update test durations * update test durations * use dot get for config * output error msg * mock to_default * remove to defaults in help * reduce neruons, remove flaky test * deactivate test * mvoe key pair tests out of the subtensor interface --------- Co-authored-by: Eugene * Fix list_delegates on non-archive nodes (#1232) * Change how pull of archival data is handled * fix for list_delegates too * . * use empty dict * fix spacing * specify exception * log out * add space in log message * use warning instead * Blacklist fixes + depreciation of old signatures (#1240) * fixes blacklist error message + remove * remove checks for parse signature * remove sign v1 tests * fix for the syanpse checks * fix tests and remove duplicate sign * Improve development workflow documentation * [BIT-636] Change u16 weight normalization to max-upscaling (#1241) * Change u16 weight normalization to max-upscaling Use full u16 bitwidth so that max_weight=U16_MAX, then rely on subtensor epoch to properly normalize weights in I32F32. This means that weights submission extrinsic to subtensor does not have to be pre-normalized. * Skip zero sum in weight conversion * Round u16 weights * remove duplicate command #1228 (#1231) * remove duplicate command #1228 * Extract create_parser for cli testing * mark as private * use in tests and test for duplicates * fix test using mock prompt answers * test_forward_priority_2nd_request_timeout fix (#1276) fix * Remove btcli query and btcli set_weights (#1144) . * Merge releases 4.0.0 and 4.0.1 back to staging (#1306) * bump version * Fix permissions for release github script (#1224) Co-authored-by: Cameron Fairchild * should be 4.1.0 * Revert "should be 4.1.0" This reverts commit 3db08ea24f4fc4775bd46858e6c77cfa165d85ed. * Staging into Release branch (#1275) * (un)Staking multiple avoid tx limit (#1244) * add tx rate limit * wait for tx limit if not done multi stake/unstake * dont "decrypt" hotkey * additional logging for prometheus (#1246) * Dataset fix (#1249) * fix * added try except * Grab delegates details from GitHub (#1245) * add url to init * add dataclass and util functions * use in cli * remove delegates json --------- Co-authored-by: joeylegere * Add raw spec for local test and new bins (#1243) * add spec and new bins * fix config netuid * use dot get * check if config netuid is list * add start to mockstatus * add attr to mock neuron * add info to mock from neurons * change ordering of neuron dict to namespace * remove test for wandb for axon * use regex for looser match * fix blacklist metagraph mock * use real mock netuid * use mock network and netuid in constructor * fix patch * patch delegate check * use mock network and netuid * remove check for wallet hotkey * fix tests for subtensor init * dont set netuid for overview test * typo in docstring * add mock status stop * add low mock tx limit * oops typo * use dot get * add wait for final and incl args * use args during setup * update bins and use 100ms blocktime * pass block arg * remove bittensor.logging and a old test * use random port * backward fix * fix block time to 1s * compile no symb on linux * compile no symb mac * remove useless init on var * use dot get for new flags * update test durations * update test durations * use dot get for config * output error msg * mock to_default * remove to defaults in help * reduce neruons, remove flaky test * deactivate test * mvoe key pair tests out of the subtensor interface --------- Co-authored-by: Eugene * Fix list_delegates on non-archive nodes (#1232) * Change how pull of archival data is handled * fix for list_delegates too * . * use empty dict * fix spacing * specify exception * log out * add space in log message * use warning instead * Blacklist fixes + depreciation of old signatures (#1240) * fixes blacklist error message + remove * remove checks for parse signature * remove sign v1 tests * fix for the syanpse checks * fix tests and remove duplicate sign * [BIT-636] Change u16 weight normalization to max-upscaling (#1241) * Change u16 weight normalization to max-upscaling Use full u16 bitwidth so that max_weight=U16_MAX, then rely on subtensor epoch to properly normalize weights in I32F32. This means that weights submission extrinsic to subtensor does not have to be pre-normalized. * Skip zero sum in weight conversion * Round u16 weights * remove duplicate command #1228 (#1231) * remove duplicate command #1228 * Extract create_parser for cli testing * mark as private * use in tests and test for duplicates * fix test using mock prompt answers * test_forward_priority_2nd_request_timeout fix (#1276) fix * Remove btcli query and btcli set_weights (#1144) . --------- Co-authored-by: Eugene-hu <85906264+Eugene-hu@users.noreply.github.com> Co-authored-by: isabella618033 <49876827+isabella618033@users.noreply.github.com> Co-authored-by: joeylegere Co-authored-by: Eugene Co-authored-by: opentaco <93473497+opentaco@users.noreply.github.com> * Remove codecov (#1282) * Use alt new preseal (#1269) * use new preseal for reg * bump cubit req * fix arg order issue * cubit req back * use alt impl * fix typehint * use 512 * modify tests for new format * refactor functions to use helpers and remove useless * refactor functions * add test for CPU solver * modify tests for privitized module and methods * private register cuda * move formatting funcs * use powsolution * privitize most methods * fix test * fix perms * remove test script * remove debug * fix call * fix seal * fix combined hash * move to method * fix test using real example * update mock bins * use new builder * fix block update tests * fix some patching in tests * mock live display for some tests * fix chain mock * update linux bin * add mock network flag * set max diff at 0 for mock netuid 1 * set min diff too * add try catch for setup * add some logging during tests * don't submit on cli register * update test durations * fix test to use mock keypair * return mock wallet * should use subtensor instance during rereg * update node subtensor bins * use fixtures and multiple subtensor instances * changelog update * skip CLI tests (#1284) * skip tests * dont test mock functions * update test durations * [Release] v4.0.0 (#1271) * bump version * Fix permissions for release github script (#1224) Co-authored-by: Cameron Fairchild * should be 4.1.0 * Revert "should be 4.1.0" This reverts commit 3db08ea24f4fc4775bd46858e6c77cfa165d85ed. * Staging into Release branch (#1275) * (un)Staking multiple avoid tx limit (#1244) * add tx rate limit * wait for tx limit if not done multi stake/unstake * dont "decrypt" hotkey * additional logging for prometheus (#1246) * Dataset fix (#1249) * fix * added try except * Grab delegates details from GitHub (#1245) * add url to init * add dataclass and util functions * use in cli * remove delegates json --------- Co-authored-by: joeylegere * Add raw spec for local test and new bins (#1243) * add spec and new bins * fix config netuid * use dot get * check if config netuid is list * add start to mockstatus * add attr to mock neuron * add info to mock from neurons * change ordering of neuron dict to namespace * remove test for wandb for axon * use regex for looser match * fix blacklist metagraph mock * use real mock netuid * use mock network and netuid in constructor * fix patch * patch delegate check * use mock network and netuid * remove check for wallet hotkey * fix tests for subtensor init * dont set netuid for overview test * typo in docstring * add mock status stop * add low mock tx limit * oops typo * use dot get * add wait for final and incl args * use args during setup * update bins and use 100ms blocktime * pass block arg * remove bittensor.logging and a old test * use random port * backward fix * fix block time to 1s * compile no symb on linux * compile no symb mac * remove useless init on var * use dot get for new flags * update test durations * update test durations * use dot get for config * output error msg * mock to_default * remove to defaults in help * reduce neruons, remove flaky test * deactivate test * mvoe key pair tests out of the subtensor interface --------- Co-authored-by: Eugene * Fix list_delegates on non-archive nodes (#1232) * Change how pull of archival data is handled * fix for list_delegates too * . * use empty dict * fix spacing * specify exception * log out * add space in log message * use warning instead * Blacklist fixes + depreciation of old signatures (#1240) * fixes blacklist error message + remove * remove checks for parse signature * remove sign v1 tests * fix for the syanpse checks * fix tests and remove duplicate sign * [BIT-636] Change u16 weight normalization to max-upscaling (#1241) * Change u16 weight normalization to max-upscaling Use full u16 bitwidth so that max_weight=U16_MAX, then rely on subtensor epoch to properly normalize weights in I32F32. This means that weights submission extrinsic to subtensor does not have to be pre-normalized. * Skip zero sum in weight conversion * Round u16 weights * remove duplicate command #1228 (#1231) * remove duplicate command #1228 * Extract create_parser for cli testing * mark as private * use in tests and test for duplicates * fix test using mock prompt answers * test_forward_priority_2nd_request_timeout fix (#1276) fix * Remove btcli query and btcli set_weights (#1144) . --------- Co-authored-by: Eugene-hu <85906264+Eugene-hu@users.noreply.github.com> Co-authored-by: isabella618033 <49876827+isabella618033@users.noreply.github.com> Co-authored-by: joeylegere Co-authored-by: Eugene Co-authored-by: opentaco <93473497+opentaco@users.noreply.github.com> * Remove codecov (#1282) * Use alt new preseal (#1269) * use new preseal for reg * bump cubit req * fix arg order issue * cubit req back * use alt impl * fix typehint * use 512 * modify tests for new format * refactor functions to use helpers and remove useless * refactor functions * add test for CPU solver * modify tests for privitized module and methods * private register cuda * move formatting funcs * use powsolution * privitize most methods * fix test * fix perms * remove test script * remove debug * fix call * fix seal * fix combined hash * move to method * fix test using real example * update mock bins * use new builder * fix block update tests * fix some patching in tests * mock live display for some tests * fix chain mock * update linux bin * add mock network flag * set max diff at 0 for mock netuid 1 * set min diff too * add try catch for setup * add some logging during tests * don't submit on cli register * update test durations * fix test to use mock keypair * return mock wallet * should use subtensor instance during rereg * update node subtensor bins * use fixtures and multiple subtensor instances * changelog update * skip CLI tests (#1284) * skip tests * dont test mock functions * update test durations --------- Co-authored-by: Eduardo García Co-authored-by: Eugene-hu <85906264+Eugene-hu@users.noreply.github.com> Co-authored-by: isabella618033 <49876827+isabella618033@users.noreply.github.com> Co-authored-by: joeylegere Co-authored-by: Eugene Co-authored-by: opentaco <93473497+opentaco@users.noreply.github.com> * fix my delegates * fix perms on changelog script * update version * fix changelog script * Catch bad endpoint protocol (#1296) * catch protocol not good * add protocol 4 * catch assertion and return bool * catch assertion errors * changelog --------- Co-authored-by: Eduardo García Co-authored-by: Eugene-hu <85906264+Eugene-hu@users.noreply.github.com> Co-authored-by: isabella618033 <49876827+isabella618033@users.noreply.github.com> Co-authored-by: joeylegere Co-authored-by: Eugene Co-authored-by: opentaco <93473497+opentaco@users.noreply.github.com> * Update DEVELOPMENT_WORKFLOW.md * final fixes * staging updates and fixes (#1540) * fix cli test * fix double-counted hotkeys per subnet and non-iterable stake obj (#1539) * fix double-counted hotkeys per subnet and non-iterable stake obj * run black * Add root get_weights command to btcli (#1536) * Add root get_weights command to btcli * Use a percentage for viewing weights instead of float values * run black, fix cli test --------- Co-authored-by: ifrit98 * Fix typo (#1543) Co-authored-by: philanthrope * remove duplicated debug message in dendrite (#1544) * Cli fix (#1541) don't break on mismatched coldkey from local wallet <> chain * update faucet helpstr (#1542) * Added mechanism to sum all delegated tao (#1547) Co-authored-by: Ala Shaabana * Dict hash fix (#1548) * use dict() when hasing body objects to not convert arbitrary objects to str * recreate synapse in axon dependency to avoid duplicating code * black * Merge master (#1552) Release/6.1.0 (#1550) * Fix typo (#1543) * remove duplicated debug message in dendrite (#1544) * Cli fix (#1541) don't break on mismatched coldkey from local wallet <> chain * update faucet helpstr (#1542) * Added mechanism to sum all delegated tao (#1547) * Dict hash fix (#1548) * use dict() when hasing body objects to not convert arbitrary objects to str * recreate synapse in axon dependency to avoid duplicating code * black * update version * update changelog --------- Co-authored-by: Cameron Fairchild Co-authored-by: Eugene Co-authored-by: isabella618033 <49876827+isabella618033@users.noreply.github.com> Co-authored-by: Cameron Fairchild Co-authored-by: opentaco <93473497+opentaco@users.noreply.github.com> Co-authored-by: Eduardo García Co-authored-by: Ayden Brewer Co-authored-by: Steffen Cruz Co-authored-by: Ala Shaabana * Streaming fix (#1551) * yield chunks immediately in process_streaming_responss so clients can access * break streaming into separate call funcs * update docstrings, types * black * duplicate debug msg * add warning for mismatched streaming arg + subclass * Fix typos (#1553) * Release/6.1.0 (#1550) Co-authored-by: ifrit98 * Fix typo (#1543) Co-authored-by: philanthrope * remove duplicated debug message in dendrite (#1544) * Cli fix (#1541) don't break on mismatched coldkey from local wallet <> chain * update faucet helpstr (#1542) * Added mechanism to sum all delegated tao (#1547) Co-authored-by: Ala Shaabana * Dict hash fix (#1548) * use dict() when hasing body objects to not convert arbitrary objects to str * recreate synapse in axon dependency to avoid duplicating code * black * update version * update changelog --------- Co-authored-by: Cameron Fairchild Co-authored-by: Eugene Co-authored-by: isabella618033 <49876827+isabella618033@users.noreply.github.com> Co-authored-by: Cameron Fairchild Co-authored-by: opentaco <93473497+opentaco@users.noreply.github.com> Co-authored-by: Eduardo García Co-authored-by: Ayden Brewer Co-authored-by: Steffen Cruz Co-authored-by: Ala Shaabana * fix typos --------- Co-authored-by: philanthrope Co-authored-by: Cameron Fairchild Co-authored-by: Eugene Co-authored-by: isabella618033 <49876827+isabella618033@users.noreply.github.com> Co-authored-by: Cameron Fairchild Co-authored-by: opentaco <93473497+opentaco@users.noreply.github.com> Co-authored-by: Eduardo García Co-authored-by: Ayden Brewer Co-authored-by: Steffen Cruz Co-authored-by: Ala Shaabana * Normalize weights in r get weights table (#1556) * Normalize weights in r get weights table * use numpy and make table look nicer * apply black * add max 1 to prevent div0 --------- Co-authored-by: Cameron Fairchild * Dendrite & Synapse updates and fixes (#1555) * remove tensor header objects to not overflow headers * update tests to reflect removal of tensor headers * ensure consistent method for calling in synapse methods * fix dendrite UnClosesedSession error * fix docstring and add tests for close/aclose * rm extra delete * run black * add default synapse dict() consistency test * call del on session after close_session(), fix tests * update dendrite dummy clsname * add dendrite.query finally block * fix test * rm root flag in metagraph (#1558) * rm root flag in metagraph * run black * typo * Max Faucet Runs == 3 (#1560) add exceptions * replace unknown wallet params (chain mismatch) with key values (#1559) * replace unknown wallet params (chain mismatch) with key values * run black * rm debug prints * Remove PoW registration cli and associated extrinsic (#1557) * Remove PoW registration cli and associated extrinsic * run black * no mo pow, no mo pow tests * remove now deprecated PoW reregister routine * remove deprecated tests * more test fixes * remove _do_pow call * return PoW but still kill reregister (unused) * run black * return test to networks choices in btcli, fix chain_endpoint selection * fix pow args * Add btcli wallet balance (#1564) * begin adding balance command * skip validator that hasn't set weights yet on root * finish balances command * formatter * issue warning for nonexistent coldkeypub.txt rather than break * Dendrite fixes (#1561) * make sure a session exists before trying to close it * don't double iterate over async generator, simply return it * black * less DRY violations * fix typehints * add versioning * update changelog * remove unused registration utils * fix typos --------- Co-authored-by: Cameron Fairchild Co-authored-by: Eugene Co-authored-by: Eugene-hu <85906264+Eugene-hu@users.noreply.github.com> Co-authored-by: isabella618033 <49876827+isabella618033@users.noreply.github.com> Co-authored-by: joeylegere Co-authored-by: Cameron Fairchild Co-authored-by: “quac88” <“mac2@thrasher.com”> Co-authored-by: opentaco <93473497+opentaco@users.noreply.github.com> Co-authored-by: Eduardo García Co-authored-by: Ayden Brewer Co-authored-by: Steffen Cruz Co-authored-by: Ala Shaabana Co-authored-by: Ala Shaabana Co-authored-by: omahs <73983677+omahs@users.noreply.github.com> Co-authored-by: Cameron Fairchild Co-authored-by: Cameron Fairchild * add show_delegates docstring * docstring should go under function name * re-add missing delegate-take-command * delegates docs completed * identity * inspect * list docstr * metagraph * update metagraph doc, add misc and network * overiew, regiser, and root docstrings * update senate * update stake and transfer * wallet * delegated stake fix --------- Co-authored-by: Cameron Fairchild Co-authored-by: Eugene Co-authored-by: Eugene-hu <85906264+Eugene-hu@users.noreply.github.com> Co-authored-by: isabella618033 <49876827+isabella618033@users.noreply.github.com> Co-authored-by: joeylegere Co-authored-by: Cameron Fairchild Co-authored-by: “quac88” <“mac2@thrasher.com”> Co-authored-by: opentaco <93473497+opentaco@users.noreply.github.com> Co-authored-by: Eduardo García Co-authored-by: Ayden Brewer Co-authored-by: Steffen Cruz Co-authored-by: Ala Shaabana Co-authored-by: Ala Shaabana Co-authored-by: omahs <73983677+omahs@users.noreply.github.com> Co-authored-by: Cameron Fairchild Co-authored-by: Cameron Fairchild * Revert "Add set_delegate_take command to btcli (#1563)" This reverts commit 2a3f6f67eec193cdb7ef3fff66efdb91debbeacb. Will be staged for next release. * Axon fix (#1586) * catch keyerror where axon doesn't have route * error msg instead of trace * update logging to use correct level * update conda install yml and readme to latest bittensor reqs (#1584) * update conda install yml and readme to latest bittensor reqs * activte the env first --------- Co-authored-by: Cameron Fairchild Co-authored-by: Eugene Co-authored-by: Eugene-hu <85906264+Eugene-hu@users.noreply.github.com> Co-authored-by: isabella618033 <49876827+isabella618033@users.noreply.github.com> Co-authored-by: joeylegere Co-authored-by: Cameron Fairchild Co-authored-by: “quac88” <“mac2@thrasher.com”> Co-authored-by: opentaco <93473497+opentaco@users.noreply.github.com> Co-authored-by: Eduardo García Co-authored-by: Ayden Brewer Co-authored-by: Steffen Cruz Co-authored-by: Ala Shaabana Co-authored-by: Ala Shaabana Co-authored-by: omahs <73983677+omahs@users.noreply.github.com> Co-authored-by: Cameron Fairchild Co-authored-by: Cameron Fairchild Co-authored-by: surcyf123 <114649324+surcyf123@users.noreply.github.com> Co-authored-by: Alex <96243054+wildcommunist@users.noreply.github.com> --- CHANGELOG.md | 58 ++++ VERSION | 2 +- bittensor/__init__.py | 8 +- bittensor/axon.py | 25 ++ bittensor/{logging.py => btlogging.py} | 7 + bittensor/chain_data.py | 26 +- bittensor/cli.py | 3 + bittensor/commands/__init__.py | 3 +- bittensor/commands/delegates.py | 237 ++++++++++++- bittensor/commands/identity.py | 313 ++++++++++++++++++ bittensor/commands/inspect.py | 38 +++ bittensor/commands/list.py | 25 ++ bittensor/commands/metagraph.py | 44 +++ bittensor/commands/misc.py | 109 ++---- bittensor/commands/network.py | 181 +++++++++- bittensor/commands/overview.py | 37 +++ bittensor/commands/register.py | 94 ++++++ bittensor/commands/root.py | 99 ++++++ bittensor/commands/senate.py | 109 ++++++ bittensor/commands/stake.py | 53 +++ bittensor/commands/transfer.py | 22 ++ bittensor/commands/unstake.py | 26 ++ bittensor/commands/wallets.py | 173 ++++++++++ bittensor/config.py | 16 +- bittensor/dendrite.py | 2 +- bittensor/errors.py | 5 + bittensor/subtensor.py | 81 ++++- bittensor/utils/__init__.py | 3 +- bittensor/utils/wallet_utils.py | 66 +++- scripts/environments/README.md | 6 + scripts/environments/apple_m1_environment.yml | 92 ++--- 31 files changed, 1801 insertions(+), 162 deletions(-) rename bittensor/{logging.py => btlogging.py} (97%) create mode 100644 bittensor/commands/identity.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e606fd03c..39ed00949b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,63 @@ # Changelog +## 6.3.0 / 2023-11-16 + +## What's Changed +* (un)Staking multiple avoid tx limit by @camfairchild in https://github.com/opentensor/bittensor/pull/1244 +* additional logging for prometheus by @Eugene-hu in https://github.com/opentensor/bittensor/pull/1246 +* Dataset fix by @isabella618033 in https://github.com/opentensor/bittensor/pull/1249 +* Grab delegates details from GitHub by @camfairchild in https://github.com/opentensor/bittensor/pull/1245 +* Add raw spec for local test and new bins by @camfairchild in https://github.com/opentensor/bittensor/pull/1243 +* Fix list_delegates on non-archive nodes by @camfairchild in https://github.com/opentensor/bittensor/pull/1232 +* Blacklist fixes + depreciation of old signatures by @Eugene-hu in https://github.com/opentensor/bittensor/pull/1240 +* [BIT-636] Change u16 weight normalization to max-upscaling by @opentaco in https://github.com/opentensor/bittensor/pull/1241 +* remove duplicate command #1228 by @camfairchild in https://github.com/opentensor/bittensor/pull/1231 +* test_forward_priority_2nd_request_timeout fix by @isabella618033 in https://github.com/opentensor/bittensor/pull/1276 +* Remove btcli query and btcli set_weights by @camfairchild in https://github.com/opentensor/bittensor/pull/1144 +* Merge releases 4.0.0 and 4.0.1 back to staging by @camfairchild in https://github.com/opentensor/bittensor/pull/1306 +* Improve development workflow documentation by @quac88 in https://github.com/opentensor/bittensor/pull/1262 +* staging updates and fixes by @ifrit98 in https://github.com/opentensor/bittensor/pull/1540 +* Add root get_weights command to btcli by @Rubberbandits in https://github.com/opentensor/bittensor/pull/1536 +* Fix typo by @steffencruz in https://github.com/opentensor/bittensor/pull/1543 +* remove duplicated debug message in dendrite by @ifrit98 in https://github.com/opentensor/bittensor/pull/1544 +* Cli fix by @ifrit98 in https://github.com/opentensor/bittensor/pull/1541 +* update faucet helpstr by @ifrit98 in https://github.com/opentensor/bittensor/pull/1542 +* Added mechanism to sum all delegated tao by @shibshib in https://github.com/opentensor/bittensor/pull/1547 +* Dict hash fix by @ifrit98 in https://github.com/opentensor/bittensor/pull/1548 +* Release/6.1.0 by @ifrit98 in https://github.com/opentensor/bittensor/pull/1550 +* Merge master by @ifrit98 in https://github.com/opentensor/bittensor/pull/1552 +* Streaming fix by @ifrit98 in https://github.com/opentensor/bittensor/pull/1551 +* Fix typos by @omahs in https://github.com/opentensor/bittensor/pull/1553 +* Normalize weights in r get weights table by @camfairchild in https://github.com/opentensor/bittensor/pull/1556 +* Dendrite & Synapse updates and fixes by @ifrit98 in https://github.com/opentensor/bittensor/pull/1555 +* rm root flag in metagraph by @ifrit98 in https://github.com/opentensor/bittensor/pull/1558 +* Max Faucet Runs == 3 by @ifrit98 in https://github.com/opentensor/bittensor/pull/1560 +* replace unknown wallet params (chain mismatch) with key values by @ifrit98 in https://github.com/opentensor/bittensor/pull/1559 +* Remove PoW registration cli and associated extrinsic by @ifrit98 in https://github.com/opentensor/bittensor/pull/1557 +* Add btcli wallet balance by @ifrit98 in https://github.com/opentensor/bittensor/pull/1564 +* Dendrite fixes by @ifrit98 in https://github.com/opentensor/bittensor/pull/1561 +* Release/6.2.0 by @ifrit98 in https://github.com/opentensor/bittensor/pull/1567 +* Master into staging by @ifrit98 in https://github.com/opentensor/bittensor/pull/1570 +* adding logging.exception by @surcyf123 in https://github.com/opentensor/bittensor/pull/1569 +* Update network.py by @wildcommunist in https://github.com/opentensor/bittensor/pull/1568 +* Subtensor Registry by @Eugene-hu in https://github.com/opentensor/bittensor/pull/1562 +* add instructions for upgrading bittensor with outdated version check by @ifrit98 in https://github.com/opentensor/bittensor/pull/1571 +* Add identity commands to btcli by @ifrit98 in https://github.com/opentensor/bittensor/pull/1566 +* Add set_delegate_take command to btcli by @Rubberbandits in https://github.com/opentensor/bittensor/pull/1563 +* Subtensor archive by @ifrit98 in https://github.com/opentensor/bittensor/pull/1575 +* Bugfix/list delegates by @ifrit98 in https://github.com/opentensor/bittensor/pull/1577 +* don't return result twice in query() by @ifrit98 in https://github.com/opentensor/bittensor/pull/1574 +* rename logging.py so doesn't circ import by @ifrit98 in https://github.com/opentensor/bittensor/pull/1572 +* add AxonInfo._string() by @ifrit98 in https://github.com/opentensor/bittensor/pull/1565 +* don't print __is_set for recursive objects by @ifrit98 in https://github.com/opentensor/bittensor/pull/1573 + +## New Contributors +* @omahs made their first contribution in https://github.com/opentensor/bittensor/pull/1553 +* @surcyf123 made their first contribution in https://github.com/opentensor/bittensor/pull/1569 + +**Full Changelog**: https://github.com/opentensor/bittensor/compare/v6.0.1...v6.3.0 + + ## 6.2.0 / 2023-10-30 ## What's Changed diff --git a/VERSION b/VERSION index 4ac4fded49..e7e42a4b58 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.2.0 \ No newline at end of file +6.3.0 \ No newline at end of file diff --git a/bittensor/__init__.py b/bittensor/__init__.py index 3215ce9155..7d620f8e08 100644 --- a/bittensor/__init__.py +++ b/bittensor/__init__.py @@ -27,7 +27,7 @@ nest_asyncio.apply() # Bittensor code and protocol version. -__version__ = "6.2.0" +__version__ = "6.3.0" version_split = __version__.split(".") __version_as_int__ = ( (100 * int(version_split[0])) @@ -87,12 +87,14 @@ def debug(on: bool = True): # Wallet ss58 address length __ss58_address_length__ = 48 -__networks__ = ["local", "finney", "test"] +__networks__ = ["local", "finney", "test", "archive"] __finney_entrypoint__ = "wss://entrypoint-finney.opentensor.ai:443" __finney_test_entrypoint__ = "wss://test.finney.opentensor.ai:443/" +__archive_entrypoint__ = "wss://archive.chain.opentensor.ai:443/" + # Needs to use wss:// __bellagene_entrypoint__ = "wss://parachain.opentensor.ai:443" @@ -208,7 +210,7 @@ def debug(on: bool = True): from .chain_data import * from .subtensor import subtensor as subtensor from .cli import cli as cli, COMMANDS as ALL_COMMANDS -from .logging import logging as logging +from .btlogging import logging as logging from .metagraph import metagraph as metagraph from .threadpool import PriorityThreadPoolExecutor as PriorityThreadPoolExecutor diff --git a/bittensor/axon.py b/bittensor/axon.py index 8dbba96e93..acce173b9d 100644 --- a/bittensor/axon.py +++ b/bittensor/axon.py @@ -559,6 +559,12 @@ def check_config(cls, config: "bittensor.config"): config.axon.external_port > 1024 and config.axon.external_port < 65535 ), "External port must be in range [1024, 65535]" + def to_string(self): + """ + Provides a human-readable representation of the AxonInfo for this Axon. + """ + return self.info().to_string() + def __str__(self) -> str: """ Provides a human-readable representation of the Axon instance. @@ -729,9 +735,28 @@ async def dispatch( # Call the postprocess function response = await self.postprocess(synapse, response, start_time) + # Catch the error case where axon is not configured to handle the request. + except KeyError: + # Log key error. + bittensor.logging.error( + f"Key Error: Synapse name {request_name} not found." + ) + + # Create a synapse instance with status code 404 (not found) and status message. + synapse: bittensor.Synapse = bittensor.Synapse() + synapse.axon.status_code = "404" + synapse.axon.status_message = f"Synapse name {request_name} not found." + + # Create a JSON response with a status code of 404 (not found error), + # synapse headers, and an empty content. + response = JSONResponse( + status_code=404, headers=synapse.to_headers(), content={} + ) + # Start of catching all exceptions, updating the status message, and processing time. except Exception as e: # Log the exception for debugging purposes. + bittensor.logging.error(f"Exception: {str(e)}") bittensor.logging.trace(f"Forward exception: {traceback.format_exc()}") # Set the status message of the synapse to the string representation of the exception. diff --git a/bittensor/logging.py b/bittensor/btlogging.py similarity index 97% rename from bittensor/logging.py rename to bittensor/btlogging.py index 87aeb5d190..a42c55612e 100644 --- a/bittensor/logging.py +++ b/bittensor/btlogging.py @@ -291,3 +291,10 @@ def trace(cls, prefix: object, sufix: object = None): if not cls.__has_been_inited__: cls() logger.trace(cls._format(prefix, sufix)) + + @classmethod + def exception(cls, prefix: object, sufix: object = None): + """Exception logging with traceback""" + if not cls.__has_been_inited__: + cls() + logger.exception(cls._format(prefix, sufix)) diff --git a/bittensor/chain_data.py b/bittensor/chain_data.py index 33d465d915..3f44a66a01 100644 --- a/bittensor/chain_data.py +++ b/bittensor/chain_data.py @@ -14,12 +14,12 @@ # 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 torch import bittensor +import json from enum import Enum -from dataclasses import dataclass +from dataclasses import dataclass, asdict from scalecodec.types import GenericCall from typing import List, Tuple, Dict, Optional, Any, TypedDict, Union from scalecodec.base import RuntimeConfiguration, ScaleBytes @@ -227,6 +227,28 @@ def __str__(self): def __repr__(self): return self.__str__() + def to_string(self) -> str: + """Converts the AxonInfo object to a string representation using JSON.""" + try: + return json.dumps(asdict(self)) + except (TypeError, ValueError) as e: + bittensor.logging.error(f"Error converting AxonInfo to string: {e}") + return AxonInfo(0, "", 0, 0, "", "").to_string() + + @classmethod + def from_string(cls, s: str) -> "AxonInfo": + """Creates an AxonInfo object from its string representation using JSON.""" + try: + data = json.loads(s) + return cls(**data) + except json.JSONDecodeError as e: + bittensor.logging.error(f"Error decoding JSON: {e}") + except TypeError as e: + bittensor.logging.error(f"Type error: {e}") + except ValueError as e: + bittensor.logging.error(f"Value error: {e}") + return AxonInfo(0, "", 0, 0, "", "") + @classmethod def from_neuron_info(cls, neuron_info: dict) -> "AxonInfo": """Converts a dictionary to an axon_info object.""" diff --git a/bittensor/cli.py b/bittensor/cli.py index a7ee579624..521af44bd1 100644 --- a/bittensor/cli.py +++ b/bittensor/cli.py @@ -67,6 +67,7 @@ "weights": RootSetWeightsCommand, "get_weights": RootGetWeightsCommand, "senate_vote": VoteCommand, + "senate": SenateCommand, "register": RootRegisterCommand, "proposals": ProposalsCommand, "delegate": DelegateStakeCommand, @@ -94,6 +95,8 @@ "regen_hotkey": RegenHotkeyCommand, "faucet": RunFaucetCommand, "update": UpdateWalletCommand, + "set_identity": SetIdentityCommand, + "get_identity": GetIdentityCommand, }, }, "stake": { diff --git a/bittensor/commands/__init__.py b/bittensor/commands/__init__.py index 03822f9bf5..d0334bb8a8 100644 --- a/bittensor/commands/__init__.py +++ b/bittensor/commands/__init__.py @@ -87,7 +87,7 @@ from .inspect import InspectCommand from .metagraph import MetagraphCommand from .list import ListCommand -from .misc import UpdateCommand, ListSubnetsCommand +from .misc import UpdateCommand from .senate import ( SenateCommand, ProposalsCommand, @@ -110,3 +110,4 @@ RootSetWeightsCommand, RootGetWeightsCommand, ) +from .identity import GetIdentityCommand, SetIdentityCommand diff --git a/bittensor/commands/delegates.py b/bittensor/commands/delegates.py index 474d4d6fd6..9209741d29 100644 --- a/bittensor/commands/delegates.py +++ b/bittensor/commands/delegates.py @@ -53,7 +53,51 @@ def show_delegates( prev_delegates: Optional[List["bittensor.DelegateInfo"]], width: Optional[int] = None, ): - """Pretty prints a table of delegates sorted by total stake.""" + """ + Displays a formatted table of Bittensor network delegates with detailed statistics + to the console. The table is sorted by total stake in descending order and provides + a snapshot of delegate performance and status, helping users make informed decisions + for staking or nominating. + + This is a helper function that is called by the 'list_delegates' and 'my_delegates' + and not intended to be used directly in user code unless specifically required. + + Parameters: + - delegates (List[bittensor.DelegateInfo]): A list of delegate information objects + to be displayed. + - prev_delegates (Optional[List[bittensor.DelegateInfo]]): A list of delegate + information objects from a previous state, used to calculate changes in stake. + Defaults to None. + - width (Optional[int]): The width of the console output table. Defaults to None, + which will make the table expand to the maximum width of the console. + + The output table includes the following columns: + - INDEX: The numerical index of the delegate. + - DELEGATE: The name of the delegate. + - SS58: The truncated SS58 address of the delegate. + - NOMINATORS: The number of nominators supporting the delegate. + - DELEGATE STAKE(τ): The stake that is directly delegated to the delegate. + - TOTAL STAKE(τ): The total stake held by the delegate, including nominators' stake. + - CHANGE/(4h): The percentage change in the delegate's stake over the past 4 hours. + - SUBNETS: A list of subnets the delegate is registered with. + - VPERMIT: Validator permits held by the delegate for the subnets. + - NOMINATOR/(24h)/kτ: The earnings per 1000 τ staked by nominators in the last 24 hours. + - DELEGATE/(24h): The earnings of the delegate in the last 24 hours. + - Desc: A brief description provided by the delegate. + + Usage: + This function is typically used within the Bittensor CLI to show current delegate + options to users who are considering where to stake their tokens. + + Example usage: + >>> show_delegates(current_delegates, previous_delegates, width=80) + + Note: + This function is primarily for display purposes within a command-line interface and does + not return any values. It relies on the 'rich' Python library to render the table in the + console. + """ + delegates.sort(key=lambda delegate: delegate.total_stake, reverse=True) prev_delegates_dict = {} if prev_delegates is not None: @@ -101,17 +145,13 @@ def show_delegates( no_wrap=True, ) table.add_column("[overline white]CHANGE/(4h)", style="grey0", justify="center") - table.add_column( - "[overline white]SUBNETS", justify="right", style="white", no_wrap=True - ) - table.add_column("[overline white]VPERMIT", justify="right", no_wrap=True) - # table.add_column("[overline white]TAKE", style='white', no_wrap=True) + table.add_column("[overline white]VPERMIT", justify="right", no_wrap=False) + table.add_column("[overline white]TAKE", style="white", no_wrap=True) table.add_column( "[overline white]NOMINATOR/(24h)/k\u03C4", style="green", justify="center" ) table.add_column("[overline white]DELEGATE/(24h)", style="green", justify="center") table.add_column("[overline white]Desc", style="rgb(50,163,219)") - # table.add_column("[overline white]DESCRIPTION", style='white') for i, delegate in enumerate(delegates): owner_stake = next( @@ -166,22 +206,46 @@ def show_delegates( f"{delegate.total_stake!s:13.13}", rate_change_in_stake_str, str(delegate.registrations), - str( - [ - "*" if subnet in delegate.validator_permits else "" - for subnet in delegate.registrations - ] - ), - # f'{delegate.take * 100:.1f}%', + f"{delegate.take * 100:.1f}%", f"{bittensor.Balance.from_tao( delegate.total_daily_return.tao * (1000/ ( 0.001 + delegate.total_stake.tao ) ))!s:6.6}", f"{bittensor.Balance.from_tao( delegate.total_daily_return.tao * (0.18) ) !s:6.6}", - str(delegate_description) - # f'{delegate_profile.description:140.140}', + str(delegate_description), + end_section=True, ) bittensor.__console__.print(table) class DelegateStakeCommand: + """ + Executes the 'delegate' command, which stakes Tao to a specified delegate on the + Bittensor network. This action allocates the user's Tao to support a delegate, + potentially earning staking rewards in return. + + Optional Arguments: + - wallet.name: The name of the wallet to use for the command. + - delegate_ss58key: The SS58 address of the delegate to stake to. + - amount: The amount of Tao to stake. + - all: If specified, the command stakes all available Tao. + + The command interacts with the user to determine the delegate and the amount of Tao + to be staked. If the '--all' flag is used, it delegates the entire available balance. + + Usage: + The user must specify the delegate's SS58 address and the amount of Tao to stake. The + function sends a transaction to the subtensor network to delegate the specified amount + to the chosen delegate. These values are prompted if not provided. + + Example usage: + >>> btcli delegate --delegate_ss58key --amount + >>> btcli delegate --delegate_ss58key --all + + Note: + This command modifies the blockchain state and may incur transaction fees. It requires + user confirmation and interaction, and is designed to be used within the Bittensor CLI + environment. The user should ensure the delegate's address and the amount to be staked + are correct before executing the command. + """ + @staticmethod def run(cli): """Delegates stake to a chain delegate.""" @@ -279,6 +343,37 @@ def check_config(config: "bittensor.config"): class DelegateUnstakeCommand: + """ + Executes the 'undelegate' command, allowing users to withdraw their staked Tao from + a delegate on the Bittensor network. This process is known as "undelegating" and it + reverses the delegation process, freeing up the staked tokens. + + Optional Arguments: + - wallet.name: The name of the wallet to use for the command. + - delegate_ss58key: The SS58 address of the delegate to undelegate from. + - amount: The amount of Tao to undelegate. + - all: If specified, the command undelegates all staked Tao from the delegate. + + The command prompts the user for the amount of Tao to undelegate and the SS58 address + of the delegate from which to undelegate. If the '--all' flag is used, it will attempt + to undelegate the entire staked amount from the specified delegate. + + Usage: + The user must provide the delegate's SS58 address and the amount of Tao to undelegate. + The function will then send a transaction to the Bittensor network to process the + undelegation. + + Example usage: + >>> btcli undelegate --delegate_ss58key --amount + >>> btcli undelegate --delegate_ss58key --all + + Note: + This command can result in a change to the blockchain state and may incur transaction + fees. It is interactive and requires confirmation from the user before proceeding. It + should be used with care as undelegating can affect the delegate's total stake and + potentially the user's staking rewards. + """ + @staticmethod def run(cli): """Undelegates stake from a chain delegate.""" @@ -376,11 +471,50 @@ def check_config(config: "bittensor.config"): class ListDelegatesCommand: + """ + Displays a formatted table of Bittensor network delegates, providing a comprehensive + overview of delegate statistics and information. This table helps users make informed + decisions on which delegates to allocate their Tao stake. + + Optional Arguments: + - wallet.name: The name of the wallet to use for the command. + - subtensor.network: The name of the network to use for the command. + + The table columns include: + - INDEX: The delegate's index in the sorted list. + - DELEGATE: The name of the delegate. + - SS58: The delegate's unique SS58 address (truncated for display). + - NOMINATORS: The count of nominators backing the delegate. + - DELEGATE STAKE(τ): The amount of delegate's own stake (not the TAO delegated from any nominators). + - TOTAL STAKE(τ): The delegate's cumulative stake, including self-staked and nominators' stakes. + - CHANGE/(4h): The percentage change in the delegate's stake over the last four hours. + - SUBNETS: The subnets to which the delegate is registered. + - VPERMIT: Indicates the subnets for which the delegate has validator permits. + - NOMINATOR/(24h)/kτ: The earnings per 1000 τ staked by nominators in the last 24 hours. + - DELEGATE/(24h): The total earnings of the delegate in the last 24 hours. + - DESCRIPTION: A brief description of the delegate's purpose and operations. + + Sorting is done based on the 'TOTAL STAKE' column in descending order. Changes in stake + are highlighted: increases in green and decreases in red. Entries with no previous data + are marked with 'NA'. Each delegate's name is a hyperlink to their respective URL, if available. + + Example usage: + >>> btcli root list_delegates + >>> btcli root list_delegates --wallet.name my_wallet + >>> btcli root list_delegates --subtensor.network finney # can also be `test` or `local` + + Note: + This function is part of the Bittensor CLI tools and is intended for use within a console + application. It prints directly to the console and does not return any value. + """ + @staticmethod def run(cli): r""" List all delegates on the network. """ + cli.config.subtensor.network = "archive" + cli.config.subtensor.chain_endpoint = "wss://archive.chain.opentensor.ai:443" subtensor = bittensor.subtensor(config=cli.config) with bittensor.__console__.status(":satellite: Loading delegates..."): delegates: bittensor.DelegateInfo = subtensor.get_delegates() @@ -413,6 +547,36 @@ def check_config(config: "bittensor.config"): class NominateCommand: + """ + Executes the 'nominate' command, which facilitates a wallet to become a delegate + on the Bittensor network. This command handles the nomination process, including + wallet unlocking and verification of the hotkey's current delegate status. + + The command performs several checks: + - Verifies that the hotkey is not already a delegate to prevent redundant nominations. + - Tries to nominate the wallet and reports success or failure. + + Upon success, the wallet's hotkey is registered as a delegate on the network. + + Optional Arguments: + - wallet.name: The name of the wallet to use for the command. + - wallet.hotkey: The name of the hotkey to use for the command. + + Usage: + To run the command, the user must have a configured wallet with both hotkey and + coldkey. If the wallet is not already nominated, this command will initiate the + process. + + Example usage: + >>> btcli root nominate + >>> btcli root nominate --wallet.name my_wallet --wallet.hotkey my_hotkey + + Note: + This function is intended to be used as a CLI command. It prints the outcome directly + to the console and does not return any value. It should not be called programmatically + in user code due to its interactive nature and side effects on the network state. + """ + @staticmethod def run(cli): r"""Nominate wallet.""" @@ -475,6 +639,47 @@ def check_config(config: "bittensor.config"): class MyDelegatesCommand: + """ + Executes the 'my_delegates' command within the Bittensor CLI, which retrieves and + displays a table of delegated stakes from a user's wallet(s) to various delegates + on the Bittensor network. The command provides detailed insights into the user's + staking activities and the performance of their chosen delegates. + + Optional Arguments: + - wallet.name: The name of the wallet to use for the command. + - all: If specified, the command aggregates information across all wallets. + + The table output includes the following columns: + - Wallet: The name of the user's wallet. + - OWNER: The name of the delegate's owner. + - SS58: The truncated SS58 address of the delegate. + - Delegation: The amount of Tao staked by the user to the delegate. + - τ/24h: The earnings from the delegate to the user over the past 24 hours. + - NOMS: The number of nominators for the delegate. + - OWNER STAKE(τ): The stake amount owned by the delegate. + - TOTAL STAKE(τ): The total stake amount held by the delegate. + - SUBNETS: The list of subnets the delegate is a part of. + - VPERMIT: Validator permits held by the delegate for various subnets. + - 24h/kτ: Earnings per 1000 Tao staked over the last 24 hours. + - Desc: A description of the delegate. + + The command also sums and prints the total amount of Tao delegated across all wallets. + + Usage: + The command can be run as part of the Bittensor CLI suite of tools and requires + no parameters if a single wallet is used. If multiple wallets are present, the + --all flag can be specified to aggregate information across all wallets. + + Example usage: + >>> btcli my_delegates + >>> btcli my_delegates --all + >>> btcli my_delegates --wallet.name my_wallet + + Note: + This function is typically called by the CLI parser and is not intended to be used + directly in user code. + """ + @staticmethod def run(cli): """Delegates stake to a chain delegate.""" diff --git a/bittensor/commands/identity.py b/bittensor/commands/identity.py new file mode 100644 index 0000000000..867cff4cd5 --- /dev/null +++ b/bittensor/commands/identity.py @@ -0,0 +1,313 @@ +import argparse +from rich import print +from rich.table import Table +from rich.prompt import Prompt +from sys import getsizeof + +import bittensor + + +class SetIdentityCommand: + """ + Executes the 'set_identity' command within the Bittensor network, which allows for the + creation or update of a delegate's on-chain identity. This identity includes various + attributes such as display name, legal name, web URL, PGP fingerprint, and contact + information, among others. + + Optional Arguments: + --display: The display name for the identity. + --legal: The legal name for the identity. + --web: The web URL for the identity. + --riot: The riot handle for the identity. + --email: The email address for the identity. + --pgp_fingerprint: The PGP fingerprint for the identity. + --image: The image URL for the identity. + --info: The info for the identity. + --twitter: The twitter URL for the identity. + + The command prompts the user for the different identity attributes and validates the + input size for each attribute. It provides an option to update an existing validator + hotkey identity. If the user consents to the transaction cost, the identity is updated + on the blockchain. + + Each field has a maximum size of 64 bytes. The PGP fingerprint field is an exception + and has a maximum size of 20 bytes. The user is prompted to enter the PGP fingerprint + as a hex string, which is then converted to bytes. The user is also prompted to enter + the coldkey or hotkey ss58 address for the identity to be updated. If the user does + not have a hotkey, the coldkey address is used by default. + + If setting a validator identity, the hotkey will be used by default. If the user is + setting an identity for a subnet, the coldkey will be used by default. + + Usage: + The user should call this command from the command line and follow the interactive + prompts to enter or update the identity information. The command will display the + updated identity details in a table format upon successful execution. + + Example usage: + >>> btcli root set_identity + + Note: + This command should only be used if the user is willing to incur the 1 TAO transaction + fee associated with setting an identity on the blockchain. It is a high-level command + that makes changes to the blockchain state and should not be used programmatically as + part of other scripts or applications. + """ + + def run(cli): + r"""Create a new or update existing identity on-chain.""" + console = bittensor.__console__ + + wallet = bittensor.wallet(config=cli.config) + subtensor = bittensor.subtensor(config=cli.config) + + id_dict = { + "display": cli.config.display, + "legal": cli.config.legal, + "web": cli.config.web, + "pgp_fingerprint": cli.config.pgp_fingerprint, + "riot": cli.config.riot, + "email": cli.config.email, + "image": cli.config.image, + "twitter": cli.config.twitter, + "info": cli.config.info, + } + + for field, string in id_dict.items(): + if getsizeof(string) > 113: # 64 + 49 overhead bytes for string + raise ValueError(f"Identity value `{field}` must be <= 64 raw bytes") + + identified = ( + wallet.hotkey.ss58_address + if str( + Prompt.ask( + "Are you updating a validator hotkey identity?", + default="y", + choices=["y", "n"], + ) + ).lower() + == "y" + else None + ) + + if ( + str( + Prompt.ask( + "Cost to register an Identity is [bold white italic]1 Tao[/bold white italic], are you sure you wish to continue?", + default="n", + choices=["y", "n"], + ) + ).lower() + == "n" + ): + console.print(":cross_mark: Aborted!") + exit(0) + + wallet.coldkey # unlock coldkey + with console.status(":satellite: [bold green]Updating identity on-chain..."): + try: + subtensor.update_identity( + identified=identified, + wallet=wallet, + params=id_dict, + ) + except Exception as e: + console.print(f"[red]:cross_mark: Failed![/red] {e}") + exit(1) + + console.print(":white_heavy_check_mark: Success!") + + identity = subtensor.query_identity(identified or wallet.coldkey.ss58_address) + + table = Table(title="[bold white italic]Updated On-Chain Identity") + table.add_column("Key", justify="right", style="cyan", no_wrap=True) + table.add_column("Value", style="magenta") + + table.add_row("Address", identified or wallet.coldkey.ss58_address) + for key, value in identity.items(): + table.add_row(key, str(value) if value is not None else "None") + + console.print(table) + + @staticmethod + def check_config(config: "bittensor.config"): + if not config.is_set("wallet.name") and not config.no_prompt: + config.wallet.name = Prompt.ask( + "Enter wallet name", default=bittensor.defaults.wallet.name + ) + if not config.is_set("wallet.hotkey") and not config.no_prompt: + config.wallet.hotkey = Prompt.ask( + "Enter wallet hotkey", default=bittensor.defaults.wallet.hotkey + ) + if not config.is_set("subtensor.network") and not config.no_prompt: + config.subtensor.network = Prompt.ask( + "Enter subtensor network", + default=bittensor.defaults.subtensor.network, + choices=bittensor.__networks__, + ) + ( + _, + config.subtensor.chain_endpoint, + ) = bittensor.subtensor.determine_chain_endpoint_and_network( + config.subtensor.network + ) + if not config.is_set("display") and not config.no_prompt: + config.display = Prompt.ask("Enter display name", default="") + if not config.is_set("legal") and not config.no_prompt: + config.legal = Prompt.ask("Enter legal string", default="") + if not config.is_set("web") and not config.no_prompt: + config.web = Prompt.ask("Enter web url", default="") + if not config.is_set("pgp_fingerprint") and not config.no_prompt: + config.pgp_fingerprint = Prompt.ask( + "Enter pgp fingerprint (must be 20 bytes)", default=None + ) + if not config.is_set("riot") and not config.no_prompt: + config.riot = Prompt.ask("Enter riot", default="") + if not config.is_set("email") and not config.no_prompt: + config.email = Prompt.ask("Enter email address", default="") + if not config.is_set("image") and not config.no_prompt: + config.image = Prompt.ask("Enter image url", default="") + if not config.is_set("twitter") and not config.no_prompt: + config.twitter = Prompt.ask("Enter twitter url", default="") + if not config.is_set("info") and not config.no_prompt: + config.info = Prompt.ask("Enter info", default="") + + @staticmethod + def add_args(parser: argparse.ArgumentParser): + new_coldkey_parser = parser.add_parser( + "set_identity", + help="""Create or update identity on-chain for a given cold wallet. Must be a subnet owner.""", + ) + new_coldkey_parser.add_argument( + "--display", + type=str, + help="""The display name for the identity.""", + ) + new_coldkey_parser.add_argument( + "--legal", + type=str, + help="""The legal name for the identity.""", + ) + new_coldkey_parser.add_argument( + "--web", + type=str, + help="""The web url for the identity.""", + ) + new_coldkey_parser.add_argument( + "--riot", + type=str, + help="""The riot handle for the identity.""", + ) + new_coldkey_parser.add_argument( + "--email", + type=str, + help="""The email address for the identity.""", + ) + new_coldkey_parser.add_argument( + "--pgp_fingerprint", + type=str, + help="""The pgp fingerprint for the identity.""", + ) + new_coldkey_parser.add_argument( + "--image", + type=str, + help="""The image url for the identity.""", + ) + new_coldkey_parser.add_argument( + "--info", + type=str, + help="""The info for the identity.""", + ) + new_coldkey_parser.add_argument( + "--twitter", + type=str, + help="""The twitter url for the identity.""", + ) + bittensor.wallet.add_args(new_coldkey_parser) + bittensor.subtensor.add_args(new_coldkey_parser) + + +class GetIdentityCommand: + """ + Executes the 'get_identity' command, which retrieves and displays the identity details + of a user's coldkey or hotkey associated with the Bittensor network. This function + queries the subtensor chain for information such as the stake, rank, and trust associated + with the provided key. + + Optional Arguments: + --key: The ss58 address of the coldkey or hotkey to query. + + The command performs the following actions: + - Connects to the subtensor network and retrieves the identity information. + - Displays the information in a structured table format. + + The displayed table includes: + - Address: The ss58 address of the queried key. + - Item: Various attributes of the identity such as stake, rank, and trust. + - Value: The corresponding values of the attributes. + + Usage: + The user must provide an ss58 address as input to the command. If the address is not + provided in the configuration, the user is prompted to enter one. + + Example usage: + >>> btli get_identity --key + + Note: + This function is designed for CLI use and should be executed in a terminal. It is + primarily used for informational purposes and has no side effects on the network state. + """ + + def run(cli): + r"""Queries the subtensor chain for user identity.""" + console = bittensor.__console__ + + with console.status(":satellite: [bold green]Querying chain identity..."): + subtensor = bittensor.subtensor(config=cli.config) + identity = subtensor.query_identity(cli.config.key) + + table = Table(title="[bold white italic]On-Chain Identity") + table.add_column("Item", justify="right", style="cyan", no_wrap=True) + table.add_column("Value", style="magenta") + + table.add_row("Address", cli.config.key) + for key, value in identity.items(): + table.add_row(key, str(value) if value is not None else "None") + + console.print(table) + + @staticmethod + def check_config(config: "bittensor.config"): + if not config.is_set("key") and not config.no_prompt: + config.key = Prompt.ask( + "Enter coldkey or hotkey ss58 address", default=None + ) + if config.key is None: + raise ValueError("key must be set") + if not config.is_set("subtensor.network") and not config.no_prompt: + config.subtensor.network = Prompt.ask( + "Enter subtensor network", + default=bittensor.defaults.subtensor.network, + choices=bittensor.__networks__, + ) + ( + _, + config.subtensor.chain_endpoint, + ) = bittensor.subtensor.determine_chain_endpoint_and_network( + config.subtensor.network + ) + + @staticmethod + def add_args(parser: argparse.ArgumentParser): + new_coldkey_parser = parser.add_parser( + "get_identity", + help="""Creates a new coldkey (for containing balance) under the specified path. """, + ) + new_coldkey_parser.add_argument( + "--key", + type=str, + default=None, + help="""The coldkey or hotkey ss58 address to query.""", + ) + bittensor.wallet.add_args(new_coldkey_parser) + bittensor.subtensor.add_args(new_coldkey_parser) diff --git a/bittensor/commands/inspect.py b/bittensor/commands/inspect.py index 4b24a2da17..c39863bea6 100644 --- a/bittensor/commands/inspect.py +++ b/bittensor/commands/inspect.py @@ -64,6 +64,44 @@ def _get_hotkey_wallets_for_wallet(wallet) -> List["bittensor.wallet"]: class InspectCommand: + """ + Executes the 'inspect' command, which compiles and displays a detailed report of a user's + wallet pairs (coldkey, hotkey) on the Bittensor network. This report includes balance and + staking information for both the coldkey and hotkey associated with the wallet. + + Optional arguments: + -all: If set to True, the command will inspect all wallets located within the specified + path. If set to False, the command will inspect only the wallet specified by the user. + + The command gathers data on: + - Coldkey balance and delegated stakes. + - Hotkey stake and emissions per neuron on the network. + - Delegate names and details fetched from the network. + + The resulting table includes columns for: + - Coldkey: The coldkey associated with the user's wallet. + - Balance: The balance of the coldkey. + - Delegate: The name of the delegate to which the coldkey has staked funds. + - Stake: The amount of stake held by both the coldkey and hotkey. + - Emission: The emission or rewards earned from staking. + - Netuid: The network unique identifier of the subnet where the hotkey is active. + - Hotkey: The hotkey associated with the neuron on the network. + + Usage: + This command can be used to inspect a single wallet or all wallets located within a + specified path. It is useful for a comprehensive overview of a user's participation + and performance in the Bittensor network. + + Example usage: + >>> btcli inspect + >>> btcli inspect --all + + Note: + The 'inspect' command is for displaying information only and does not perform any + transactions or state changes on the Bittensor network. It is intended to be used as + part of the Bittensor CLI and not as a standalone function within user code. + """ + @staticmethod def run(cli): r"""Inspect a cold, hot pair.""" diff --git a/bittensor/commands/list.py b/bittensor/commands/list.py index 209ddf979d..1625d56375 100644 --- a/bittensor/commands/list.py +++ b/bittensor/commands/list.py @@ -25,6 +25,31 @@ class ListCommand: + """ + Executes the 'list' command which enumerates all wallets and their respective hotkeys + present in the user's Bittensor configuration directory. The command organizes the + information in a tree structure, displaying each wallet along with the SS58 addresses + for the coldkey public key and any hotkeys associated with it. + + Optional arguments: + -p, --path: The path to the Bittensor configuration directory. Defaults to '~/.bittensor'. + + The output is presented in a hierarchical tree format, with each wallet as a root node, + and any associated hotkeys as child nodes. The SS58 address is displayed for each + coldkey and hotkey that is not encrypted and exists on the device. + + Usage: + Upon invocation, the command scans the wallet directory and prints a list of all wallets, + indicating whether the public keys are available ('?' denotes unavailable or encrypted keys). + + Example usage: + >>> btcli list --path ~/.bittensor + + Note: + This command is read-only and does not modify the filesystem or the network state. It is + intended for use within the Bittensor CLI to provide a quick overview of the user's wallets. + """ + @staticmethod def run(cli): r"""Lists wallets.""" diff --git a/bittensor/commands/metagraph.py b/bittensor/commands/metagraph.py index 61b35695e1..4457bccd2f 100644 --- a/bittensor/commands/metagraph.py +++ b/bittensor/commands/metagraph.py @@ -25,6 +25,50 @@ class MetagraphCommand: + """ + Executes the 'metagraph' command to retrieve and display the entire metagraph + for a specified network. This metagraph contains detailed information about + all the neurons (nodes) participating in the network, including their stakes, + trust scores, and more. + + Optional arguments: + --netuid: The netuid of the network to query. Defaults to the default network UID. + --subtensor.network: The name of the network to query. Defaults to the default network name. + + The table displayed includes the following columns for each neuron: + - UID: Unique identifier of the neuron. + - STAKE(τ): Total stake of the neuron in Tau (τ). + - RANK: Rank score of the neuron. + - TRUST: Trust score assigned to the neuron by other neurons. + - CONSENSUS: Consensus score of the neuron. + - INCENTIVE: Incentive score representing the neuron's incentive alignment. + - DIVIDENDS: Dividends earned by the neuron. + - EMISSION(p): Emission in Rho (p) received by the neuron. + - VTRUST: Validator trust score indicating the network's trust in the neuron as a validator. + - VAL: Validator status of the neuron. + - UPDATED: Number of blocks since the neuron's last update. + - ACTIVE: Activity status of the neuron. + - AXON: Network endpoint information of the neuron. + - HOTKEY: Partial hotkey (public key) of the neuron. + - COLDKEY: Partial coldkey (public key) of the neuron. + + The command also prints network-wide statistics such as total stake, issuance, + and difficulty. + + Usage: + The user must specify the network UID to query the metagraph. If not specified, + the default network UID is used. + + Example usage: + >>> btcli metagraph --netuid 0 # Root network + >>> btcli metagraph --netuid 1 --subtensor.network test + + Note: + This command provides a snapshot of the network's state at the time of calling. + It is useful for network analysis and diagnostics. It is intended to be used as + part of the Bittensor CLI and not as a standalone function within user code. + """ + @staticmethod def run(cli): r"""Prints an entire metagraph.""" diff --git a/bittensor/commands/misc.py b/bittensor/commands/misc.py index 9d62581201..1b2ae156da 100644 --- a/bittensor/commands/misc.py +++ b/bittensor/commands/misc.py @@ -27,6 +27,25 @@ class UpdateCommand: + """ + Executes the 'update' command to update the local Bittensor package. This command performs a series of operations to ensure that the user's local Bittensor installation is updated to the latest version from the master branch of its GitHub repository. It primarily involves pulling the latest changes from the repository and reinstalling the package. + + Usage: + Upon invocation, the command first checks the user's configuration for the 'no_prompt' setting. If 'no_prompt' is set to True, or if the user explicitly confirms with 'Y' when prompted, the command proceeds to update the local Bittensor package. It changes the current directory to the Bittensor package directory, checks out the master branch of the Bittensor repository, pulls the latest changes, and then reinstalls the package using pip. + + The command structure is as follows: + 1. Change directory to the Bittensor package directory. + 2. Check out the master branch of the Bittensor GitHub repository. + 3. Pull the latest changes with the '--ff-only' option to ensure a fast-forward update. + 4. Reinstall the Bittensor package using pip. + + Example usage: + >>> btcli legacy update + + Note: + This command is intended to be used within the Bittensor CLI to facilitate easy updates of the Bittensor package. It should be used with caution as it directly affects the local installation of the package. It is recommended to ensure that any important data or configurations are backed up before running this command. + """ + @staticmethod def run(cli): if cli.config.no_prompt or cli.config.answer == "Y": @@ -52,93 +71,3 @@ def add_args(parser: argparse.ArgumentParser): ) bittensor.subtensor.add_args(update_parser) - - -class ListSubnetsCommand: - @staticmethod - def run(cli): - r"""List all subnet netuids in the network.""" - subtensor = bittensor.subtensor(config=cli.config) - subnets: List[bittensor.SubnetInfo] = subtensor.get_all_subnets_info() - - rows = [] - total_neurons = 0 - - for subnet in subnets: - total_neurons += subnet.max_n - # netuid, N, Max N, difficulty, network connect, tempo, emission, burn rate - rows.append( - ( - str(subnet.netuid), - str(subnet.subnetwork_n), - str(bittensor.utils.formatting.millify(subnet.max_n)), - str(bittensor.utils.formatting.millify(subnet.difficulty)), - str(subnet.tempo), - str( - [ - f"{cr[0]}: {cr[1] * 100:.1f}%" - for cr in subnet.connection_requirements.items() - ] - if len(subnet.connection_requirements) > 0 - else None - ), - f"{subnet.emission_value / bittensor.utils.RAOPERTAO * 100:0.2f}%", - f"{subnet.burn!s:8.8}", - f"{subnet.owner_ss58}", - ) - ) - - table = Table( - show_footer=True, - width=cli.config.get("width", None), - pad_edge=True, - box=None, - show_edge=True, - ) - table.title = "[white]Subnets - {}".format(subtensor.network) - # netuid, N, Max N, difficulty, network connect, tempo, emission, burn rate - table.add_column( - "[overline white]NETUID", - str(len(subnets)), - footer_style="overline white", - style="bold green", - justify="center", - ) - table.add_column( - "[overline white]NEURONS", - str(total_neurons), - footer_style="overline white", - style="white", - justify="center", - ) - table.add_column("[overline white]MAX_N", style="white", justify="center") - table.add_column("[overline white]DIFFICULTY", style="white", justify="center") - # table.add_column("[overline white]IMMUNITY", style='white') - # table.add_column("[overline white]BATCH SIZE", style='white') - # table.add_column("[overline white]SEQ_LEN", style='white') - table.add_column("[overline white]TEMPO", style="white", justify="center") - # table.add_column("[overline white]MODALITY", style='white') - table.add_column("[overline white]CON_REQ", style="white", justify="center") - # table.add_column("[overline white]STAKE", style="green", justify="center") - table.add_column( - "[overline white]EMISSION", style="white", justify="center" - ) # sums to 100% - table.add_column("[overline white]BURN(\u03C4)", style="white") - table.add_column("[overline white]OWNER(\u03C4)", style="white") - - for row in rows: - table.add_row(*row) - - bittensor.__console__.print(table) - - @staticmethod - def check_config(config: "bittensor.config"): - pass - - @staticmethod - def add_args(parser: argparse.ArgumentParser): - list_subnets_parser = parser.add_parser( - "list_subnets", help="""List all subnets on the network""" - ) - - bittensor.subtensor.add_args(list_subnets_parser) diff --git a/bittensor/commands/network.py b/bittensor/commands/network.py index f1a9a6f428..37507f7241 100644 --- a/bittensor/commands/network.py +++ b/bittensor/commands/network.py @@ -28,6 +28,31 @@ class RegisterSubnetworkCommand: + """ + Executes the 'register_subnetwork' command to register a new subnetwork on the Bittensor network. This command facilitates the creation and registration of a subnetwork, which involves interaction with the user's wallet and the Bittensor subtensor. It ensures that the user has the necessary credentials and configurations to successfully register a new subnetwork. + + Usage: + Upon invocation, the command performs several key steps to register a subnetwork: + 1. It copies the user's current configuration settings. + 2. It accesses the user's wallet using the provided configuration. + 3. It initializes the Bittensor subtensor object with the user's configuration. + 4. It then calls the `register_subnetwork` function of the subtensor object, passing the user's wallet and a prompt setting based on the user's configuration. + + If the user's configuration does not specify a wallet name and 'no_prompt' is not set, the command will prompt the user to enter a wallet name. This name is then used in the registration process. + + The command structure includes: + - Copying the user's configuration. + - Accessing and preparing the user's wallet. + - Initializing the Bittensor subtensor. + - Registering the subnetwork with the necessary credentials. + + Example usage: + >>> btcli subnets create + + Note: + This command is intended for advanced users of the Bittensor network who wish to contribute by adding new subnetworks. It requires a clear understanding of the network's functioning and the roles of subnetworks. Users should ensure that they have secured their wallet and are aware of the implications of adding a new subnetwork to the Bittensor ecosystem. + """ + @staticmethod def run(cli): r"""Register a subnetwork""" @@ -58,6 +83,32 @@ def add_args(cls, parser: argparse.ArgumentParser): class SubnetLockCostCommand: + """ + Executes the 'lock_cost' command to view the locking cost required for creating a new subnetwork on the Bittensor network. This command is designed to provide users with the current cost of registering a new subnetwork, which is a critical piece of information for anyone considering expanding the network's infrastructure. + + The current implementation anneals the cost of creating a subnet over a period of two days. If the cost is unappealing currently, check back in a day or two to see if it has reached an amenble level. + + Usage: + Upon invocation, the command performs the following operations: + 1. It copies the user's current Bittensor configuration. + 2. It initializes the Bittensor subtensor object with this configuration. + 3. It then retrieves the subnet lock cost using the `get_subnet_burn_cost()` method from the subtensor object. + 4. The cost is displayed to the user in a readable format, indicating the amount of cryptocurrency required to lock for registering a new subnetwork. + + In case of any errors during the process (e.g., network issues, configuration problems), the command will catch these exceptions and inform the user that it failed to retrieve the lock cost, along with the specific error encountered. + + The command structure includes: + - Copying and using the user's configuration for Bittensor. + - Retrieving the current subnet lock cost from the Bittensor network. + - Displaying the cost in a user-friendly manner. + + Example usage: + >>> btcli subnets lock_cost + + Note: + This command is particularly useful for users who are planning to contribute to the Bittensor network by adding new subnetworks. Understanding the lock cost is essential for these users to make informed decisions about their potential contributions and investments in the network. + """ + @staticmethod def run(cli): r"""View locking cost of creating a new subnetwork""" @@ -88,6 +139,38 @@ def add_args(cls, parser: argparse.ArgumentParser): class SubnetListCommand: + """ + Executes the 'list' command to list all subnets and their detailed information on the Bittensor network. + This command is designed to provide users with comprehensive information about each subnet within the + network, including its unique identifier (netuid), the number of neurons, maximum neuron capacity, + emission rate, tempo, recycle register cost (burn), proof of work (PoW) difficulty, and the name or + SS58 address of the subnet owner. + + Usage: + Upon invocation, the command performs the following actions: + 1. It initializes the Bittensor subtensor object with the user's configuration. + 2. It retrieves a list of all subnets in the network along with their detailed information. + 3. The command compiles this data into a table format, displaying key information about each subnet. + + In addition to the basic subnet details, the command also fetches delegate information to provide the + name of the subnet owner where available. If the owner's name is not available, the owner's SS58 + address is displayed. + + The command structure includes: + - Initializing the Bittensor subtensor and retrieving subnet information. + - Calculating the total number of neurons across all subnets. + - Constructing a table that includes columns for NETUID, N (current neurons), MAX_N (maximum neurons), + EMISSION, TEMPO, BURN, POW (proof of work difficulty), and SUDO (owner's name or SS58 address). + - Displaying the table with a footer that summarizes the total number of subnets and neurons. + + Example usage: + >>> btcli subnets list + + Note: + This command is particularly useful for users seeking an overview of the Bittensor network's structure + and the distribution of its resources and ownership information for each subnet. + """ + @staticmethod def run(cli): r"""List all subnet netuids in the network.""" @@ -161,7 +244,7 @@ def add_args(parser: argparse.ArgumentParser): HYPERPARAMS = { "serving_rate_limit": "sudo_set_serving_rate_limit", "weights_version": "sudo_set_weights_version_key", - "weights_rate_Limit": "sudo_set_weights_set_rate_limit", + "weights_rate_limit": "sudo_set_weights_set_rate_limit", "max_weight_limit": "sudo_set_max_weight_limit", "immunity_period": "sudo_set_immunity_period", "min_allowed_weights": "sudo_set_min_allowed_weights", @@ -171,6 +254,25 @@ def add_args(parser: argparse.ArgumentParser): class SubnetSudoCommand: + """ + Executes the 'set' command to set hyperparameters for a specific subnet on the Bittensor network. + This command allows subnet owners to modify various hyperparameters of theirs subnet, such as its tempo, + emission rates, and other network-specific settings. + + Usage: + The command first prompts the user to enter the hyperparameter they wish to change and its new value. + It then uses the user's wallet and configuration settings to authenticate and send the hyperparameter update + to the specified subnet. + + Example usage: + >>> btcli sudo set --netuid 1 --param 'tempo' --value '0.5' + + Note: + This command requires the user to specify the subnet identifier (netuid) and both the hyperparameter + and its new value. It is intended for advanced users who are familiar with the network's functioning + and the impact of changing these parameters. + """ + @staticmethod def run(cli): r"""Set subnet hyperparameters.""" @@ -217,6 +319,45 @@ def add_args(parser: argparse.ArgumentParser): class SubnetHyperparamsCommand: + """ + Executes the 'hyperparameters' command to view the current hyperparameters of a specific subnet on + the Bittensor network. This command is useful for users who wish to understand the configuration and + operational parameters of a particular subnet. + + Usage: + Upon invocation, the command fetches and displays a list of all hyperparameters for the specified subnet. + These include settings like tempo, emission rates, and other critical network parameters that define + the subnet's behavior. + + Example usage: + >>> btcli subnets hyperparameters --netuid 1 + + Subnet Hyperparameters - NETUID: 1 - finney + HYPERPARAMETER VALUE + rho 10 + kappa 32767 + immunity_period 7200 + min_allowed_weights 8 + max_weight_limit 455 + tempo 99 + min_difficulty 1000000000000000000 + max_difficulty 1000000000000000000 + weights_version 2013 + weights_rate_limit 100 + adjustment_interval 112 + activity_cutoff 5000 + registration_allowed True + target_regs_per_interval 2 + min_burn 1000000000 + max_burn 100000000000 + bonds_moving_avg 900000 + max_regs_per_block 1 + + Note: + The user must specify the subnet identifier (netuid) for which they want to view the hyperparameters. + This command is read-only and does not modify the network state or configurations. + """ + @staticmethod def run(cli): r"""View hyperparameters of a subnetwork.""" @@ -260,6 +401,44 @@ def add_args(parser: argparse.ArgumentParser): class SubnetGetHyperparamsCommand: + """ + Executes the 'get' command to retrieve the hyperparameters of a specific subnet on the Bittensor network. + This command is similar to the 'hyperparameters' command but may be used in different contexts within the CLI. + + Usage: + The command connects to the Bittensor network, queries the specified subnet, and returns a detailed list + of all its hyperparameters. This includes crucial operational parameters that determine the subnet's + performance and interaction within the network. + + Example usage: + >>> btcli sudo get --netuid 1 + + Subnet Hyperparameters - NETUID: 1 - finney + HYPERPARAMETER VALUE + rho 10 + kappa 32767 + immunity_period 7200 + min_allowed_weights 8 + max_weight_limit 455 + tempo 99 + min_difficulty 1000000000000000000 + max_difficulty 1000000000000000000 + weights_version 2013 + weights_rate_limit 100 + adjustment_interval 112 + activity_cutoff 5000 + registration_allowed True + target_regs_per_interval 2 + min_burn 1000000000 + max_burn 100000000000 + bonds_moving_avg 900000 + max_regs_per_block 1 + + Note: + Users need to provide the netuid of the subnet whose hyperparameters they wish to view. This command is + designed for informational purposes and does not alter any network settings or configurations. + """ + @staticmethod def run(cli): r"""View hyperparameters of a subnetwork.""" diff --git a/bittensor/commands/overview.py b/bittensor/commands/overview.py index 733ec81d7c..39fc465b07 100644 --- a/bittensor/commands/overview.py +++ b/bittensor/commands/overview.py @@ -36,6 +36,43 @@ class OverviewCommand: + """ + Executes the 'overview' command to present a detailed overview of the user's registered accounts on the Bittensor network. + This command compiles and displays comprehensive information about each neuron associated with the user's wallets, + including both hotkeys and coldkeys. It is especially useful for users managing multiple accounts or seeking a summary + of their network activities and stake distributions. + + Usage: + The command offers various options to customize the output. Users can filter the displayed data by specific netuids, + sort by different criteria, and choose to include all wallets in the user's configuration directory. The output is + presented in a tabular format with the following columns: + - COLDKEY: The SS58 address of the coldkey. + - HOTKEY: The SS58 address of the hotkey. + - UID: Unique identifier of the neuron. + - ACTIVE: Indicates if the neuron is active. + - STAKE(τ): Amount of stake in the neuron, in Tao. + - RANK: The rank of the neuron within the network. + - TRUST: Trust score of the neuron. + - CONSENSUS: Consensus score of the neuron. + - INCENTIVE: Incentive score of the neuron. + - DIVIDENDS: Dividends earned by the neuron. + - EMISSION(p): Emission received by the neuron, in Rho. + - VTRUST: Validator trust score of the neuron. + - VPERMIT: Indicates if the neuron has a validator permit. + - UPDATED: Time since last update. + - AXON: IP address and port of the neuron. + - HOTKEY_SS58: Human-readable representation of the hotkey. + + Example usage: + >>> btcli wallet overview + >>> btcli wallet overview --all --sort_by stake --sort_order descending + + Note: + This command is read-only and does not modify the network state or account configurations. It provides a quick and + comprehensive view of the user's network presence, making it ideal for monitoring account status, stake distribution, + and overall contribution to the Bittensor network. + """ + @staticmethod def run(cli: "bittensor.cli"): r"""Prints an overview for the wallet's colkey.""" diff --git a/bittensor/commands/register.py b/bittensor/commands/register.py index 32c7f97fc4..1db114a714 100644 --- a/bittensor/commands/register.py +++ b/bittensor/commands/register.py @@ -27,6 +27,35 @@ class RegisterCommand: + """ + Executes the 'register' command to register a neuron on the Bittensor network by recycling some TAO (the network's native token). + This command is used to add a new neuron to a specified subnet within the network, contributing to the decentralization and robustness of Bittensor. + + Usage: + Before registering, the command checks if the specified subnet exists and whether the user's balance is sufficient to cover the registration cost. + The registration cost is determined by the current recycle amount for the specified subnet. If the balance is insufficient or the subnet does not exist, + the command will exit with an appropriate error message. + + If the preconditions are met, and the user confirms the transaction (if 'no_prompt' is not set), the command proceeds to register the neuron by burning the required amount of TAO. + + The command structure includes: + - Verification of subnet existence. + - Checking the user's balance against the current recycle amount for the subnet. + - User confirmation prompt for proceeding with registration. + - Execution of the registration process. + + Columns Displayed in the Confirmation Prompt: + - Balance: The current balance of the user's wallet in TAO. + - Cost to Register: The required amount of TAO needed to register on the specified subnet. + + Example usage: + >>> btcli subnets register --netuid 1 + + Note: + This command is critical for users who wish to contribute a new neuron to the network. It requires careful consideration of the subnet selection and + an understanding of the registration costs. Users should ensure their wallet is sufficiently funded before attempting to register a neuron. + """ + @staticmethod def run(cli): r"""Register neuron by recycling some TAO.""" @@ -109,6 +138,39 @@ def check_config(config: "bittensor.config"): class PowRegisterCommand: + """ + Executes the 'pow_register' command to register a neuron on the Bittensor network using Proof of Work (PoW). + This method is an alternative registration process that leverages computational work for securing a neuron's place on the network. + + Usage: + The command starts by verifying the existence of the specified subnet. If the subnet does not exist, it terminates with an error message. + On successful verification, the PoW registration process is initiated, which requires solving computational puzzles. + + Optional arguments: + - --netuid (int): The netuid for the subnet on which to serve the neuron. Mandatory for specifying the target subnet. + - --pow_register.num_processes (int): The number of processors to use for PoW registration. Defaults to the system's default setting. + - --pow_register.update_interval (int): The number of nonces to process before checking for the next block during registration. + Affects the frequency of update checks. + - --pow_register.no_output_in_place (bool): When set, disables the output of registration statistics in place. Useful for cleaner logs. + - --pow_register.verbose (bool): Enables verbose output of registration statistics for detailed information. + - --pow_register.cuda.use_cuda (bool): Enables the use of CUDA for GPU-accelerated PoW calculations. Requires a CUDA-compatible GPU. + - --pow_register.cuda.no_cuda (bool): Disables the use of CUDA, defaulting to CPU-based calculations. + - --pow_register.cuda.dev_id (int): Specifies the CUDA device ID, useful for systems with multiple CUDA-compatible GPUs. + - --pow_register.cuda.TPB (int): Sets the number of Threads Per Block for CUDA operations, affecting the GPU calculation dynamics. + + The command also supports additional wallet and subtensor arguments, enabling further customization of the registration process. + + Example usage: + >>> btcli pow_register --netuid 1 --pow_register.num_processes 4 --cuda.use_cuda + + Note: + This command is suited for users with adequate computational resources to participate in PoW registration. It requires a sound understanding + of the network's operations and PoW mechanics. Users should ensure their systems meet the necessary hardware and software requirements, + particularly when opting for CUDA-based GPU acceleration. + + This command may be disabled according on the subnet owner's directive. For example, on netuid 1 this is permanently disabled. + """ + @staticmethod def run(cli): r"""Register neuron.""" @@ -261,6 +323,38 @@ def check_config(config: "bittensor.config"): class RunFaucetCommand: + """ + Executes the 'faucet' command to obtain test TAO tokens by performing Proof of Work (PoW). + This command is particularly useful for users who need test tokens for operations on the Bittensor testnet. + + Usage: + The command uses the PoW mechanism to validate the user's effort and rewards them with test TAO tokens. + It is typically used in testnet environments where real value transactions are not necessary. + + Optional arguments: + - --faucet.num_processes (int): Specifies the number of processors to use for the PoW operation. + A higher number of processors may increase the chances of successful computation. + - --faucet.update_interval (int): Sets the frequency of nonce processing before checking for the next block, + which impacts the PoW operation's responsiveness. + - --faucet.no_output_in_place (bool): When set, it disables in-place output of registration statistics for cleaner log visibility. + - --faucet.verbose (bool): Enables verbose output for detailed statistical information during the PoW process. + - --faucet.cuda.use_cuda (bool): Activates the use of CUDA for GPU acceleration in the PoW process, suitable for CUDA-compatible GPUs. + - --faucet.cuda.no_cuda (bool): Disables the use of CUDA, opting for CPU-based calculations. + - --faucet.cuda.dev_id (int[]): Allows selection of specific CUDA device IDs for the operation, useful in multi-GPU setups. + - --faucet.cuda.TPB (int): Determines the number of Threads Per Block for CUDA operations, affecting GPU calculation efficiency. + + These options provide flexibility in configuring the PoW process according to the user's hardware capabilities and preferences. + + Example usage: + >>> btcli wallet faucet --faucet.num_processes 4 --faucet.cuda.use_cuda + + Note: + This command is meant for use in testnet environments where users can experiment with the network without using real TAO tokens. + It's important for users to have the necessary hardware setup, especially when opting for CUDA-based GPU calculations. + + **THIS COMMAND IS CURRENTLY DISABLED!** + """ + @staticmethod def run(cli): r"""Register neuron.""" diff --git a/bittensor/commands/root.py b/bittensor/commands/root.py index 78c31fa3d7..0517f94628 100644 --- a/bittensor/commands/root.py +++ b/bittensor/commands/root.py @@ -33,6 +33,24 @@ class RootRegisterCommand: + """ + Executes the 'register' command to register a wallet to the root network of the Bittensor network. + This command is used to formally acknowledge a wallet's participation in the network's root layer. + + Usage: + The command registers the user's wallet with the root network, which is a crucial step for participating in network governance and other advanced functions. + + Optional arguments: + - None. The command primarily uses the wallet and subtensor configurations. + + Example usage: + >>> btcli root register + + Note: + This command is important for users seeking to engage deeply with the Bittensor network, particularly in aspects related to network governance and decision-making. + It is a straightforward process but requires the user to have an initialized and configured wallet. + """ + @staticmethod def run(cli): r"""Register to root network.""" @@ -62,6 +80,35 @@ def check_config(config: "bittensor.config"): class RootList: + """ + Executes the 'list' command to display the members of the root network on the Bittensor network. + This command provides an overview of the neurons that constitute the network's foundational layer. + + Usage: + Upon execution, the command fetches and lists the neurons in the root network, showing their unique identifiers (UIDs), names, addresses, stakes, + and whether they are part of the senate (network governance body). + + Optional arguments: + - None. The command uses the subtensor configuration to retrieve data. + + Example usage: + >>> btcli root list + + UID NAME ADDRESS STAKE(τ) SENATOR + 0 5CaCUPsSSdKWcMJbmdmJdnWVa15fJQuz5HsSGgVdZffpHAUa 27086.37070 Yes + 1 RaoK9 5GmaAk7frPXnAxjbQvXcoEzMGZfkrDee76eGmKoB3wxUburE 520.24199 No + 2 Openτensor Foundaτion 5F4tQyWrhfGVcNhoqeiNsR6KjD4wMZ2kfhLj4oHYuyHbZAc3 1275437.45895 Yes + 3 RoundTable21 5FFApaS75bv5pJHfAp2FVLBj9ZaXuFDjEypsaBNc1wCfe52v 84718.42095 Yes + 4 5HK5tp6t2S59DywmHRWPBVJeJ86T61KjurYqeooqj8sREpeN 168897.40859 Yes + 5 Rizzo 5CXRfP2ekFhe62r7q3vppRajJmGhTi7vwvb2yr79jveZ282w 53383.34400 No + 6 τaosτaτs and BitAPAI 5Hddm3iBFD2GLT5ik7LZnT3XJUnRnN8PoeCFgGQgawUVKNm8 646944.73569 Yes + ... + + Note: + This command is useful for users interested in understanding the composition and governance structure of the Bittensor network's root layer. + It provides insights into which neurons hold significant influence and responsibility within the network. + """ + @staticmethod def run(cli): r"""List the root network""" @@ -144,6 +191,26 @@ def check_config(config: "bittensor.config"): class RootSetWeightsCommand: + """ + Executes the 'weights' command to set the weights for the root network on the Bittensor network. + This command is used by network senators to influence the distribution of network rewards and responsibilities. + + Usage: + The command allows setting weights for different subnets within the root network. + Users need to specify the netuids (network unique identifiers) and corresponding weights they wish to assign. + + Optional arguments: + - --netuids (str): A comma-separated list of netuids for which weights are to be set. + - --weights (str): Corresponding weights for the specified netuids, in comma-separated format. + + Example usage: + >>> btcli root weights --netuids 1,2,3 --weights 0.3,0.3,0.4 + + Note: + This command is particularly important for network senators and requires a comprehensive understanding of the network's dynamics. + It is a powerful tool that directly impacts the network's operational mechanics and reward distribution. + """ + @staticmethod def run(cli): r"""Set weights for root network.""" @@ -214,6 +281,38 @@ def check_config(config: "bittensor.config"): class RootGetWeightsCommand: + """ + Executes the 'get_weights' command to retrieve the weights set for the root network on the Bittensor network. + This command provides visibility into how network responsibilities and rewards are distributed among various subnets. + + Usage: + The command outputs a table listing the weights assigned to each subnet within the root network. + This information is crucial for understanding the current influence and reward distribution among the subnets. + + Optional arguments: + - None. The command fetches weight information based on the subtensor configuration. + + Example usage: + >>> btcli root get_weights + + Root Network Weights + UID 0 1 2 3 4 5 8 9 11 13 18 19 + 1 100.00% - - - - - - - - - - - + 2 - 40.00% 5.00% 10.00% 10.00% 10.00% 10.00% 5.00% - - 10.00% - + 3 - - 25.00% - 25.00% - 25.00% - - - 25.00% - + 4 - - 7.00% 7.00% 20.00% 20.00% 20.00% - 6.00% - 20.00% - + 5 - 20.00% - 10.00% 15.00% 15.00% 15.00% 5.00% - - 10.00% 10.00% + 6 - - - - 10.00% 10.00% 25.00% 25.00% - - 30.00% - + 7 - 60.00% - - 20.00% - - - 20.00% - - - + 8 - 49.35% - 7.18% 13.59% 21.14% 1.53% 0.12% 7.06% 0.03% - - + 9 100.00% - - - - - - - - - - - + ... + + Note: + This command is essential for users interested in the governance and operational dynamics of the Bittensor network. + It offers transparency into how network rewards and responsibilities are allocated across different subnets. + """ + @staticmethod def run(cli): r"""Get weights for root network.""" diff --git a/bittensor/commands/senate.py b/bittensor/commands/senate.py index ba4165ce47..1e283dea60 100644 --- a/bittensor/commands/senate.py +++ b/bittensor/commands/senate.py @@ -20,6 +20,7 @@ import bittensor from rich.prompt import Prompt, Confirm from rich.table import Table +from rich.console import Console from typing import List, Union, Optional, Dict, Tuple from .utils import get_delegates_details, DelegatesDetails from . import defaults @@ -28,9 +29,27 @@ class SenateCommand: + """ + Executes the 'senate' command to view the members of Bittensor's governance protocol, known as the Senate. + This command lists the delegates involved in the decision-making process of the Bittensor network. + + Usage: + The command retrieves and displays a list of Senate members, showing their names and wallet addresses. + This information is crucial for understanding who holds governance roles within the network. + + Example usage: + >>> btcli root senate + + Note: + This command is particularly useful for users interested in the governance structure and participants of the Bittensor network. + It provides transparency into the network's decision-making body. + """ + @staticmethod def run(cli): r"""View Bittensor's governance protocol proposals""" + console = bittensor.__console__ + config = cli.config.copy() subtensor: bittensor.subtensor = bittensor.subtensor(config=config) @@ -136,9 +155,27 @@ def display_votes( class ProposalsCommand: + """ + Executes the 'proposals' command to view active proposals within Bittensor's governance protocol. + This command displays the details of ongoing proposals, including votes, thresholds, and proposal data. + + Usage: + The command lists all active proposals, showing their hash, voting threshold, number of ayes and nays, + detailed votes by address, end block number, and call data associated with each proposal. + + Example usage: + >>> btcli root proposals + + Note: + This command is essential for users who are actively participating in or monitoring the governance of the Bittensor network. + It provides a detailed view of the proposals being considered, along with the community's response to each. + """ + @staticmethod def run(cli): r"""View Bittensor's governance protocol proposals""" + console = bittensor.__console__ + config = cli.config.copy() subtensor: bittensor.subtensor = bittensor.subtensor(config=config) @@ -222,6 +259,27 @@ def add_args(cls, parser: argparse.ArgumentParser): class ShowVotesCommand: + """ + Executes the 'proposal_votes' command to view the votes for a specific proposal in Bittensor's governance protocol. + This command provides a detailed breakdown of the votes cast by the senators for a particular proposal. + + Usage: + Users need to specify the hash of the proposal they are interested in. The command then displays the voting addresses + and their respective votes (Aye or Nay) for the specified proposal. + + Optional arguments: + - --proposal (str): The hash of the proposal for which votes need to be displayed. + + Example usage: + >>> btcli root proposal_votes --proposal + + Note: + This command is crucial for users seeking detailed insights into the voting behavior of the Senate on specific governance proposals. + It helps in understanding the level of consensus or disagreement within the Senate on key decisions. + + **THIS COMMAND IS DEPRECATED: Please use `btcli root proposals` to see vote status.** + """ + @staticmethod def run(cli): r"""View Bittensor's governance protocol proposals active votes""" @@ -297,6 +355,22 @@ def add_args(cls, parser: argparse.ArgumentParser): class SenateRegisterCommand: + """ + Executes the 'senate_register' command to register as a member of the Senate in Bittensor's governance protocol. + This command is used by delegates who wish to participate in the governance and decision-making process of the network. + + Usage: + The command checks if the user's hotkey is a delegate and not already a Senate member before registering them to the Senate. + Successful execution allows the user to participate in proposal voting and other governance activities. + + Example usage: + >>> btcli root senate_register + + Note: + This command is intended for delegates who are interested in actively participating in the governance of the Bittensor network. + It is a significant step towards engaging in network decision-making processes. + """ + @staticmethod def run(cli): r"""Register to participate in Bittensor's governance protocol proposals""" @@ -349,6 +423,22 @@ def add_args(cls, parser: argparse.ArgumentParser): class SenateLeaveCommand: + """ + Executes the 'senate_leave' command to discard membership in Bittensor's Senate. + This command allows a Senate member to voluntarily leave the governance body. + + Usage: + The command checks if the user's hotkey is currently a Senate member before processing the request to leave the Senate. + It effectively removes the user from participating in future governance decisions. + + Example usage: + >>> btcli root senate_leave + + Note: + This command is relevant for Senate members who wish to step down from their governance responsibilities within the Bittensor network. + It should be used when a member no longer desires to participate in the Senate activities. + """ + @staticmethod def run(cli): r"""Discard membership in Bittensor's governance protocol proposals""" @@ -392,6 +482,25 @@ def add_args(cls, parser: argparse.ArgumentParser): class VoteCommand: + """ + Executes the 'senate_vote' command to cast a vote on an active proposal in Bittensor's governance protocol. + This command is used by Senate members to vote on various proposals that shape the network's future. + + Usage: + The user needs to specify the hash of the proposal they want to vote on. The command then allows the Senate member + to cast an 'Aye' or 'Nay' vote, contributing to the decision-making process. + + Optional arguments: + - --proposal (str): The hash of the proposal to vote on. + + Example usage: + >>> btcli root senate_vote --proposal + + Note: + This command is crucial for Senate members to exercise their voting rights on key proposals. It plays a vital role in the governance + and evolution of the Bittensor network. + """ + @staticmethod def run(cli): r"""Vote in Bittensor's governance protocol proposals""" diff --git a/bittensor/commands/stake.py b/bittensor/commands/stake.py index acf5f76a49..51f54afe97 100644 --- a/bittensor/commands/stake.py +++ b/bittensor/commands/stake.py @@ -29,6 +29,33 @@ class StakeCommand: + """ + Executes the 'add' command to stake tokens to one or more hotkeys from a user's coldkey on the Bittensor network. + This command is used to allocate tokens to different hotkeys, securing their position and influence on the network. + + Usage: + Users can specify the amount to stake, the hotkeys to stake to (either by name or SS58 address), + and whether to stake to all hotkeys. The command checks for sufficient balance and hotkey registration + before proceeding with the staking process. + + Optional arguments: + - --all (bool): When set, stakes all available tokens from the coldkey. + - --uid (int): The unique identifier of the neuron to which the stake is to be added. + - --amount (float): The amount of TAO tokens to stake. + - --max_stake (float): Sets the maximum amount of TAO to have staked in each hotkey. + - --hotkeys (list): Specifies hotkeys by name or SS58 address to stake to. + - --all_hotkeys (bool): When set, stakes to all hotkeys associated with the wallet, excluding any specified in --hotkeys. + + The command prompts for confirmation before executing the staking operation. + + Example usage: + >>> btcli stake add --amount 100 --wallet.name --wallet.hotkey + + Note: + This command is critical for users who wish to distribute their stakes among different neurons (hotkeys) on the network. + It allows for a strategic allocation of tokens to enhance network participation and influence. + """ + @staticmethod def run(cli): r"""Stake token of amount to hotkey(s).""" @@ -302,6 +329,32 @@ def _get_hotkey_wallets_for_wallet(wallet) -> List["bittensor.wallet"]: class StakeShow: + """ + Executes the 'show' command to list all stake accounts associated with a user's wallet on the Bittensor network. + This command provides a comprehensive view of the stakes associated with both hotkeys and delegates linked to the user's coldkey. + + Usage: + The command lists all stake accounts for a specified wallet or all wallets in the user's configuration directory. + It displays the coldkey, balance, account details (hotkey/delegate name), stake amount, and the rate of return. + + Optional arguments: + - --all (bool): When set, the command checks all coldkey wallets instead of just the specified wallet. + + The command compiles a table showing: + - Coldkey: The coldkey associated with the wallet. + - Balance: The balance of the coldkey. + - Account: The name of the hotkey or delegate. + - Stake: The amount of TAO staked to the hotkey or delegate. + - Rate: The rate of return on the stake, typically shown in TAO per day. + + Example usage: + >>> btcli stake show --all + + Note: + This command is essential for users who wish to monitor their stake distribution and returns across various accounts on the Bittensor network. + It provides a clear and detailed overview of the user's staking activities. + """ + @staticmethod def run(cli): r"""Show all stake accounts.""" diff --git a/bittensor/commands/transfer.py b/bittensor/commands/transfer.py index b0c51486f0..9735d81577 100644 --- a/bittensor/commands/transfer.py +++ b/bittensor/commands/transfer.py @@ -26,6 +26,28 @@ class TransferCommand: + """ + Executes the 'transfer' command to transfer TAO tokens from one account to another on the Bittensor network. + This command is used for transactions between different accounts, enabling users to send tokens to other participants on the network. + + Usage: + The command requires specifying the destination address (public key) and the amount of TAO to be transferred. + It checks for sufficient balance and prompts for confirmation before proceeding with the transaction. + + Optional arguments: + - --dest (str): The destination address for the transfer. This can be in the form of an SS58 or ed2519 public key. + - --amount (float): The amount of TAO tokens to transfer. + + The command displays the user's current balance before prompting for the amount to transfer, ensuring transparency and accuracy in the transaction. + + Example usage: + >>> btcli wallet transfer --dest 5Dp8... --amount 100 + + Note: + This command is crucial for executing token transfers within the Bittensor network. Users should verify the destination address and amount + before confirming the transaction to avoid errors or loss of funds. + """ + @staticmethod def run(cli): r"""Transfer token of amount to destination.""" diff --git a/bittensor/commands/unstake.py b/bittensor/commands/unstake.py index f7e8d41201..4a0bafe139 100644 --- a/bittensor/commands/unstake.py +++ b/bittensor/commands/unstake.py @@ -28,6 +28,32 @@ class UnStakeCommand: + """ + Executes the 'remove' command to unstake TAO tokens from one or more hotkeys and transfer them back to the user's coldkey on the Bittensor network. + This command is used to withdraw tokens previously staked to different hotkeys. + + Usage: + Users can specify the amount to unstake, the hotkeys to unstake from (either by name or SS58 address), + and whether to unstake from all hotkeys. The command checks for sufficient stake and prompts for confirmation before proceeding with the unstaking process. + + Optional arguments: + - --all (bool): When set, unstakes all staked tokens from the specified hotkeys. + - --amount (float): The amount of TAO tokens to unstake. + - --hotkey_ss58address (str): The SS58 address of the hotkey to unstake from. + - --max_stake (float): Sets the maximum amount of TAO to remain staked in each hotkey. + - --hotkeys (list): Specifies hotkeys by name or SS58 address to unstake from. + - --all_hotkeys (bool): When set, unstakes from all hotkeys associated with the wallet, excluding any specified in --hotkeys. + + The command prompts for confirmation before executing the unstaking operation. + + Example usage: + >>> btcli stake remove --amount 100 --hotkeys hk1,hk2 + + Note: + This command is important for users who wish to reallocate their stakes or withdraw them from the network. + It allows for flexible management of token stakes across different neurons (hotkeys) on the network. + """ + @classmethod def check_config(cls, config: "bittensor.config"): if not config.is_set("wallet.name") and not config.no_prompt: diff --git a/bittensor/commands/wallets.py b/bittensor/commands/wallets.py index 9e892b5524..30405f7d12 100644 --- a/bittensor/commands/wallets.py +++ b/bittensor/commands/wallets.py @@ -26,6 +26,30 @@ class RegenColdkeyCommand: + """ + Executes the 'regen_coldkey' command to regenerate a coldkey for a wallet on the Bittensor network. + This command is used to create a new coldkey from an existing mnemonic, seed, or JSON file. + + Usage: + Users can specify a mnemonic, a seed string, or a JSON file path to regenerate a coldkey. + The command supports optional password protection for the generated key and can overwrite an existing coldkey. + + Optional arguments: + - --mnemonic (str): A mnemonic phrase used to regenerate the key. + - --seed (str): A seed hex string used for key regeneration. + - --json (str): Path to a JSON file containing an encrypted key backup. + - --json_password (str): Password to decrypt the JSON file. + - --use_password (bool): Enables password protection for the generated key. + - --overwrite_coldkey (bool): Overwrites the existing coldkey with the new one. + + Example usage: + >>> btcli wallet regen_coldkey --mnemonic "word1 word2 ... word12" + + Note: + This command is critical for users who need to regenerate their coldkey, possibly for recovery or security reasons. + It should be used with caution to avoid overwriting existing keys unintentionally. + """ + def run(cli): r"""Creates a new coldkey under this wallet.""" wallet = bittensor.wallet(config=cli.config) @@ -126,6 +150,27 @@ def add_args(parser: argparse.ArgumentParser): class RegenColdkeypubCommand: + """ + Executes the 'regen_coldkeypub' command to regenerate the public part of a coldkey (coldkeypub) for a wallet on the Bittensor network. + This command is used when a user needs to recreate their coldkeypub from an existing public key or SS58 address. + + Usage: + The command requires either a public key in hexadecimal format or an SS58 address to regenerate the coldkeypub. + It optionally allows overwriting an existing coldkeypub file. + + Optional arguments: + - --public_key_hex (str): The public key in hex format. + - --ss58_address (str): The SS58 address of the coldkey. + - --overwrite_coldkeypub (bool): Overwrites the existing coldkeypub file with the new one. + + Example usage: + >>> btcli wallet regen_coldkeypub --ss58_address 5DkQ4... + + Note: + This command is particularly useful for users who need to regenerate their coldkeypub, perhaps due to file corruption or loss. + It is a recovery-focused utility that ensures continued access to wallet functionalities. + """ + def run(cli): r"""Creates a new coldkeypub under this wallet.""" wallet = bittensor.wallet(config=cli.config) @@ -191,6 +236,31 @@ def add_args(parser: argparse.ArgumentParser): class RegenHotkeyCommand: + """ + Executes the 'regen_hotkey' command to regenerate a hotkey for a wallet on the Bittensor network. + Similar to regenerating a coldkey, this command creates a new hotkey from a mnemonic, seed, or JSON file. + + Usage: + Users can provide a mnemonic, seed string, or a JSON file to regenerate the hotkey. + The command supports optional password protection and can overwrite an existing hotkey. + + Optional arguments: + - --mnemonic (str): A mnemonic phrase used to regenerate the key. + - --seed (str): A seed hex string used for key regeneration. + - --json (str): Path to a JSON file containing an encrypted key backup. + - --json_password (str): Password to decrypt the JSON file. + - --use_password (bool): Enables password protection for the generated key. + - --overwrite_hotkey (bool): Overwrites the existing hotkey with the new one. + + Example usage: + >>> btcli wallet regen_hotkey + >>> btcli wallet regen_hotkey --seed 0x1234... + + Note: + This command is essential for users who need to regenerate their hotkey, possibly for security upgrades or key recovery. + It should be used cautiously to avoid accidental overwrites of existing keys. + """ + def run(cli): r"""Creates a new coldkey under this wallet.""" wallet = bittensor.wallet(config=cli.config) @@ -296,6 +366,27 @@ def add_args(parser: argparse.ArgumentParser): class NewHotkeyCommand: + """ + Executes the 'new_hotkey' command to create a new hotkey under a wallet on the Bittensor network. + This command is used to generate a new hotkey for managing a neuron or participating in the network. + + Usage: + The command creates a new hotkey with an optional word count for the mnemonic and supports password protection. + It also allows overwriting an existing hotkey. + + Optional arguments: + - --n_words (int): The number of words in the mnemonic phrase. + - --use_password (bool): Enables password protection for the generated key. + - --overwrite_hotkey (bool): Overwrites the existing hotkey with the new one. + + Example usage: + >>> btcli wallet new_hotkey --n_words 24 + + Note: + This command is useful for users who wish to create additional hotkeys for different purposes, + such as running multiple miners or separating operational roles within the network. + """ + def run(cli): """Creates a new hotke under this wallet.""" wallet = bittensor.wallet(config=cli.config) @@ -352,6 +443,27 @@ def add_args(parser: argparse.ArgumentParser): class NewColdkeyCommand: + """ + Executes the 'new_coldkey' command to create a new coldkey under a wallet on the Bittensor network. + This command generates a coldkey, which is essential for holding balances and performing high-value transactions. + + Usage: + The command creates a new coldkey with an optional word count for the mnemonic and supports password protection. + It also allows overwriting an existing coldkey. + + Optional arguments: + - --n_words (int): The number of words in the mnemonic phrase. + - --use_password (bool): Enables password protection for the generated key. + - --overwrite_coldkey (bool): Overwrites the existing coldkey with the new one. + + Example usage: + >>> btcli wallet new_coldkey --n_words 15 + + Note: + This command is crucial for users who need to create a new coldkey for enhanced security or as part of setting up a new wallet. + It's a foundational step in establishing a secure presence on the Bittensor network. + """ + def run(cli): r"""Creates a new coldkey under this wallet.""" wallet = bittensor.wallet(config=cli.config) @@ -404,6 +516,28 @@ def add_args(parser: argparse.ArgumentParser): class WalletCreateCommand: + """ + Executes the 'create' command to generate both a new coldkey and hotkey under a specified wallet on the Bittensor network. + This command is a comprehensive utility for creating a complete wallet setup with both cold and hotkeys. + + Usage: + The command facilitates the creation of a new coldkey and hotkey with an optional word count for the mnemonics. + It supports password protection for the coldkey and allows overwriting of existing keys. + + Optional arguments: + - --n_words (int): The number of words in the mnemonic phrase for both keys. + - --use_password (bool): Enables password protection for the coldkey. + - --overwrite_coldkey (bool): Overwrites the existing coldkey with the new one. + - --overwrite_hotkey (bool): Overwrites the existing hotkey with the new one. + + Example usage: + >>> btcli wallet create --n_words 21 + + Note: + This command is ideal for new users setting up their wallet for the first time or for those who wish to completely renew their wallet keys. + It ensures a fresh start with new keys for secure and effective participation in the network. + """ + def run(cli): r"""Creates a new coldkey and hotkey under this wallet.""" wallet = bittensor.wallet(config=cli.config) @@ -481,6 +615,26 @@ def _get_coldkey_wallets_for_path(path: str) -> List["bittensor.wallet"]: class UpdateWalletCommand: + """ + Executes the 'update' command to check and potentially update the security of the wallets in the Bittensor network. + This command is used to enhance wallet security using modern encryption standards. + + Usage: + The command checks if any of the wallets need an update in their security protocols. + It supports updating all legacy wallets or a specific one based on the user's choice. + + Optional arguments: + - --all (bool): When set, updates all legacy wallets. + - --no_prompt (bool): Disables user prompting during the update process. + + Example usage: + >>> btcli wallet update --all + + Note: + This command is important for maintaining the highest security standards for users' wallets. + It is recommended to run this command periodically to ensure wallets are up-to-date with the latest security practices. + """ + @staticmethod def run(cli): """Check if any of the wallets needs an update.""" @@ -554,6 +708,25 @@ def list_coldkeypub_files(dir_path): class WalletBalanceCommand: + """ + Executes the 'balance' command to check the balance of the wallet on the Bittensor network. + This command provides a detailed view of the wallet's coldkey balances, including free and staked balances. + + Usage: + The command lists the balances of all wallets in the user's configuration directory, showing the wallet name, + coldkey address, and the respective free and staked balances. + + Optional arguments: + None. The command uses the wallet and subtensor configurations to fetch balance data. + + Example usage: + >>> btcli wallet balance + + Note: + This command is essential for users to monitor their financial status on the Bittensor network. + It helps in keeping track of assets and ensuring the wallet's financial health. + """ + @staticmethod def run(cli): """Check the balance of the wallet.""" diff --git a/bittensor/config.py b/bittensor/config.py index b402b30ddd..8fea624830 100644 --- a/bittensor/config.py +++ b/bittensor/config.py @@ -287,12 +287,24 @@ def __deepcopy__(self, memo) -> "config": def __repr__(self) -> str: return self.__str__() + @staticmethod + def _remove_private_keys(d): + if "__parser" in d: + d.pop("__parser", None) + if "__is_set" in d: + d.pop("__is_set", None) + for k, v in list(d.items()): + if isinstance(v, dict): + config._remove_private_keys(v) + return d + def __str__(self) -> str: # remove the parser and is_set map from the visible config - visible = self.toDict() + visible = copy.deepcopy(self.toDict()) visible.pop("__parser", None) visible.pop("__is_set", None) - return "\n" + yaml.dump(visible) + cleaned = config._remove_private_keys(visible) + return "\n" + yaml.dump(cleaned, sort_keys=False) def copy(self) -> "config": return copy.deepcopy(self) diff --git a/bittensor/dendrite.py b/bittensor/dendrite.py index a99b28e300..3895d526c9 100644 --- a/bittensor/dendrite.py +++ b/bittensor/dendrite.py @@ -223,6 +223,7 @@ def query( returns the response from that axon. If multiple target axons are provided, returns a list of responses from all target axons. """ + result = None try: loop = asyncio.get_event_loop() result = loop.run_until_complete(self.forward(*args, **kwargs)) @@ -231,7 +232,6 @@ def query( asyncio.set_event_loop(new_loop) result = loop.run_until_complete(self.forward(*args, **kwargs)) new_loop.close() - return result finally: self.close_session() return result diff --git a/bittensor/errors.py b/bittensor/errors.py index 9053cf6824..36dac7c588 100644 --- a/bittensor/errors.py +++ b/bittensor/errors.py @@ -46,6 +46,11 @@ class UnstakeError(ChainTransactionError): pass +class IdentityError(ChainTransactionError): + r"""Error raised when an identity transaction fails.""" + pass + + class NominationError(ChainTransactionError): r"""Error raised when a nomination transaction fails.""" pass diff --git a/bittensor/subtensor.py b/bittensor/subtensor.py index 9eda2fada1..543a4610da 100644 --- a/bittensor/subtensor.py +++ b/bittensor/subtensor.py @@ -160,7 +160,7 @@ def determine_chain_endpoint_and_network(network: str): """ if network == None: return None, None - if network in ["finney", "local", "test"]: + if network in ["finney", "local", "test", "archive"]: if network == "finney": # Kiru Finney stagin network. return network, bittensor.__finney_entrypoint__ @@ -168,6 +168,8 @@ def determine_chain_endpoint_and_network(network: str): return network, bittensor.__local_entrypoint__ elif network == "test": return network, bittensor.__finney_test_entrypoint__ + elif network == "archive": + return network, bittensor.__archive_entrypoint__ else: if ( network == bittensor.__finney_entrypoint__ @@ -179,6 +181,11 @@ def determine_chain_endpoint_and_network(network: str): or "test.finney.opentensor.ai" in network ): return "test", bittensor.__finney_test_entrypoint__ + elif ( + network == bittensor.__archive_entrypoint__ + or "archive.chain.opentensor.ai" in network + ): + return "archive", bittensor.__archive_entrypoint__ elif "127.0.0.1" in network or "localhost" in network: return "local", network else: @@ -1320,6 +1327,78 @@ def root_set_weights( prompt=prompt, ) + ######################## + #### Registry Calls #### + ######################## + + """ Queries subtensor registry named storage with params and block. """ + + def query_identity( + self, + key: str, + block: Optional[int] = None, + ) -> Optional[object]: + @retry(delay=2, tries=3, backoff=2, max_delay=4) + def make_substrate_call_with_retry(): + with self.substrate as substrate: + return substrate.query( + module="Registry", + storage_function="IdentityOf", + params=[key], + block_hash=None + if block == None + else substrate.get_block_hash(block), + ) + + identity_info = make_substrate_call_with_retry() + return bittensor.utils.wallet_utils.decode_hex_identity_dict( + identity_info.value["info"] + ) + + def update_identity( + self, + wallet: "bittensor.wallet", + identified: str = None, + params: dict = {}, + wait_for_inclusion: bool = True, + wait_for_finalization: bool = False, + ) -> bool: + """ + Creates an identity extrinsics with the specific structure. + """ + if identified == None: + identified = wallet.coldkey.ss58_address + + call_params = bittensor.utils.wallet_utils.create_identity_dict(**params) + call_params["identified"] = identified + + @retry(delay=2, tries=3, backoff=2, max_delay=4) + def make_substrate_call_with_retry(): + with self.substrate as substrate: + call = substrate.compose_call( + call_module="Registry", + call_function="set_identity", + call_params=call_params, + ) + extrinsic = substrate.create_signed_extrinsic( + call=call, keypair=wallet.coldkey + ) + response = substrate.submit_extrinsic( + extrinsic, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + ) + # We only wait here if we expect finalization. + if not wait_for_finalization and not wait_for_inclusion: + return True + response.process_events() + if response.is_success: + return True + else: + raise IdentityError(response.error_message) + + return make_substrate_call_with_retry() + ######################## #### Standard Calls #### ######################## diff --git a/bittensor/utils/__init__.py b/bittensor/utils/__init__.py index dea833d72b..aa9b5e854b 100644 --- a/bittensor/utils/__init__.py +++ b/bittensor/utils/__init__.py @@ -77,7 +77,8 @@ def version_checking(timeout: int = 15): if latest_version_as_int > bittensor.__version_as_int__: print( - "\u001b[33mBittensor Version: Current {}/Latest {}\nPlease update to the latest version at your earliest convenience\u001b[0m".format( + "\u001b[33mBittensor Version: Current {}/Latest {}\nPlease update to the latest version at your earliest convenience. " + "Run the following command to upgrade:\n\n\u001b[0mpython -m pip install --upgrade bittensor".format( bittensor.__version__, latest_version ) ) diff --git a/bittensor/utils/wallet_utils.py b/bittensor/utils/wallet_utils.py index 8768d3a5ba..78a7ed065c 100644 --- a/bittensor/utils/wallet_utils.py +++ b/bittensor/utils/wallet_utils.py @@ -18,7 +18,7 @@ # DEALINGS IN THE SOFTWARE. from substrateinterface.utils import ss58 -from typing import Union +from typing import Union, Optional from .. import __ss58_format__ from substrateinterface import Keypair as Keypair @@ -102,3 +102,67 @@ def is_valid_bittensor_address_or_public_key(address: Union[str, bytes]) -> bool else: # Invalid address type return False + + +def create_identity_dict( + display: str = "", + legal: str = "", + web: str = "", + riot: str = "", + email: str = "", + pgp_fingerprint: Optional[str] = None, + image: str = "", + info: str = "", + twitter: str = "", +) -> dict: + """ + Creates a dictionary with structure for identity extrinsic. Must fit within 64 bits. + + Args: + display (str): String to be converted and stored under 'display'. + legal (str): String to be converted and stored under 'legal'. + web (str): String to be converted and stored under 'web'. + riot (str): String to be converted and stored under 'riot'. + email (str): String to be converted and stored under 'email'. + pgp_fingerprint (str): String to be converted and stored under 'pgp_fingerprint'. + image (str): String to be converted and stored under 'image'. + info (str): String to be converted and stored under 'info'. + twitter (str): String to be converted and stored under 'twitter'. + + Returns: + dict: A dictionary with the specified structure and byte string conversions. + + Raises: + ValueError: If pgp_fingerprint is not exactly 20 bytes long when encoded. + """ + if pgp_fingerprint and len(pgp_fingerprint.encode()) != 20: + raise ValueError("pgp_fingerprint must be exactly 20 bytes long when encoded") + + return { + "info": { + "additional": [[]], + "display": {f"Raw{len(display.encode())}": display.encode()}, + "legal": {f"Raw{len(legal.encode())}": legal.encode()}, + "web": {f"Raw{len(web.encode())}": web.encode()}, + "riot": {f"Raw{len(riot.encode())}": riot.encode()}, + "email": {f"Raw{len(email.encode())}": email.encode()}, + "pgp_fingerprint": pgp_fingerprint.encode() if pgp_fingerprint else None, + "image": {f"Raw{len(image.encode())}": image.encode()}, + "info": {f"Raw{len(info.encode())}": info.encode()}, + "twitter": {f"Raw{len(twitter.encode())}": twitter.encode()}, + } + } + + +def decode_hex_identity_dict(info_dictionary): + for key, value in info_dictionary.items(): + if isinstance(value, dict): + item = list(value.values())[0] + if isinstance(item, str) and item.startswith("0x"): + try: + info_dictionary[key] = bytes.fromhex(item[2:]).decode() + except UnicodeDecodeError: + print(f"Could not decode: {key}: {item}") + else: + info_dictionary[key] = item + return info_dictionary diff --git a/scripts/environments/README.md b/scripts/environments/README.md index 4506b00a05..23c14e1d5a 100644 --- a/scripts/environments/README.md +++ b/scripts/environments/README.md @@ -13,3 +13,9 @@ There are quite a few Python libraries that are not yet compatible with Apple M ```bash conda env list ``` + +4. Install bittensor (without dependencies): + ```bash + conda activate bittensor # activate the env + pip install --no-deps bittensor # install bittensor + ``` \ No newline at end of file diff --git a/scripts/environments/apple_m1_environment.yml b/scripts/environments/apple_m1_environment.yml index 0826ec38ff..9d226d1259 100644 --- a/scripts/environments/apple_m1_environment.yml +++ b/scripts/environments/apple_m1_environment.yml @@ -13,6 +13,7 @@ dependencies: - backcall=0.2.0=pyh9f0ad1d_0 - backports=1.0=pyhd8ed1ab_3 - backports.functools_lru_cache=1.6.4=pyhd8ed1ab_0 + - black=23.7.0=py310hbe9552e_1 - beautifulsoup4=4.12.2=pyha770c72_0 - bleach=6.0.0=pyhd8ed1ab_0 - brotli=1.0.9=h1a8c8d9_8 @@ -77,12 +78,14 @@ dependencies: - ptyprocess=0.7.0=pyhd3deb0d_0 - pure_eval=0.2.2=pyhd8ed1ab_0 - pycparser=2.21=pyhd8ed1ab_0 - - pycryptodome=3.11.0=py310hb4a4851_1 + - pycryptodome=3.19.0=py310hd71b1c6_1 - pygments=2.15.1=pyhd8ed1ab_0 + - python-levenshtein=0.12.2=py310he2143c4_1 - pyobjc-core=9.1.1=py310h44ed3dd_0 - pyobjc-framework-cocoa=9.1.1=py310h44ed3dd_0 - pyrsistent=0.19.3=py310h8e9501a_0 - pysocks=1.7.1=pyha2e5f31_6 + - pytest-asyncio=0.21.0=pyhd8ed1ab_0 - python=3.10.10=h3ba56d0_0_cpython - python-dateutil=2.8.2=pyhd8ed1ab_0 - python-json-logger=2.0.7=pyhd8ed1ab_0 @@ -105,6 +108,7 @@ dependencies: - traitlets=5.9.0=pyhd8ed1ab_0 - typing_extensions=4.6.1=pyha770c72_0 - tzdata=2023c=h71feb2d_0 + - uvicorn=0.22.0=py310hbe9552e_0 - wcwidth=0.2.6=pyhd8ed1ab_0 - xz=5.2.6=h57fd34a_0 - yaml=0.2.5=h3422bc3_2 @@ -113,11 +117,11 @@ dependencies: - zlib=1.2.13=h03a7124_4 - pip: - addict==2.4.0 - - aiohttp==3.8.4 + - aiohttp==3.8.5 - aiosignal==1.3.1 - altair==4.2.2 - - ansible==7.5.0 - - ansible-core==2.14.5 + - ansible==6.7.0 + - ansible-core==2.13.7 - ansible-vault==2.1.0 - appdirs==1.4.4 - argparse==1.4.0 @@ -126,30 +130,32 @@ dependencies: - backoff==2.1.0 - blinker==1.6.2 - cachetools==4.2.4 - - certifi==2020.11.8 - - cfgv==3.3.1 + - certifi==2023.7.22 + - cfgv==3.4.0 - chardet==3.0.4 - click==8.1.3 - commonmark==0.9.1 - - cryptography==39.0.0 - - cytoolz==0.12.1 - - dataclasses-json==0.5.7 + - cryptography==41.0.3 + - cytoolz==0.12.2 + - dataclasses-json==0.5.13 + - ddt==1.6.0 - dill==0.3.6 - - distlib==0.3.6 + - distlib==0.3.7 - docker-pycreds==0.4.0 - ecdsa==0.18.0 - - eth-hash==0.5.1 + - eth-hash==0.5.2 - eth-keys==0.4.0 - - eth-typing==3.3.0 - - eth-utils==2.1.0 - - exceptiongroup==1.1.1 - - filelock==3.12.0 + - eth-typing==3.4.0 + - eth-utils==2.2.0 + - exceptiongroup==1.1.2 + - fastapi==0.99.1 + - filelock==3.12.2 - fqdn==1.5.1 - - frozenlist==1.3.3 - - fsspec==2023.5.0 + - frozenlist==1.4.0 + - fsspec==2023.6.0 - fuzzywuzzy==0.18.0 - gitdb==4.0.10 - - gitpython==3.1.31 + - gitpython==3.1.32 - google-api-core==1.34.0 - google-api-python-client==2.7.0 - google-auth==1.35.0 @@ -157,24 +163,25 @@ dependencies: - googleapis-common-protos==1.59.0 - grpcio-tools==1.42.0 - httplib2==0.22.0 - - huggingface-hub==0.14.1 + - huggingface-hub==0.16.4 - hypothesis==6.47.4 - - identify==2.5.24 - - ipykernel==6.23.0 + - identify==2.5.26 + - ipykernel==6.26.0 - ipython-genutils==0.2.0 - ipywidgets==8.0.6 - isoduration==20.11.0 - - jinja2==3.0.0 + - jinja2==3.1.2 - joblib==1.2.0 - jsonpointer==2.3 - jupyter==1.0.0 - jupyter-console==6.6.3 - jupyterlab-widgets==3.0.7 - - loguru==0.6.0 + - loguru==0.7.0 - markupsafe==2.0.1 - marshmallow==3.19.0 - marshmallow-enum==1.5.1 - - more-itertools==9.1.0 + - more-itertools==10.0.0 + - msgpack-numpy-opentensor==0.5.0 - multidict==6.0.4 - multiprocess==0.70.14 - munch==2.5.0 @@ -188,11 +195,10 @@ dependencies: - notebook==6.5.4 - numexpr==2.8.4 - openapi-schema-pydantic==1.2.4 - - openvalidators==1.0.1 - password-strength==0.0.3.post2 - pathtools==0.1.2 - - pillow==9.5.0 - - platformdirs==3.5.0 + - pillow==10.1.0 + - platformdirs==3.10.0 - plotly==5.14.1 - pre-commit==3.3.2 - prometheus-client==0.14.1 @@ -202,20 +208,21 @@ dependencies: - py-ed25519-bindings==1.0.2 - py-ed25519-zebra-bindings==1.0.1 - py-sr25519-bindings==0.2.0 - - pyarrow==12.0.0 + - pyarrow==12.0.1 - pyasn1==0.5.0 - pyasn1-modules==0.3.0 - - pydantic==1.10.7 + - pydantic==1.10.12 - pydeck==0.8.1b0 - pyinstrument==4.4.0 - pympler==1.0.1 - pynacl==1.5.0 - - pyparsing==3.0.9 + - pyparsing==3.1.1 + - pytest==7.4.0 - qqdm==0.0.7 - qtconsole==5.4.3 - qtpy==2.3.1 - - regex==2023.5.5 - - requests==2.25.0 + - regex==2023.6.3 + - requests==2.31.0 - resolvelib==0.8.1 - responses==0.18.0 - retry==0.9.2 @@ -224,25 +231,26 @@ dependencies: - scalecodec==1.2.0 - scikit-learn==1.2.2 - scipy==1.10.1 - - sentencepiece==0.1.97 - - sentry-sdk==1.22.2 + - sentencepiece==0.1.99 + - sentry-sdk==1.28.1 - setproctitle==1.3.2 - shortuuid==1.0.11 - smmap==5.0.0 - sortedcontainers==2.4.0 - soupsieve==2.4.1 - - sqlalchemy==2.0.12 + - sqlalchemy==2.0.19 + - starlette==0.27.0 - streamlit==1.22.0 - substrate-interface==1.5.0 - tenacity==8.2.2 - termcolor==2.1.1 - threadpoolctl==3.1.0 - - tokenizers==0.12.1 + - tokenizers==0.13.3 - toml==0.10.2 - toolz==0.12.0 - - torch==1.13.1 - - torchvision==0.14.1 - - tornado==6.3.1 + - torch==2.0.1 + - torchvision==0.15.2 + - tornado==6.3.3 - tqdm==4.64.1 - typing-extensions==4.5.0 - typing-inspect==0.8.0 @@ -251,11 +259,11 @@ dependencies: - uritemplate==3.0.1 - urllib3==1.26.15 - validators==0.20.0 - - virtualenv==20.23.0 - - wandb==0.15.3 + - virtualenv==20.24.3 + - wandb==0.15.10 - webcolors==1.13 - webencodings==0.5.1 - - websocket-client==1.5.1 + - websocket-client==1.6.1 - wheel==0.37.1 - widgetsnbextension==4.0.7 - xxhash==3.2.0 From fe24809c9390eb2fdfcad04b2841444954419388 Mon Sep 17 00:00:00 2001 From: unconst Date: Tue, 28 Nov 2023 11:43:44 -0500 Subject: [PATCH 3/8] boost command --- bittensor/_wallet | 1 + bittensor/cli.py | 1 + bittensor/commands/__init__.py | 1 + bittensor/commands/root.py | 72 ++++++++++++++++++++++++++++++++++ s.py | 65 ++++++++++++++++++++++++++++++ 5 files changed, 140 insertions(+) create mode 160000 bittensor/_wallet create mode 100644 s.py diff --git a/bittensor/_wallet b/bittensor/_wallet new file mode 160000 index 0000000000..286eb4d1d3 --- /dev/null +++ b/bittensor/_wallet @@ -0,0 +1 @@ +Subproject commit 286eb4d1d39c90fe9cb720ad0c7570ee6cc26543 diff --git a/bittensor/cli.py b/bittensor/cli.py index 521af44bd1..382ee00d4c 100644 --- a/bittensor/cli.py +++ b/bittensor/cli.py @@ -66,6 +66,7 @@ "list": RootList, "weights": RootSetWeightsCommand, "get_weights": RootGetWeightsCommand, + 'boost': RootSetBoostCommand, "senate_vote": VoteCommand, "senate": SenateCommand, "register": RootRegisterCommand, diff --git a/bittensor/commands/__init__.py b/bittensor/commands/__init__.py index d0334bb8a8..cc40483926 100644 --- a/bittensor/commands/__init__.py +++ b/bittensor/commands/__init__.py @@ -109,5 +109,6 @@ RootList, RootSetWeightsCommand, RootGetWeightsCommand, + RootSetBoostCommand, ) from .identity import GetIdentityCommand, SetIdentityCommand diff --git a/bittensor/commands/root.py b/bittensor/commands/root.py index 0517f94628..99aa4ec388 100644 --- a/bittensor/commands/root.py +++ b/bittensor/commands/root.py @@ -190,6 +190,78 @@ def check_config(config: "bittensor.config"): pass +class RootSetBoostCommand: + """ + Executes the 'boost' command to boost the weights for a specific subnet within the root network on the Bittensor network. + + Usage: + The command allows boosting the weights for different subnets within the root network. + + Optional arguments: + - --netuid (int): A single netuid for which weights are to be boosted. + - --increase (float): The cooresponding increase in the weight for this subnet. + + Example usage: + >>> btcli root boost --netuid 1 --increase 0.01 + + """ + + @staticmethod + def run(cli): + r"""Set weights for root network.""" + wallet = bittensor.wallet(config=cli.config) + subtensor = bittensor.subtensor(config=cli.config) + subnets: List[bittensor.SubnetInfo] = subtensor.get_all_subnets_info() + + # Get values if not set. + if not cli.config.is_set("netuid"): + cli.config.netuids = int(Prompt.ask(f"Enter netuid (e.g. 1)")) + + if not cli.config.is_set("amount"): + cli.config.amount = float(Prompt.ask(f"Enter amount (e.g. 0.01)")) + + + bittensor.__console__.print("Boosting weight for subnet: {} by amount: {}".format(cli.config.netuid, cli.config.amount)) + root = subtensor.metagraph( 0, lite=False ) + try: + my_uid = root.hotkeys.index( wallet.hotkey.ss58_address ) + except ValueError: + bittensor.__console__.print("Wallet hotkey: {} not found in root metagraph".format( wallet.hotkey )) + exit() + my_weights = root.weights[my_uid] + my_weights[cli.config.netuid] += cli.config.amount + my_weights /= my_weights.sum() + all_netuids = torch.tensor( list( range( len(my_weights) ) ) ) + + subtensor.root_set_weights( + wallet=wallet, + netuids = all_netuids, + weights = my_weights, + version_key=0, + prompt=not cli.config.no_prompt, + wait_for_finalization=True, + wait_for_inclusion=True, + ) + + @staticmethod + def add_args(parser: argparse.ArgumentParser): + parser = parser.add_parser("boost", help="""Boost weight for a specific subnet by increase amount.""") + parser.add_argument("--netuid", dest="netuid", type=int, required=False) + parser.add_argument("--amount", dest="amount", type=float, required=False) + + bittensor.wallet.add_args(parser) + bittensor.subtensor.add_args(parser) + + @staticmethod + def check_config(config: "bittensor.config"): + if not config.is_set("wallet.name") and not config.no_prompt: + wallet_name = Prompt.ask("Enter wallet name", default=defaults.wallet.name) + config.wallet.name = str(wallet_name) + if not config.is_set("wallet.hotkey") and not config.no_prompt: + hotkey = Prompt.ask("Enter hotkey name", default=defaults.wallet.hotkey) + config.wallet.hotkey = str(hotkey) + + class RootSetWeightsCommand: """ Executes the 'weights' command to set the weights for the root network on the Bittensor network. diff --git a/s.py b/s.py new file mode 100644 index 0000000000..da0c9aaa22 --- /dev/null +++ b/s.py @@ -0,0 +1,65 @@ +import bittensor as bt +import typing +import os +import rich +from rich.prompt import Confirm + + +def _get_coldkey_wallets_for_path(path: str) -> typing.List["bt.wallet"]: + try: + wallet_names = next(os.walk(os.path.expanduser(path)))[1] + return [bt.wallet(path=path, name=name) for name in wallet_names] + except StopIteration: + # No wallet files found. + wallets = [] + return wallets + + +registered_delegate_info = bt.commands.utils.get_delegates_details(url=bt.__delegates_details_url__) +subtensor = bt.subtensor(network = 'finney') +wallets = _get_coldkey_wallets_for_path('~/.bittensor/wallets') +for wallet in wallets: + if not Confirm.ask(f"Unstake from {wallet.name}?"): continue + if not wallet.coldkeypub_file.exists_on_device(): continue + + delegates = subtensor.get_delegated( coldkey_ss58 = wallet.coldkeypub.ss58_address) + + my_delegates = {} # hotkey, amount + for delegate in delegates: + for coldkey_addr, staked in delegate[0].nominators: + if ( + coldkey_addr == wallet.coldkeypub.ss58_address + and staked.tao > 0 + ): + my_delegates[delegate[0].hotkey_ss58] = staked + + for delegate_ss58, amount in my_delegates.items(): + if delegate_ss58 in registered_delegate_info: + delegate_name = registered_delegate_info[ delegate_ss58 ].name + else: delegate_name = 'unknown' + if not Confirm.ask(f"Unstake from {wallet.name} to {delegate_name}:{delegate_ss58} amount {amount} ?"): continue + try: + call = subtensor.substrate.compose_call( + call_module="SubtensorModule", + call_function="remove_stake", + call_params={ + "hotkey": delegate_ss58, + "amount_unstaked": amount.rao, + }, + ) + extrinsic = subtensor.substrate.create_signed_extrinsic( + call=call, keypair=wallet.coldkey + ) + response = subtensor.substrate.submit_extrinsic( + extrinsic, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + response.process_events() + if response.is_success: + print ('unstaked successfully') + else: + print( response.error_message ) + except Exception as e: + print (e) + print ('failed to unstake') \ No newline at end of file From cc5d84f1f292078748c9b8016691b2e49df58fa0 Mon Sep 17 00:00:00 2001 From: unconst Date: Tue, 28 Nov 2023 11:44:20 -0500 Subject: [PATCH 4/8] remove spurious --- bittensor/_wallet | 1 - s.py | 65 ----------------------------------------------- 2 files changed, 66 deletions(-) delete mode 160000 bittensor/_wallet delete mode 100644 s.py diff --git a/bittensor/_wallet b/bittensor/_wallet deleted file mode 160000 index 286eb4d1d3..0000000000 --- a/bittensor/_wallet +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 286eb4d1d39c90fe9cb720ad0c7570ee6cc26543 diff --git a/s.py b/s.py deleted file mode 100644 index da0c9aaa22..0000000000 --- a/s.py +++ /dev/null @@ -1,65 +0,0 @@ -import bittensor as bt -import typing -import os -import rich -from rich.prompt import Confirm - - -def _get_coldkey_wallets_for_path(path: str) -> typing.List["bt.wallet"]: - try: - wallet_names = next(os.walk(os.path.expanduser(path)))[1] - return [bt.wallet(path=path, name=name) for name in wallet_names] - except StopIteration: - # No wallet files found. - wallets = [] - return wallets - - -registered_delegate_info = bt.commands.utils.get_delegates_details(url=bt.__delegates_details_url__) -subtensor = bt.subtensor(network = 'finney') -wallets = _get_coldkey_wallets_for_path('~/.bittensor/wallets') -for wallet in wallets: - if not Confirm.ask(f"Unstake from {wallet.name}?"): continue - if not wallet.coldkeypub_file.exists_on_device(): continue - - delegates = subtensor.get_delegated( coldkey_ss58 = wallet.coldkeypub.ss58_address) - - my_delegates = {} # hotkey, amount - for delegate in delegates: - for coldkey_addr, staked in delegate[0].nominators: - if ( - coldkey_addr == wallet.coldkeypub.ss58_address - and staked.tao > 0 - ): - my_delegates[delegate[0].hotkey_ss58] = staked - - for delegate_ss58, amount in my_delegates.items(): - if delegate_ss58 in registered_delegate_info: - delegate_name = registered_delegate_info[ delegate_ss58 ].name - else: delegate_name = 'unknown' - if not Confirm.ask(f"Unstake from {wallet.name} to {delegate_name}:{delegate_ss58} amount {amount} ?"): continue - try: - call = subtensor.substrate.compose_call( - call_module="SubtensorModule", - call_function="remove_stake", - call_params={ - "hotkey": delegate_ss58, - "amount_unstaked": amount.rao, - }, - ) - extrinsic = subtensor.substrate.create_signed_extrinsic( - call=call, keypair=wallet.coldkey - ) - response = subtensor.substrate.submit_extrinsic( - extrinsic, - wait_for_inclusion=True, - wait_for_finalization=True, - ) - response.process_events() - if response.is_success: - print ('unstaked successfully') - else: - print( response.error_message ) - except Exception as e: - print (e) - print ('failed to unstake') \ No newline at end of file From caccc039a1c05c23f9aba3d58aa143e45ec5d66b Mon Sep 17 00:00:00 2001 From: ifrit98 Date: Tue, 28 Nov 2023 17:05:35 +0000 Subject: [PATCH 5/8] run black --- bittensor/cli.py | 2 +- bittensor/commands/root.py | 25 ++++++++++++++++--------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/bittensor/cli.py b/bittensor/cli.py index cc3a1d73e7..ccb0b9e2cb 100644 --- a/bittensor/cli.py +++ b/bittensor/cli.py @@ -66,7 +66,7 @@ "list": RootList, "weights": RootSetWeightsCommand, "get_weights": RootGetWeightsCommand, - 'boost': RootSetBoostCommand, + "boost": RootSetBoostCommand, "senate_vote": VoteCommand, "senate": SenateCommand, "register": RootRegisterCommand, diff --git a/bittensor/commands/root.py b/bittensor/commands/root.py index 99aa4ec388..3a42bc2cea 100644 --- a/bittensor/commands/root.py +++ b/bittensor/commands/root.py @@ -220,23 +220,28 @@ def run(cli): if not cli.config.is_set("amount"): cli.config.amount = float(Prompt.ask(f"Enter amount (e.g. 0.01)")) - - bittensor.__console__.print("Boosting weight for subnet: {} by amount: {}".format(cli.config.netuid, cli.config.amount)) - root = subtensor.metagraph( 0, lite=False ) + bittensor.__console__.print( + "Boosting weight for subnet: {} by amount: {}".format( + cli.config.netuid, cli.config.amount + ) + ) + root = subtensor.metagraph(0, lite=False) try: - my_uid = root.hotkeys.index( wallet.hotkey.ss58_address ) + my_uid = root.hotkeys.index(wallet.hotkey.ss58_address) except ValueError: - bittensor.__console__.print("Wallet hotkey: {} not found in root metagraph".format( wallet.hotkey )) + bittensor.__console__.print( + "Wallet hotkey: {} not found in root metagraph".format(wallet.hotkey) + ) exit() my_weights = root.weights[my_uid] my_weights[cli.config.netuid] += cli.config.amount my_weights /= my_weights.sum() - all_netuids = torch.tensor( list( range( len(my_weights) ) ) ) + all_netuids = torch.tensor(list(range(len(my_weights)))) subtensor.root_set_weights( wallet=wallet, - netuids = all_netuids, - weights = my_weights, + netuids=all_netuids, + weights=my_weights, version_key=0, prompt=not cli.config.no_prompt, wait_for_finalization=True, @@ -245,7 +250,9 @@ def run(cli): @staticmethod def add_args(parser: argparse.ArgumentParser): - parser = parser.add_parser("boost", help="""Boost weight for a specific subnet by increase amount.""") + parser = parser.add_parser( + "boost", help="""Boost weight for a specific subnet by increase amount.""" + ) parser.add_argument("--netuid", dest="netuid", type=int, required=False) parser.add_argument("--amount", dest="amount", type=float, required=False) From e274dc6709d8e4c3816d0129dd7a608fa51fb4b4 Mon Sep 17 00:00:00 2001 From: ifrit98 Date: Tue, 28 Nov 2023 18:26:37 +0000 Subject: [PATCH 6/8] add slash command --- bittensor/cli.py | 1 + bittensor/commands/__init__.py | 1 + bittensor/commands/root.py | 89 +++++++++++++++++++++++++++++++--- 3 files changed, 83 insertions(+), 8 deletions(-) diff --git a/bittensor/cli.py b/bittensor/cli.py index ccb0b9e2cb..2a73b7e41c 100644 --- a/bittensor/cli.py +++ b/bittensor/cli.py @@ -67,6 +67,7 @@ "weights": RootSetWeightsCommand, "get_weights": RootGetWeightsCommand, "boost": RootSetBoostCommand, + "slash": RootSetSlashCommand, "senate_vote": VoteCommand, "senate": SenateCommand, "register": RootRegisterCommand, diff --git a/bittensor/commands/__init__.py b/bittensor/commands/__init__.py index 0e89d4bf6e..32676f625d 100644 --- a/bittensor/commands/__init__.py +++ b/bittensor/commands/__init__.py @@ -111,5 +111,6 @@ RootSetWeightsCommand, RootGetWeightsCommand, RootSetBoostCommand, + RootSetSlashCommand, ) from .identity import GetIdentityCommand, SetIdentityCommand diff --git a/bittensor/commands/root.py b/bittensor/commands/root.py index 3a42bc2cea..20cfdc7026 100644 --- a/bittensor/commands/root.py +++ b/bittensor/commands/root.py @@ -213,13 +213,6 @@ def run(cli): subtensor = bittensor.subtensor(config=cli.config) subnets: List[bittensor.SubnetInfo] = subtensor.get_all_subnets_info() - # Get values if not set. - if not cli.config.is_set("netuid"): - cli.config.netuids = int(Prompt.ask(f"Enter netuid (e.g. 1)")) - - if not cli.config.is_set("amount"): - cli.config.amount = float(Prompt.ask(f"Enter amount (e.g. 0.01)")) - bittensor.__console__.print( "Boosting weight for subnet: {} by amount: {}".format( cli.config.netuid, cli.config.amount @@ -254,7 +247,83 @@ def add_args(parser: argparse.ArgumentParser): "boost", help="""Boost weight for a specific subnet by increase amount.""" ) parser.add_argument("--netuid", dest="netuid", type=int, required=False) - parser.add_argument("--amount", dest="amount", type=float, required=False) + parser.add_argument("--increase", dest="amount", type=float, required=False) + + bittensor.wallet.add_args(parser) + bittensor.subtensor.add_args(parser) + + @staticmethod + def check_config(config: "bittensor.config"): + if not config.is_set("wallet.name") and not config.no_prompt: + wallet_name = Prompt.ask("Enter wallet name", default=defaults.wallet.name) + config.wallet.name = str(wallet_name) + if not config.is_set("wallet.hotkey") and not config.no_prompt: + hotkey = Prompt.ask("Enter hotkey name", default=defaults.wallet.hotkey) + config.wallet.hotkey = str(hotkey) + if not config.is_set("netuid"): + config.netuid = int(Prompt.ask(f"Enter netuid (e.g. 1)")) + if not config.is_set("amount"): + config.amount = float(Prompt.ask(f"Enter amount (e.g. 0.01)")) + + +class RootSetSlashCommand: + """ + Executes the 'slash' command to decrease the weights for a specific subnet within the root network on the Bittensor network. + + Usage: + The command allows slashing (decreasing) the weights for different subnets within the root network. + + Optional arguments: + - --netuid (int): A single netuid for which weights are to be slashed. + - --decrease (float): The corresponding decrease in the weight for this subnet. + + Example usage: + >>> btcli root slash --netuid 1 --decrease 0.01 + """ + + @staticmethod + def run(cli): + """Set weights for root network with decreased values.""" + wallet = bittensor.wallet(config=cli.config) + subtensor = bittensor.subtensor(config=cli.config) + subnets: List[bittensor.SubnetInfo] = subtensor.get_all_subnets_info() + + bittensor.__console__.print( + "Slashing weight for subnet: {} by amount: {}".format( + cli.config.netuid, cli.config.amount + ) + ) + root = subtensor.metagraph(0, lite=False) + try: + my_uid = root.hotkeys.index(wallet.hotkey.ss58_address) + except ValueError: + bittensor.__console__.print( + "Wallet hotkey: {} not found in root metagraph".format(wallet.hotkey) + ) + exit() + my_weights = root.weights[my_uid] + my_weights[cli.config.netuid] -= cli.config.amount + my_weights[my_weights < 0] = 0 # Ensure weights don't go negative + my_weights /= my_weights.sum() + all_netuids = torch.tensor(list(range(len(my_weights)))) + + subtensor.root_set_weights( + wallet=wallet, + netuids=all_netuids, + weights=my_weights, + version_key=0, + prompt=not cli.config.no_prompt, + wait_for_finalization=True, + wait_for_inclusion=True, + ) + + @staticmethod + def add_args(parser: argparse.ArgumentParser): + parser = parser.add_parser( + "slash", help="""Slash weight for a specific subnet by decrease amount.""" + ) + parser.add_argument("--netuid", dest="netuid", type=int, required=False) + parser.add_argument("--decrease", dest="amount", type=float, required=False) bittensor.wallet.add_args(parser) bittensor.subtensor.add_args(parser) @@ -267,6 +336,10 @@ def check_config(config: "bittensor.config"): if not config.is_set("wallet.hotkey") and not config.no_prompt: hotkey = Prompt.ask("Enter hotkey name", default=defaults.wallet.hotkey) config.wallet.hotkey = str(hotkey) + if not config.is_set("netuid"): + config.netuid = int(Prompt.ask(f"Enter netuid (e.g. 1)")) + if not config.is_set("amount"): + config.amount = float(Prompt.ask(f"Enter decrease amount (e.g. 0.01)")) class RootSetWeightsCommand: From f4a1cb617622a787aa239925263ba93a7bca55cd Mon Sep 17 00:00:00 2001 From: ifrit98 Date: Tue, 28 Nov 2023 22:16:17 +0000 Subject: [PATCH 7/8] boost/slash example docstring --- bittensor/commands/root.py | 61 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/bittensor/commands/root.py b/bittensor/commands/root.py index 20cfdc7026..09b348eb09 100644 --- a/bittensor/commands/root.py +++ b/bittensor/commands/root.py @@ -204,6 +204,37 @@ class RootSetBoostCommand: Example usage: >>> btcli root boost --netuid 1 --increase 0.01 + Enter netuid (e.g. 1): 1 + Enter amount (e.g. 0.01): 0.1 + Boosting weight for subnet: 1 by amount: 0.1 + + Normalized weights: + tensor([ + 0.0000, 0.5455, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.4545, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000]) -> tensor([0.0000, 0.5455, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.4545, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000] + ) + + Do you want to set the following root weights?: + weights: tensor([ + 0.0000, 0.5455, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.4545, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000]) + uids: tensor([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40])? [y/n]: y + True None + ✅ Finalized + ⠙ 📡 Setting root weights on test ...2023-11-28 22:09:14.001 | SUCCESS | Set weights Finalized: True + """ @staticmethod @@ -279,6 +310,36 @@ class RootSetSlashCommand: Example usage: >>> btcli root slash --netuid 1 --decrease 0.01 + + Enter netuid (e.g. 1): 1 + Enter decrease amount (e.g. 0.01): 0.2 + Slashing weight for subnet: 1 by amount: 0.2 + + Normalized weights: + tensor([ + 0.0000, 0.4318, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.5682, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000]) -> tensor([ + 0.0000, 0.4318, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.5682, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000] + ) + + Do you want to set the following root weights?: + weights: tensor([ + 0.0000, 0.4318, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.5682, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000]) + uids: tensor([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40])? [y/n]: y + ⠙ 📡 Setting root weights on test ...2023-11-28 22:09:14.001 | SUCCESS | Set weights Finalized: True """ @staticmethod From 7bb396a55d655743c1824b4764825df5c3912748 Mon Sep 17 00:00:00 2001 From: ifrit98 Date: Tue, 28 Nov 2023 23:16:08 +0000 Subject: [PATCH 8/8] fix failing test --- bittensor/commands/root.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bittensor/commands/root.py b/bittensor/commands/root.py index 09b348eb09..0e3c8e9ab3 100644 --- a/bittensor/commands/root.py +++ b/bittensor/commands/root.py @@ -291,9 +291,9 @@ def check_config(config: "bittensor.config"): if not config.is_set("wallet.hotkey") and not config.no_prompt: hotkey = Prompt.ask("Enter hotkey name", default=defaults.wallet.hotkey) config.wallet.hotkey = str(hotkey) - if not config.is_set("netuid"): + if not config.is_set("netuid") and not config.no_prompt: config.netuid = int(Prompt.ask(f"Enter netuid (e.g. 1)")) - if not config.is_set("amount"): + if not config.is_set("amount") and not config.no_prompt: config.amount = float(Prompt.ask(f"Enter amount (e.g. 0.01)")) @@ -397,9 +397,9 @@ def check_config(config: "bittensor.config"): if not config.is_set("wallet.hotkey") and not config.no_prompt: hotkey = Prompt.ask("Enter hotkey name", default=defaults.wallet.hotkey) config.wallet.hotkey = str(hotkey) - if not config.is_set("netuid"): + if not config.is_set("netuid") and not config.no_prompt: config.netuid = int(Prompt.ask(f"Enter netuid (e.g. 1)")) - if not config.is_set("amount"): + if not config.is_set("amount") and not config.no_prompt: config.amount = float(Prompt.ask(f"Enter decrease amount (e.g. 0.01)"))