diff --git a/CHANGELOG.md b/CHANGELOG.md index 61ec3c666..c4bbc0128 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## 9.4.0 /2025-04-17 + +## What's Changed +* Formatting/ruff fixes by @thewhaleking in https://github.com/opentensor/btcli/pull/426 +* Allows for torch 2.6+ by @thewhaleking in https://github.com/opentensor/btcli/pull/427 +* chore: fixed version format error and improved readability by @mdqst in https://github.com/opentensor/btcli/pull/428 +* Fixes help msg of safe staking help (in stake add etc) by @ibraheem-abe in https://github.com/opentensor/btcli/pull/432 +* Feat/start call by @ibraheem-abe in https://github.com/opentensor/btcli/pull/434 + +## New Contributors +* @mdqst made their first contribution in https://github.com/opentensor/btcli/pull/428 + +**Full Changelog**: https://github.com/opentensor/btcli/compare/v9.3.0...v9.4.0 + ## 9.3.0 /2025-04-09 ## What's Changed diff --git a/bittensor_cli/cli.py b/bittensor_cli/cli.py index 84497ce64..9df39d24e 100755 --- a/bittensor_cli/cli.py +++ b/bittensor_cli/cli.py @@ -259,14 +259,15 @@ class Options: "--slippage-tolerance", "--tolerance", "--rate-tolerance", - help="Set the rate tolerance percentage for transactions (default: 0.05%).", + help="Set the rate tolerance percentage for transactions (default: 0.05 for 5%).", callback=validate_rate_tolerance, ) safe_staking = typer.Option( None, "--safe-staking/--no-safe-staking", "--safe/--unsafe", - help="Enable or disable safe staking mode (default: enabled).", + show_default=False, + help="Enable or disable safe staking mode [dim](default: enabled)[/dim].", ) allow_partial_stake = typer.Option( None, @@ -274,7 +275,8 @@ class Options: "--partial/--no-partial", "--allow/--not-allow", "--allow-partial/--not-partial", - help="Enable or disable partial stake mode (default: disabled).", + show_default=False, + help="Enable or disable partial stake mode [dim](default: disabled)[/dim].", ) dashboard_path = typer.Option( None, @@ -873,6 +875,12 @@ def __init__(self): self.subnets_app.command( "get-identity", rich_help_panel=HELP_PANELS["SUBNETS"]["IDENTITY"] )(self.subnets_get_identity) + self.subnets_app.command( + "start", rich_help_panel=HELP_PANELS["SUBNETS"]["CREATION"] + )(self.subnets_start) + self.subnets_app.command( + "check-start", rich_help_panel=HELP_PANELS["SUBNETS"]["INFO"] + )(self.subnets_check_start) # weights commands self.weights_app.command( @@ -1184,12 +1192,14 @@ def set_config( "--safe-staking/--no-safe-staking", "--safe/--unsafe", help="Enable or disable safe staking mode.", + show_default=False, ), allow_partial_stake: Optional[bool] = typer.Option( None, "--allow-partial-stake/--no-allow-partial-stake", "--partial/--no-partial", "--allow/--not-allow", + show_default=False, ), dashboard_path: Optional[str] = Options.dashboard_path, ): @@ -4975,6 +4985,70 @@ def subnets_create( ) ) + def subnets_check_start( + self, + network: Optional[list[str]] = Options.network, + netuid: int = Options.netuid, + quiet: bool = Options.quiet, + verbose: bool = Options.verbose, + ): + """ + Checks if a subnet's emission schedule can be started. + + This command verifies if a subnet's emission schedule can be started based on the subnet's registration block. + + Example: + [green]$[/green] btcli subnets check_start --netuid 1 + """ + self.verbosity_handler(quiet, verbose) + return self._run_command( + subnets.get_start_schedule(self.initialize_chain(network), netuid) + ) + + def subnets_start( + self, + wallet_name: str = Options.wallet_name, + wallet_path: str = Options.wallet_path, + wallet_hotkey: str = Options.wallet_hotkey, + network: Optional[list[str]] = Options.network, + netuid: int = Options.netuid, + prompt: bool = Options.prompt, + quiet: bool = Options.quiet, + verbose: bool = Options.verbose, + ): + """ + Starts a subnet's emission schedule. + + The owner of the subnet must call this command to start the emission schedule. + + Example: + [green]$[/green] btcli subnets start --netuid 1 + [green]$[/green] btcli subnets start --netuid 1 --wallet-name alice + """ + self.verbosity_handler(quiet, verbose) + if not wallet_name: + wallet_name = Prompt.ask( + "Enter the [blue]wallet name[/blue] [dim](which you used to create the subnet)[/dim]", + default=self.config.get("wallet_name") or defaults.wallet.name, + ) + wallet = self.wallet_ask( + wallet_name, + wallet_path, + wallet_hotkey, + ask_for=[ + WO.NAME, + ], + validate=WV.WALLET, + ) + return self._run_command( + subnets.start_subnet( + wallet, + self.initialize_chain(network), + netuid, + prompt, + ) + ) + def subnets_get_identity( self, network: Optional[list[str]] = Options.network, diff --git a/bittensor_cli/src/__init__.py b/bittensor_cli/src/__init__.py index e8dcdc531..5c508812c 100644 --- a/bittensor_cli/src/__init__.py +++ b/bittensor_cli/src/__init__.py @@ -38,6 +38,7 @@ class Constants: "test": "0x8f9cf856bf558a14440e75569c9e58594757048d7b3a84b5d25f6bd978263105", } delegates_detail_url = "https://raw.githubusercontent.com/opentensor/bittensor-delegates/main/public/delegates.json" + emission_start_schedule = 7 * 24 * 60 * 60 / 12 # 7 days @dataclass @@ -168,446 +169,445 @@ class WalletValidationTypes(Enum): } UNITS = [ - b"\xCE\xA4".decode(), # Τ (Upper case Tau, 0) - b"\xCE\xB1".decode(), # α (Alpha, 1) - b"\xCE\xB2".decode(), # β (Beta, 2) - b"\xCE\xB3".decode(), # γ (Gamma, 3) - b"\xCE\xB4".decode(), # δ (Delta, 4) - b"\xCE\xB5".decode(), # ε (Epsilon, 5) - b"\xCE\xB6".decode(), # ζ (Zeta, 6) - b"\xCE\xB7".decode(), # η (Eta, 7) - b"\xCE\xB8".decode(), # θ (Theta, 8) - b"\xCE\xB9".decode(), # ι (Iota, 9) - b"\xCE\xBA".decode(), # κ (Kappa, 10) - b"\xCE\xBB".decode(), # λ (Lambda, 11) - b"\xCE\xBC".decode(), # μ (Mu, 12) - b"\xCE\xBD".decode(), # ν (Nu, 13) - b"\xCE\xBE".decode(), # ξ (Xi, 14) - b"\xCE\xBF".decode(), # ο (Omicron, 15) - b"\xCF\x80".decode(), # π (Pi, 16) - b"\xCF\x81".decode(), # ρ (Rho, 17) - b"\xCF\x83".decode(), # σ (Sigma, 18) - "t", # t (Tau, 19) - b"\xCF\x85".decode(), # υ (Upsilon, 20) - b"\xCF\x86".decode(), # φ (Phi, 21) - b"\xCF\x87".decode(), # χ (Chi, 22) - b"\xCF\x88".decode(), # ψ (Psi, 23) - b"\xCF\x89".decode(), # ω (Omega, 24) - b"\xD7\x90".decode(), # א (Aleph, 25) - b"\xD7\x91".decode(), # ב (Bet, 26) - b"\xD7\x92".decode(), # ג (Gimel, 27) - b"\xD7\x93".decode(), # ד (Dalet, 28) - b"\xD7\x94".decode(), # ה (He, 29) - b"\xD7\x95".decode(), # ו (Vav, 30) - b"\xD7\x96".decode(), # ז (Zayin, 31) - b"\xD7\x97".decode(), # ח (Het, 32) - b"\xD7\x98".decode(), # ט (Tet, 33) - b"\xD7\x99".decode(), # י (Yod, 34) - b"\xD7\x9A".decode(), # ך (Final Kaf, 35) - b"\xD7\x9B".decode(), # כ (Kaf, 36) - b"\xD7\x9C".decode(), # ל (Lamed, 37) - b"\xD7\x9D".decode(), # ם (Final Mem, 38) - b"\xD7\x9E".decode(), # מ (Mem, 39) - b"\xD7\x9F".decode(), # ן (Final Nun, 40) - b"\xD7\xA0".decode(), # נ (Nun, 41) - b"\xD7\xA1".decode(), # ס (Samekh, 42) - b"\xD7\xA2".decode(), # ע (Ayin, 43) - b"\xD7\xA3".decode(), # ף (Final Pe, 44) - b"\xD7\xA4".decode(), # פ (Pe, 45) - b"\xD7\xA5".decode(), # ץ (Final Tsadi, 46) - b"\xD7\xA6".decode(), # צ (Tsadi, 47) - b"\xD7\xA7".decode(), # ק (Qof, 48) - b"\xD7\xA8".decode(), # ר (Resh, 49) - b"\xD7\xA9".decode(), # ש (Shin, 50) - b"\xD7\xAA".decode(), # ת (Tav, 51) - b"\xD8\xA7".decode(), # ا (Alif, 52) - b"\xD8\xA8".decode(), # ب (Ba, 53) - b"\xD8\xAA".decode(), # ت (Ta, 54) - b"\xD8\xAB".decode(), # ث (Tha, 55) - b"\xD8\xAC".decode(), # ج (Jim, 56) - b"\xD8\xAD".decode(), # ح (Ha, 57) - b"\xD8\xAE".decode(), # خ (Kha, 58) - b"\xD8\xAF".decode(), # د (Dal, 59) - b"\xD8\xB0".decode(), # ذ (Dhal, 60) - b"\xD8\xB1".decode(), # ر (Ra, 61) - b"\xD8\xB2".decode(), # ز (Zay, 62) - b"\xD8\xB3".decode(), # س (Sin, 63) - b"\xD8\xB4".decode(), # ش (Shin, 64) - b"\xD8\xB5".decode(), # ص (Sad, 65) - b"\xD8\xB6".decode(), # ض (Dad, 66) - b"\xD8\xB7".decode(), # ط (Ta, 67) - b"\xD8\xB8".decode(), # ظ (Dha, 68) - b"\xD8\xB9".decode(), # ع (Ain, 69) - b"\xD8\xBA".decode(), # غ (Ghayn, 70) - b"\xD9\x81".decode(), # ف (Fa, 71) - b"\xD9\x82".decode(), # ق (Qaf, 72) - b"\xD9\x83".decode(), # ك (Kaf, 73) - b"\xD9\x84".decode(), # ل (Lam, 74) - b"\xD9\x85".decode(), # م (Mim, 75) - b"\xD9\x86".decode(), # ن (Nun, 76) - b"\xD9\x87".decode(), # ه (Ha, 77) - b"\xD9\x88".decode(), # و (Waw, 78) - b"\xD9\x8A".decode(), # ي (Ya, 79) - b"\xD9\x89".decode(), # ى (Alef Maksura, 80) - b"\xE1\x9A\xA0".decode(), # ᚠ (Fehu, wealth, 81) - b"\xE1\x9A\xA2".decode(), # ᚢ (Uruz, strength, 82) - b"\xE1\x9A\xA6".decode(), # ᚦ (Thurisaz, giant, 83) - b"\xE1\x9A\xA8".decode(), # ᚨ (Ansuz, god, 84) - b"\xE1\x9A\xB1".decode(), # ᚱ (Raidho, ride, 85) - b"\xE1\x9A\xB3".decode(), # ᚲ (Kaunan, ulcer, 86) - b"\xD0\xAB".decode(), # Ы (Cyrillic Yeru, 87) - b"\xE1\x9B\x89".decode(), # ᛉ (Algiz, protection, 88) - b"\xE1\x9B\x92".decode(), # ᛒ (Berkanan, birch, 89) - b"\xE1\x9A\x80".decode(), #   (Space, 90) - b"\xE1\x9A\x81".decode(), # ᚁ (Beith, birch, 91) - b"\xE1\x9A\x82".decode(), # ᚂ (Luis, rowan, 92) - b"\xE1\x9A\x83".decode(), # ᚃ (Fearn, alder, 93) - b"\xE1\x9A\x84".decode(), # ᚄ (Sail, willow, 94) - b"\xE1\x9A\x85".decode(), # ᚅ (Nion, ash, 95) - b"\xE1\x9A\x9B".decode(), # ᚛ (Forfeda, 96) - b"\xE1\x83\x90".decode(), # ა (Ani, 97) - b"\xE1\x83\x91".decode(), # ბ (Bani, 98) - b"\xE1\x83\x92".decode(), # გ (Gani, 99) - b"\xE1\x83\x93".decode(), # დ (Doni, 100) - b"\xE1\x83\x94".decode(), # ე (Eni, 101) - b"\xE1\x83\x95".decode(), # ვ (Vini, 102) - b"\xD4\xB1".decode(), # Ա (Ayp, 103) - b"\xD4\xB2".decode(), # Բ (Ben, 104) - b"\xD4\xB3".decode(), # Գ (Gim, 105) - b"\xD4\xB4".decode(), # Դ (Da, 106) - b"\xD4\xB5".decode(), # Ե (Ech, 107) - b"\xD4\xB6".decode(), # Զ (Za, 108) - b"\xD5\x9E".decode(), # ՞ (Question mark, 109) - b"\xD0\x80".decode(), # Ѐ (Ie with grave, 110) - b"\xD0\x81".decode(), # Ё (Io, 111) - b"\xD0\x82".decode(), # Ђ (Dje, 112) - b"\xD0\x83".decode(), # Ѓ (Gje, 113) - b"\xD0\x84".decode(), # Є (Ukrainian Ie, 114) - b"\xD0\x85".decode(), # Ѕ (Dze, 115) - b"\xD1\x8A".decode(), # Ъ (Hard sign, 116) - b"\xE2\xB2\x80".decode(), # Ⲁ (Alfa, 117) - b"\xE2\xB2\x81".decode(), # ⲁ (Small Alfa, 118) - b"\xE2\xB2\x82".decode(), # Ⲃ (Vida, 119) - b"\xE2\xB2\x83".decode(), # ⲃ (Small Vida, 120) - b"\xE2\xB2\x84".decode(), # Ⲅ (Gamma, 121) - b"\xE2\xB2\x85".decode(), # ⲅ (Small Gamma, 122) - b"\xF0\x91\x80\x80".decode(), # 𑀀 (A, 123) - b"\xF0\x91\x80\x81".decode(), # 𑀁 (Aa, 124) - b"\xF0\x91\x80\x82".decode(), # 𑀂 (I, 125) - b"\xF0\x91\x80\x83".decode(), # 𑀃 (Ii, 126) - b"\xF0\x91\x80\x85".decode(), # 𑀅 (U, 127) - b"\xE0\xB6\xB1".decode(), # ඲ (La, 128) - b"\xE0\xB6\xB2".decode(), # ඳ (Va, 129) - b"\xE0\xB6\xB3".decode(), # ප (Sha, 130) - b"\xE0\xB6\xB4".decode(), # ඵ (Ssa, 131) - b"\xE0\xB6\xB5".decode(), # බ (Sa, 132) - b"\xE0\xB6\xB6".decode(), # භ (Ha, 133) - b"\xE2\xB0\x80".decode(), # Ⰰ (Az, 134) - b"\xE2\xB0\x81".decode(), # Ⰱ (Buky, 135) - b"\xE2\xB0\x82".decode(), # Ⰲ (Vede, 136) - b"\xE2\xB0\x83".decode(), # Ⰳ (Glagoli, 137) - b"\xE2\xB0\x84".decode(), # Ⰴ (Dobro, 138) - b"\xE2\xB0\x85".decode(), # Ⰵ (Yest, 139) - b"\xE2\xB0\x86".decode(), # Ⰶ (Zhivete, 140) - b"\xE2\xB0\x87".decode(), # Ⰷ (Zemlja, 141) - b"\xE2\xB0\x88".decode(), # Ⰸ (Izhe, 142) - b"\xE2\xB0\x89".decode(), # Ⰹ (Initial Izhe, 143) - b"\xE2\xB0\x8A".decode(), # Ⰺ (I, 144) - b"\xE2\xB0\x8B".decode(), # Ⰻ (Djerv, 145) - b"\xE2\xB0\x8C".decode(), # Ⰼ (Kako, 146) - b"\xE2\xB0\x8D".decode(), # Ⰽ (Ljudije, 147) - b"\xE2\xB0\x8E".decode(), # Ⰾ (Myse, 148) - b"\xE2\xB0\x8F".decode(), # Ⰿ (Nash, 149) - b"\xE2\xB0\x90".decode(), # Ⱀ (On, 150) - b"\xE2\xB0\x91".decode(), # Ⱁ (Pokoj, 151) - b"\xE2\xB0\x92".decode(), # Ⱂ (Rtsy, 152) - b"\xE2\xB0\x93".decode(), # Ⱃ (Slovo, 153) - b"\xE2\xB0\x94".decode(), # Ⱄ (Tvrido, 154) - b"\xE2\xB0\x95".decode(), # Ⱅ (Uku, 155) - b"\xE2\xB0\x96".decode(), # Ⱆ (Fert, 156) - b"\xE2\xB0\x97".decode(), # Ⱇ (Xrivi, 157) - b"\xE2\xB0\x98".decode(), # Ⱈ (Ot, 158) - b"\xE2\xB0\x99".decode(), # Ⱉ (Cy, 159) - b"\xE2\xB0\x9A".decode(), # Ⱊ (Shcha, 160) - b"\xE2\xB0\x9B".decode(), # Ⱋ (Er, 161) - b"\xE2\xB0\x9C".decode(), # Ⱌ (Yeru, 162) - b"\xE2\xB0\x9D".decode(), # Ⱍ (Small Yer, 163) - b"\xE2\xB0\x9E".decode(), # Ⱎ (Yo, 164) - b"\xE2\xB0\x9F".decode(), # Ⱏ (Yu, 165) - b"\xE2\xB0\xA0".decode(), # Ⱐ (Ja, 166) - b"\xE0\xB8\x81".decode(), # ก (Ko Kai, 167) - b"\xE0\xB8\x82".decode(), # ข (Kho Khai, 168) - b"\xE0\xB8\x83".decode(), # ฃ (Kho Khuat, 169) - b"\xE0\xB8\x84".decode(), # ค (Kho Khon, 170) - b"\xE0\xB8\x85".decode(), # ฅ (Kho Rakhang, 171) - b"\xE0\xB8\x86".decode(), # ฆ (Kho Khwai, 172) - b"\xE0\xB8\x87".decode(), # ง (Ngo Ngu, 173) - b"\xE0\xB8\x88".decode(), # จ (Cho Chan, 174) - b"\xE0\xB8\x89".decode(), # ฉ (Cho Ching, 175) - b"\xE0\xB8\x8A".decode(), # ช (Cho Chang, 176) - b"\xE0\xB8\x8B".decode(), # ซ (So So, 177) - b"\xE0\xB8\x8C".decode(), # ฌ (Cho Choe, 178) - b"\xE0\xB8\x8D".decode(), # ญ (Yo Ying, 179) - b"\xE0\xB8\x8E".decode(), # ฎ (Do Chada, 180) - b"\xE0\xB8\x8F".decode(), # ฏ (To Patak, 181) - b"\xE0\xB8\x90".decode(), # ฐ (Tho Than, 182) - b"\xE0\xB8\x91".decode(), # ฑ (Tho Nangmontho, 183) - b"\xE0\xB8\x92".decode(), # ฒ (Tho Phuthao, 184) - b"\xE0\xB8\x93".decode(), # ณ (No Nen, 185) - b"\xE0\xB8\x94".decode(), # ด (Do Dek, 186) - b"\xE0\xB8\x95".decode(), # ต (To Tao, 187) - b"\xE0\xB8\x96".decode(), # ถ (Tho Thung, 188) - b"\xE0\xB8\x97".decode(), # ท (Tho Thahan, 189) - b"\xE0\xB8\x98".decode(), # ธ (Tho Thong, 190) - b"\xE0\xB8\x99".decode(), # น (No Nu, 191) - b"\xE0\xB8\x9A".decode(), # บ (Bo Baimai, 192) - b"\xE0\xB8\x9B".decode(), # ป (Po Pla, 193) - b"\xE0\xB8\x9C".decode(), # ผ (Pho Phung, 194) - b"\xE0\xB8\x9D".decode(), # ฝ (Fo Fa, 195) - b"\xE0\xB8\x9E".decode(), # พ (Pho Phan, 196) - b"\xE0\xB8\x9F".decode(), # ฟ (Fo Fan, 197) - b"\xE0\xB8\xA0".decode(), # ภ (Pho Samphao, 198) - b"\xE0\xB8\xA1".decode(), # ม (Mo Ma, 199) - b"\xE0\xB8\xA2".decode(), # ย (Yo Yak, 200) - b"\xE0\xB8\xA3".decode(), # ร (Ro Rua, 201) - b"\xE0\xB8\xA5".decode(), # ล (Lo Ling, 202) - b"\xE0\xB8\xA7".decode(), # ว (Wo Waen, 203) - b"\xE0\xB8\xA8".decode(), # ศ (So Sala, 204) - b"\xE0\xB8\xA9".decode(), # ษ (So Rusi, 205) - b"\xE0\xB8\xAA".decode(), # ส (So Sua, 206) - b"\xE0\xB8\xAB".decode(), # ห (Ho Hip, 207) - b"\xE0\xB8\xAC".decode(), # ฬ (Lo Chula, 208) - b"\xE0\xB8\xAD".decode(), # อ (O Ang, 209) - b"\xE0\xB8\xAE".decode(), # ฮ (Ho Nokhuk, 210) - b"\xE1\x84\x80".decode(), # ㄱ (Giyeok, 211) - b"\xE1\x84\x81".decode(), # ㄴ (Nieun, 212) - b"\xE1\x84\x82".decode(), # ㄷ (Digeut, 213) - b"\xE1\x84\x83".decode(), # ㄹ (Rieul, 214) - b"\xE1\x84\x84".decode(), # ㅁ (Mieum, 215) - b"\xE1\x84\x85".decode(), # ㅂ (Bieup, 216) - b"\xE1\x84\x86".decode(), # ㅅ (Siot, 217) - b"\xE1\x84\x87".decode(), # ㅇ (Ieung, 218) - b"\xE1\x84\x88".decode(), # ㅈ (Jieut, 219) - b"\xE1\x84\x89".decode(), # ㅊ (Chieut, 220) - b"\xE1\x84\x8A".decode(), # ㅋ (Kieuk, 221) - b"\xE1\x84\x8B".decode(), # ㅌ (Tieut, 222) - b"\xE1\x84\x8C".decode(), # ㅍ (Pieup, 223) - b"\xE1\x84\x8D".decode(), # ㅎ (Hieut, 224) - b"\xE1\x85\xA1".decode(), # ㅏ (A, 225) - b"\xE1\x85\xA2".decode(), # ㅐ (Ae, 226) - b"\xE1\x85\xA3".decode(), # ㅑ (Ya, 227) - b"\xE1\x85\xA4".decode(), # ㅒ (Yae, 228) - b"\xE1\x85\xA5".decode(), # ㅓ (Eo, 229) - b"\xE1\x85\xA6".decode(), # ㅔ (E, 230) - b"\xE1\x85\xA7".decode(), # ㅕ (Yeo, 231) - b"\xE1\x85\xA8".decode(), # ㅖ (Ye, 232) - b"\xE1\x85\xA9".decode(), # ㅗ (O, 233) - b"\xE1\x85\xAA".decode(), # ㅘ (Wa, 234) - b"\xE1\x85\xAB".decode(), # ㅙ (Wae, 235) - b"\xE1\x85\xAC".decode(), # ㅚ (Oe, 236) - b"\xE1\x85\xAD".decode(), # ㅛ (Yo, 237) - b"\xE1\x85\xAE".decode(), # ㅜ (U, 238) - b"\xE1\x85\xAF".decode(), # ㅝ (Weo, 239) - b"\xE1\x85\xB0".decode(), # ㅞ (We, 240) - b"\xE1\x85\xB1".decode(), # ㅟ (Wi, 241) - b"\xE1\x85\xB2".decode(), # ㅠ (Yu, 242) - b"\xE1\x85\xB3".decode(), # ㅡ (Eu, 243) - b"\xE1\x85\xB4".decode(), # ㅢ (Ui, 244) - b"\xE1\x85\xB5".decode(), # ㅣ (I, 245) - b"\xE1\x8A\xA0".decode(), # አ (Glottal A, 246) - b"\xE1\x8A\xA1".decode(), # ኡ (Glottal U, 247) - b"\xE1\x8A\xA2".decode(), # ኢ (Glottal I, 248) - b"\xE1\x8A\xA3".decode(), # ኣ (Glottal Aa, 249) - b"\xE1\x8A\xA4".decode(), # ኤ (Glottal E, 250) - b"\xE1\x8A\xA5".decode(), # እ (Glottal Ie, 251) - b"\xE1\x8A\xA6".decode(), # ኦ (Glottal O, 252) - b"\xE1\x8A\xA7".decode(), # ኧ (Glottal Wa, 253) - b"\xE1\x8B\x88".decode(), # ወ (Wa, 254) - b"\xE1\x8B\x89".decode(), # ዉ (Wu, 255) - b"\xE1\x8B\x8A".decode(), # ዊ (Wi, 256) - b"\xE1\x8B\x8B".decode(), # ዋ (Waa, 257) - b"\xE1\x8B\x8C".decode(), # ዌ (We, 258) - b"\xE1\x8B\x8D".decode(), # ው (Wye, 259) - b"\xE1\x8B\x8E".decode(), # ዎ (Wo, 260) - b"\xE1\x8A\xB0".decode(), # ኰ (Ko, 261) - b"\xE1\x8A\xB1".decode(), # ኱ (Ku, 262) - b"\xE1\x8A\xB2".decode(), # ኲ (Ki, 263) - b"\xE1\x8A\xB3".decode(), # ኳ (Kua, 264) - b"\xE1\x8A\xB4".decode(), # ኴ (Ke, 265) - b"\xE1\x8A\xB5".decode(), # ኵ (Kwe, 266) - b"\xE1\x8A\xB6".decode(), # ኶ (Ko, 267) - b"\xE1\x8A\x90".decode(), # ጐ (Go, 268) - b"\xE1\x8A\x91".decode(), # ጑ (Gu, 269) - b"\xE1\x8A\x92".decode(), # ጒ (Gi, 270) - b"\xE1\x8A\x93".decode(), # መ (Gua, 271) - b"\xE1\x8A\x94".decode(), # ጔ (Ge, 272) - b"\xE1\x8A\x95".decode(), # ጕ (Gwe, 273) - b"\xE1\x8A\x96".decode(), # ጖ (Go, 274) - b"\xE0\xA4\x85".decode(), # अ (A, 275) - b"\xE0\xA4\x86".decode(), # आ (Aa, 276) - b"\xE0\xA4\x87".decode(), # इ (I, 277) - b"\xE0\xA4\x88".decode(), # ई (Ii, 278) - b"\xE0\xA4\x89".decode(), # उ (U, 279) - b"\xE0\xA4\x8A".decode(), # ऊ (Uu, 280) - b"\xE0\xA4\x8B".decode(), # ऋ (R, 281) - b"\xE0\xA4\x8F".decode(), # ए (E, 282) - b"\xE0\xA4\x90".decode(), # ऐ (Ai, 283) - b"\xE0\xA4\x93".decode(), # ओ (O, 284) - b"\xE0\xA4\x94".decode(), # औ (Au, 285) - b"\xE0\xA4\x95".decode(), # क (Ka, 286) - b"\xE0\xA4\x96".decode(), # ख (Kha, 287) - b"\xE0\xA4\x97".decode(), # ग (Ga, 288) - b"\xE0\xA4\x98".decode(), # घ (Gha, 289) - b"\xE0\xA4\x99".decode(), # ङ (Nga, 290) - b"\xE0\xA4\x9A".decode(), # च (Cha, 291) - b"\xE0\xA4\x9B".decode(), # छ (Chha, 292) - b"\xE0\xA4\x9C".decode(), # ज (Ja, 293) - b"\xE0\xA4\x9D".decode(), # झ (Jha, 294) - b"\xE0\xA4\x9E".decode(), # ञ (Nya, 295) - b"\xE0\xA4\x9F".decode(), # ट (Ta, 296) - b"\xE0\xA4\xA0".decode(), # ठ (Tha, 297) - b"\xE0\xA4\xA1".decode(), # ड (Da, 298) - b"\xE0\xA4\xA2".decode(), # ढ (Dha, 299) - b"\xE0\xA4\xA3".decode(), # ण (Na, 300) - b"\xE0\xA4\xA4".decode(), # त (Ta, 301) - b"\xE0\xA4\xA5".decode(), # थ (Tha, 302) - b"\xE0\xA4\xA6".decode(), # द (Da, 303) - b"\xE0\xA4\xA7".decode(), # ध (Dha, 304) - b"\xE0\xA4\xA8".decode(), # न (Na, 305) - b"\xE0\xA4\xAA".decode(), # प (Pa, 306) - b"\xE0\xA4\xAB".decode(), # फ (Pha, 307) - b"\xE0\xA4\xAC".decode(), # ब (Ba, 308) - b"\xE0\xA4\xAD".decode(), # भ (Bha, 309) - b"\xE0\xA4\xAE".decode(), # म (Ma, 310) - b"\xE0\xA4\xAF".decode(), # य (Ya, 311) - b"\xE0\xA4\xB0".decode(), # र (Ra, 312) - b"\xE0\xA4\xB2".decode(), # ल (La, 313) - b"\xE0\xA4\xB5".decode(), # व (Va, 314) - b"\xE0\xA4\xB6".decode(), # श (Sha, 315) - b"\xE0\xA4\xB7".decode(), # ष (Ssa, 316) - b"\xE0\xA4\xB8".decode(), # स (Sa, 317) - b"\xE0\xA4\xB9".decode(), # ह (Ha, 318) - b"\xE3\x82\xA2".decode(), # ア (A, 319) - b"\xE3\x82\xA4".decode(), # イ (I, 320) - b"\xE3\x82\xA6".decode(), # ウ (U, 321) - b"\xE3\x82\xA8".decode(), # エ (E, 322) - b"\xE3\x82\xAA".decode(), # オ (O, 323) - b"\xE3\x82\xAB".decode(), # カ (Ka, 324) - b"\xE3\x82\xAD".decode(), # キ (Ki, 325) - b"\xE3\x82\xAF".decode(), # ク (Ku, 326) - b"\xE3\x82\xB1".decode(), # ケ (Ke, 327) - b"\xE3\x82\xB3".decode(), # コ (Ko, 328) - b"\xE3\x82\xB5".decode(), # サ (Sa, 329) - b"\xE3\x82\xB7".decode(), # シ (Shi, 330) - b"\xE3\x82\xB9".decode(), # ス (Su, 331) - b"\xE3\x82\xBB".decode(), # セ (Se, 332) - b"\xE3\x82\xBD".decode(), # ソ (So, 333) - b"\xE3\x82\xBF".decode(), # タ (Ta, 334) - b"\xE3\x83\x81".decode(), # チ (Chi, 335) - b"\xE3\x83\x84".decode(), # ツ (Tsu, 336) - b"\xE3\x83\x86".decode(), # テ (Te, 337) - b"\xE3\x83\x88".decode(), # ト (To, 338) - b"\xE3\x83\x8A".decode(), # ナ (Na, 339) - b"\xE3\x83\x8B".decode(), # ニ (Ni, 340) - b"\xE3\x83\x8C".decode(), # ヌ (Nu, 341) - b"\xE3\x83\x8D".decode(), # ネ (Ne, 342) - b"\xE3\x83\x8E".decode(), # ノ (No, 343) - b"\xE3\x83\x8F".decode(), # ハ (Ha, 344) - b"\xE3\x83\x92".decode(), # ヒ (Hi, 345) - b"\xE3\x83\x95".decode(), # フ (Fu, 346) - b"\xE3\x83\x98".decode(), # ヘ (He, 347) - b"\xE3\x83\x9B".decode(), # ホ (Ho, 348) - b"\xE3\x83\x9E".decode(), # マ (Ma, 349) - b"\xE3\x83\x9F".decode(), # ミ (Mi, 350) - b"\xE3\x83\xA0".decode(), # ム (Mu, 351) - b"\xE3\x83\xA1".decode(), # メ (Me, 352) - b"\xE3\x83\xA2".decode(), # モ (Mo, 353) - b"\xE3\x83\xA4".decode(), # ヤ (Ya, 354) - b"\xE3\x83\xA6".decode(), # ユ (Yu, 355) - b"\xE3\x83\xA8".decode(), # ヨ (Yo, 356) - b"\xE3\x83\xA9".decode(), # ラ (Ra, 357) - b"\xE3\x83\xAA".decode(), # リ (Ri, 358) - b"\xE3\x83\xAB".decode(), # ル (Ru, 359) - b"\xE3\x83\xAC".decode(), # レ (Re, 360) - b"\xE3\x83\xAD".decode(), # ロ (Ro, 361) - b"\xE3\x83\xAF".decode(), # ワ (Wa, 362) - b"\xE3\x83\xB2".decode(), # ヲ (Wo, 363) - b"\xE3\x83\xB3".decode(), # ン (N, 364) - b"\xE2\xB4\xB0".decode(), # ⴰ (Ya, 365) - b"\xE2\xB4\xB1".decode(), # ⴱ (Yab, 366) - b"\xE2\xB4\xB2".decode(), # ⴲ (Yabh, 367) - b"\xE2\xB4\xB3".decode(), # ⴳ (Yag, 368) - b"\xE2\xB4\xB4".decode(), # ⴴ (Yagh, 369) - b"\xE2\xB4\xB5".decode(), # ⴵ (Yaj, 370) - b"\xE2\xB4\xB6".decode(), # ⴶ (Yach, 371) - b"\xE2\xB4\xB7".decode(), # ⴷ (Yad, 372) - b"\xE2\xB4\xB8".decode(), # ⴸ (Yadh, 373) - b"\xE2\xB4\xB9".decode(), # ⴹ (Yadh, emphatic, 374) - b"\xE2\xB4\xBA".decode(), # ⴺ (Yaz, 375) - b"\xE2\xB4\xBB".decode(), # ⴻ (Yazh, 376) - b"\xE2\xB4\xBC".decode(), # ⴼ (Yaf, 377) - b"\xE2\xB4\xBD".decode(), # ⴽ (Yak, 378) - b"\xE2\xB4\xBE".decode(), # ⴾ (Yak, variant, 379) - b"\xE2\xB4\xBF".decode(), # ⴿ (Yaq, 380) - b"\xE2\xB5\x80".decode(), # ⵀ (Yah, 381) - b"\xE2\xB5\x81".decode(), # ⵁ (Yahh, 382) - b"\xE2\xB5\x82".decode(), # ⵂ (Yahl, 383) - b"\xE2\xB5\x83".decode(), # ⵃ (Yahm, 384) - b"\xE2\xB5\x84".decode(), # ⵄ (Yayn, 385) - b"\xE2\xB5\x85".decode(), # ⵅ (Yakh, 386) - b"\xE2\xB5\x86".decode(), # ⵆ (Yakl, 387) - b"\xE2\xB5\x87".decode(), # ⵇ (Yahq, 388) - b"\xE2\xB5\x88".decode(), # ⵈ (Yash, 389) - b"\xE2\xB5\x89".decode(), # ⵉ (Yi, 390) - b"\xE2\xB5\x8A".decode(), # ⵊ (Yij, 391) - b"\xE2\xB5\x8B".decode(), # ⵋ (Yizh, 392) - b"\xE2\xB5\x8C".decode(), # ⵌ (Yink, 393) - b"\xE2\xB5\x8D".decode(), # ⵍ (Yal, 394) - b"\xE2\xB5\x8E".decode(), # ⵎ (Yam, 395) - b"\xE2\xB5\x8F".decode(), # ⵏ (Yan, 396) - b"\xE2\xB5\x90".decode(), # ⵐ (Yang, 397) - b"\xE2\xB5\x91".decode(), # ⵑ (Yany, 398) - b"\xE2\xB5\x92".decode(), # ⵒ (Yap, 399) - b"\xE2\xB5\x93".decode(), # ⵓ (Yu, 400) - b"\xE0\xB6\x85".decode(), # අ (A, 401) - b"\xE0\xB6\x86".decode(), # ආ (Aa, 402) - b"\xE0\xB6\x87".decode(), # ඉ (I, 403) - b"\xE0\xB6\x88".decode(), # ඊ (Ii, 404) - b"\xE0\xB6\x89".decode(), # උ (U, 405) - b"\xE0\xB6\x8A".decode(), # ඌ (Uu, 406) - b"\xE0\xB6\x8B".decode(), # ඍ (R, 407) - b"\xE0\xB6\x8C".decode(), # ඎ (Rr, 408) - b"\xE0\xB6\x8F".decode(), # ඏ (L, 409) - b"\xE0\xB6\x90".decode(), # ඐ (Ll, 410) - b"\xE0\xB6\x91".decode(), # එ (E, 411) - b"\xE0\xB6\x92".decode(), # ඒ (Ee, 412) - b"\xE0\xB6\x93".decode(), # ඓ (Ai, 413) - b"\xE0\xB6\x94".decode(), # ඔ (O, 414) - b"\xE0\xB6\x95".decode(), # ඕ (Oo, 415) - b"\xE0\xB6\x96".decode(), # ඖ (Au, 416) - b"\xE0\xB6\x9A".decode(), # ක (Ka, 417) - b"\xE0\xB6\x9B".decode(), # ඛ (Kha, 418) - b"\xE0\xB6\x9C".decode(), # ග (Ga, 419) - b"\xE0\xB6\x9D".decode(), # ඝ (Gha, 420) - b"\xE0\xB6\x9E".decode(), # ඞ (Nga, 421) - b"\xE0\xB6\x9F".decode(), # ච (Cha, 422) - b"\xE0\xB6\xA0".decode(), # ඡ (Chha, 423) - b"\xE0\xB6\xA1".decode(), # ජ (Ja, 424) - b"\xE0\xB6\xA2".decode(), # ඣ (Jha, 425) - b"\xE0\xB6\xA3".decode(), # ඤ (Nya, 426) - b"\xE0\xB6\xA4".decode(), # ට (Ta, 427) - b"\xE0\xB6\xA5".decode(), # ඥ (Tha, 428) - b"\xE0\xB6\xA6".decode(), # ඦ (Da, 429) - b"\xE0\xB6\xA7".decode(), # ට (Dha, 430) - b"\xE0\xB6\xA8".decode(), # ඨ (Na, 431) - b"\xE0\xB6\xAA".decode(), # ඪ (Pa, 432) - b"\xE0\xB6\xAB".decode(), # ණ (Pha, 433) - b"\xE0\xB6\xAC".decode(), # ඬ (Ba, 434) - b"\xE0\xB6\xAD".decode(), # ත (Bha, 435) - b"\xE0\xB6\xAE".decode(), # ථ (Ma, 436) - b"\xE0\xB6\xAF".decode(), # ද (Ya, 437) - b"\xE0\xB6\xB0".decode(), # ධ (Ra, 438) - + b"\xce\xa4".decode(), # Τ (Upper case Tau, 0) + b"\xce\xb1".decode(), # α (Alpha, 1) + b"\xce\xb2".decode(), # β (Beta, 2) + b"\xce\xb3".decode(), # γ (Gamma, 3) + b"\xce\xb4".decode(), # δ (Delta, 4) + b"\xce\xb5".decode(), # ε (Epsilon, 5) + b"\xce\xb6".decode(), # ζ (Zeta, 6) + b"\xce\xb7".decode(), # η (Eta, 7) + b"\xce\xb8".decode(), # θ (Theta, 8) + b"\xce\xb9".decode(), # ι (Iota, 9) + b"\xce\xba".decode(), # κ (Kappa, 10) + b"\xce\xbb".decode(), # λ (Lambda, 11) + b"\xce\xbc".decode(), # μ (Mu, 12) + b"\xce\xbd".decode(), # ν (Nu, 13) + b"\xce\xbe".decode(), # ξ (Xi, 14) + b"\xce\xbf".decode(), # ο (Omicron, 15) + b"\xcf\x80".decode(), # π (Pi, 16) + b"\xcf\x81".decode(), # ρ (Rho, 17) + b"\xcf\x83".decode(), # σ (Sigma, 18) + "t", # t (Tau, 19) + b"\xcf\x85".decode(), # υ (Upsilon, 20) + b"\xcf\x86".decode(), # φ (Phi, 21) + b"\xcf\x87".decode(), # χ (Chi, 22) + b"\xcf\x88".decode(), # ψ (Psi, 23) + b"\xcf\x89".decode(), # ω (Omega, 24) + b"\xd7\x90".decode(), # א (Aleph, 25) + b"\xd7\x91".decode(), # ב (Bet, 26) + b"\xd7\x92".decode(), # ג (Gimel, 27) + b"\xd7\x93".decode(), # ד (Dalet, 28) + b"\xd7\x94".decode(), # ה (He, 29) + b"\xd7\x95".decode(), # ו (Vav, 30) + b"\xd7\x96".decode(), # ז (Zayin, 31) + b"\xd7\x97".decode(), # ח (Het, 32) + b"\xd7\x98".decode(), # ט (Tet, 33) + b"\xd7\x99".decode(), # י (Yod, 34) + b"\xd7\x9a".decode(), # ך (Final Kaf, 35) + b"\xd7\x9b".decode(), # כ (Kaf, 36) + b"\xd7\x9c".decode(), # ל (Lamed, 37) + b"\xd7\x9d".decode(), # ם (Final Mem, 38) + b"\xd7\x9e".decode(), # מ (Mem, 39) + b"\xd7\x9f".decode(), # ן (Final Nun, 40) + b"\xd7\xa0".decode(), # נ (Nun, 41) + b"\xd7\xa1".decode(), # ס (Samekh, 42) + b"\xd7\xa2".decode(), # ע (Ayin, 43) + b"\xd7\xa3".decode(), # ף (Final Pe, 44) + b"\xd7\xa4".decode(), # פ (Pe, 45) + b"\xd7\xa5".decode(), # ץ (Final Tsadi, 46) + b"\xd7\xa6".decode(), # צ (Tsadi, 47) + b"\xd7\xa7".decode(), # ק (Qof, 48) + b"\xd7\xa8".decode(), # ר (Resh, 49) + b"\xd7\xa9".decode(), # ש (Shin, 50) + b"\xd7\xaa".decode(), # ת (Tav, 51) + b"\xd8\xa7".decode(), # ا (Alif, 52) + b"\xd8\xa8".decode(), # ب (Ba, 53) + b"\xd8\xaa".decode(), # ت (Ta, 54) + b"\xd8\xab".decode(), # ث (Tha, 55) + b"\xd8\xac".decode(), # ج (Jim, 56) + b"\xd8\xad".decode(), # ح (Ha, 57) + b"\xd8\xae".decode(), # خ (Kha, 58) + b"\xd8\xaf".decode(), # د (Dal, 59) + b"\xd8\xb0".decode(), # ذ (Dhal, 60) + b"\xd8\xb1".decode(), # ر (Ra, 61) + b"\xd8\xb2".decode(), # ز (Zay, 62) + b"\xd8\xb3".decode(), # س (Sin, 63) + b"\xd8\xb4".decode(), # ش (Shin, 64) + b"\xd8\xb5".decode(), # ص (Sad, 65) + b"\xd8\xb6".decode(), # ض (Dad, 66) + b"\xd8\xb7".decode(), # ط (Ta, 67) + b"\xd8\xb8".decode(), # ظ (Dha, 68) + b"\xd8\xb9".decode(), # ع (Ain, 69) + b"\xd8\xba".decode(), # غ (Ghayn, 70) + b"\xd9\x81".decode(), # ف (Fa, 71) + b"\xd9\x82".decode(), # ق (Qaf, 72) + b"\xd9\x83".decode(), # ك (Kaf, 73) + b"\xd9\x84".decode(), # ل (Lam, 74) + b"\xd9\x85".decode(), # م (Mim, 75) + b"\xd9\x86".decode(), # ن (Nun, 76) + b"\xd9\x87".decode(), # ه (Ha, 77) + b"\xd9\x88".decode(), # و (Waw, 78) + b"\xd9\x8a".decode(), # ي (Ya, 79) + b"\xd9\x89".decode(), # ى (Alef Maksura, 80) + b"\xe1\x9a\xa0".decode(), # ᚠ (Fehu, wealth, 81) + b"\xe1\x9a\xa2".decode(), # ᚢ (Uruz, strength, 82) + b"\xe1\x9a\xa6".decode(), # ᚦ (Thurisaz, giant, 83) + b"\xe1\x9a\xa8".decode(), # ᚨ (Ansuz, god, 84) + b"\xe1\x9a\xb1".decode(), # ᚱ (Raidho, ride, 85) + b"\xe1\x9a\xb3".decode(), # ᚲ (Kaunan, ulcer, 86) + b"\xd0\xab".decode(), # Ы (Cyrillic Yeru, 87) + b"\xe1\x9b\x89".decode(), # ᛉ (Algiz, protection, 88) + b"\xe1\x9b\x92".decode(), # ᛒ (Berkanan, birch, 89) + b"\xe1\x9a\x80".decode(), #   (Space, 90) + b"\xe1\x9a\x81".decode(), # ᚁ (Beith, birch, 91) + b"\xe1\x9a\x82".decode(), # ᚂ (Luis, rowan, 92) + b"\xe1\x9a\x83".decode(), # ᚃ (Fearn, alder, 93) + b"\xe1\x9a\x84".decode(), # ᚄ (Sail, willow, 94) + b"\xe1\x9a\x85".decode(), # ᚅ (Nion, ash, 95) + b"\xe1\x9a\x9b".decode(), # ᚛ (Forfeda, 96) + b"\xe1\x83\x90".decode(), # ა (Ani, 97) + b"\xe1\x83\x91".decode(), # ბ (Bani, 98) + b"\xe1\x83\x92".decode(), # გ (Gani, 99) + b"\xe1\x83\x93".decode(), # დ (Doni, 100) + b"\xe1\x83\x94".decode(), # ე (Eni, 101) + b"\xe1\x83\x95".decode(), # ვ (Vini, 102) + b"\xd4\xb1".decode(), # Ա (Ayp, 103) + b"\xd4\xb2".decode(), # Բ (Ben, 104) + b"\xd4\xb3".decode(), # Գ (Gim, 105) + b"\xd4\xb4".decode(), # Դ (Da, 106) + b"\xd4\xb5".decode(), # Ե (Ech, 107) + b"\xd4\xb6".decode(), # Զ (Za, 108) + b"\xd5\x9e".decode(), # ՞ (Question mark, 109) + b"\xd0\x80".decode(), # Ѐ (Ie with grave, 110) + b"\xd0\x81".decode(), # Ё (Io, 111) + b"\xd0\x82".decode(), # Ђ (Dje, 112) + b"\xd0\x83".decode(), # Ѓ (Gje, 113) + b"\xd0\x84".decode(), # Є (Ukrainian Ie, 114) + b"\xd0\x85".decode(), # Ѕ (Dze, 115) + b"\xd1\x8a".decode(), # Ъ (Hard sign, 116) + b"\xe2\xb2\x80".decode(), # Ⲁ (Alfa, 117) + b"\xe2\xb2\x81".decode(), # ⲁ (Small Alfa, 118) + b"\xe2\xb2\x82".decode(), # Ⲃ (Vida, 119) + b"\xe2\xb2\x83".decode(), # ⲃ (Small Vida, 120) + b"\xe2\xb2\x84".decode(), # Ⲅ (Gamma, 121) + b"\xe2\xb2\x85".decode(), # ⲅ (Small Gamma, 122) + b"\xf0\x91\x80\x80".decode(), # 𑀀 (A, 123) + b"\xf0\x91\x80\x81".decode(), # 𑀁 (Aa, 124) + b"\xf0\x91\x80\x82".decode(), # 𑀂 (I, 125) + b"\xf0\x91\x80\x83".decode(), # 𑀃 (Ii, 126) + b"\xf0\x91\x80\x85".decode(), # 𑀅 (U, 127) + b"\xe0\xb6\xb1".decode(), # ඲ (La, 128) + b"\xe0\xb6\xb2".decode(), # ඳ (Va, 129) + b"\xe0\xb6\xb3".decode(), # ප (Sha, 130) + b"\xe0\xb6\xb4".decode(), # ඵ (Ssa, 131) + b"\xe0\xb6\xb5".decode(), # බ (Sa, 132) + b"\xe0\xb6\xb6".decode(), # භ (Ha, 133) + b"\xe2\xb0\x80".decode(), # Ⰰ (Az, 134) + b"\xe2\xb0\x81".decode(), # Ⰱ (Buky, 135) + b"\xe2\xb0\x82".decode(), # Ⰲ (Vede, 136) + b"\xe2\xb0\x83".decode(), # Ⰳ (Glagoli, 137) + b"\xe2\xb0\x84".decode(), # Ⰴ (Dobro, 138) + b"\xe2\xb0\x85".decode(), # Ⰵ (Yest, 139) + b"\xe2\xb0\x86".decode(), # Ⰶ (Zhivete, 140) + b"\xe2\xb0\x87".decode(), # Ⰷ (Zemlja, 141) + b"\xe2\xb0\x88".decode(), # Ⰸ (Izhe, 142) + b"\xe2\xb0\x89".decode(), # Ⰹ (Initial Izhe, 143) + b"\xe2\xb0\x8a".decode(), # Ⰺ (I, 144) + b"\xe2\xb0\x8b".decode(), # Ⰻ (Djerv, 145) + b"\xe2\xb0\x8c".decode(), # Ⰼ (Kako, 146) + b"\xe2\xb0\x8d".decode(), # Ⰽ (Ljudije, 147) + b"\xe2\xb0\x8e".decode(), # Ⰾ (Myse, 148) + b"\xe2\xb0\x8f".decode(), # Ⰿ (Nash, 149) + b"\xe2\xb0\x90".decode(), # Ⱀ (On, 150) + b"\xe2\xb0\x91".decode(), # Ⱁ (Pokoj, 151) + b"\xe2\xb0\x92".decode(), # Ⱂ (Rtsy, 152) + b"\xe2\xb0\x93".decode(), # Ⱃ (Slovo, 153) + b"\xe2\xb0\x94".decode(), # Ⱄ (Tvrido, 154) + b"\xe2\xb0\x95".decode(), # Ⱅ (Uku, 155) + b"\xe2\xb0\x96".decode(), # Ⱆ (Fert, 156) + b"\xe2\xb0\x97".decode(), # Ⱇ (Xrivi, 157) + b"\xe2\xb0\x98".decode(), # Ⱈ (Ot, 158) + b"\xe2\xb0\x99".decode(), # Ⱉ (Cy, 159) + b"\xe2\xb0\x9a".decode(), # Ⱊ (Shcha, 160) + b"\xe2\xb0\x9b".decode(), # Ⱋ (Er, 161) + b"\xe2\xb0\x9c".decode(), # Ⱌ (Yeru, 162) + b"\xe2\xb0\x9d".decode(), # Ⱍ (Small Yer, 163) + b"\xe2\xb0\x9e".decode(), # Ⱎ (Yo, 164) + b"\xe2\xb0\x9f".decode(), # Ⱏ (Yu, 165) + b"\xe2\xb0\xa0".decode(), # Ⱐ (Ja, 166) + b"\xe0\xb8\x81".decode(), # ก (Ko Kai, 167) + b"\xe0\xb8\x82".decode(), # ข (Kho Khai, 168) + b"\xe0\xb8\x83".decode(), # ฃ (Kho Khuat, 169) + b"\xe0\xb8\x84".decode(), # ค (Kho Khon, 170) + b"\xe0\xb8\x85".decode(), # ฅ (Kho Rakhang, 171) + b"\xe0\xb8\x86".decode(), # ฆ (Kho Khwai, 172) + b"\xe0\xb8\x87".decode(), # ง (Ngo Ngu, 173) + b"\xe0\xb8\x88".decode(), # จ (Cho Chan, 174) + b"\xe0\xb8\x89".decode(), # ฉ (Cho Ching, 175) + b"\xe0\xb8\x8a".decode(), # ช (Cho Chang, 176) + b"\xe0\xb8\x8b".decode(), # ซ (So So, 177) + b"\xe0\xb8\x8c".decode(), # ฌ (Cho Choe, 178) + b"\xe0\xb8\x8d".decode(), # ญ (Yo Ying, 179) + b"\xe0\xb8\x8e".decode(), # ฎ (Do Chada, 180) + b"\xe0\xb8\x8f".decode(), # ฏ (To Patak, 181) + b"\xe0\xb8\x90".decode(), # ฐ (Tho Than, 182) + b"\xe0\xb8\x91".decode(), # ฑ (Tho Nangmontho, 183) + b"\xe0\xb8\x92".decode(), # ฒ (Tho Phuthao, 184) + b"\xe0\xb8\x93".decode(), # ณ (No Nen, 185) + b"\xe0\xb8\x94".decode(), # ด (Do Dek, 186) + b"\xe0\xb8\x95".decode(), # ต (To Tao, 187) + b"\xe0\xb8\x96".decode(), # ถ (Tho Thung, 188) + b"\xe0\xb8\x97".decode(), # ท (Tho Thahan, 189) + b"\xe0\xb8\x98".decode(), # ธ (Tho Thong, 190) + b"\xe0\xb8\x99".decode(), # น (No Nu, 191) + b"\xe0\xb8\x9a".decode(), # บ (Bo Baimai, 192) + b"\xe0\xb8\x9b".decode(), # ป (Po Pla, 193) + b"\xe0\xb8\x9c".decode(), # ผ (Pho Phung, 194) + b"\xe0\xb8\x9d".decode(), # ฝ (Fo Fa, 195) + b"\xe0\xb8\x9e".decode(), # พ (Pho Phan, 196) + b"\xe0\xb8\x9f".decode(), # ฟ (Fo Fan, 197) + b"\xe0\xb8\xa0".decode(), # ภ (Pho Samphao, 198) + b"\xe0\xb8\xa1".decode(), # ม (Mo Ma, 199) + b"\xe0\xb8\xa2".decode(), # ย (Yo Yak, 200) + b"\xe0\xb8\xa3".decode(), # ร (Ro Rua, 201) + b"\xe0\xb8\xa5".decode(), # ล (Lo Ling, 202) + b"\xe0\xb8\xa7".decode(), # ว (Wo Waen, 203) + b"\xe0\xb8\xa8".decode(), # ศ (So Sala, 204) + b"\xe0\xb8\xa9".decode(), # ษ (So Rusi, 205) + b"\xe0\xb8\xaa".decode(), # ส (So Sua, 206) + b"\xe0\xb8\xab".decode(), # ห (Ho Hip, 207) + b"\xe0\xb8\xac".decode(), # ฬ (Lo Chula, 208) + b"\xe0\xb8\xad".decode(), # อ (O Ang, 209) + b"\xe0\xb8\xae".decode(), # ฮ (Ho Nokhuk, 210) + b"\xe1\x84\x80".decode(), # ㄱ (Giyeok, 211) + b"\xe1\x84\x81".decode(), # ㄴ (Nieun, 212) + b"\xe1\x84\x82".decode(), # ㄷ (Digeut, 213) + b"\xe1\x84\x83".decode(), # ㄹ (Rieul, 214) + b"\xe1\x84\x84".decode(), # ㅁ (Mieum, 215) + b"\xe1\x84\x85".decode(), # ㅂ (Bieup, 216) + b"\xe1\x84\x86".decode(), # ㅅ (Siot, 217) + b"\xe1\x84\x87".decode(), # ㅇ (Ieung, 218) + b"\xe1\x84\x88".decode(), # ㅈ (Jieut, 219) + b"\xe1\x84\x89".decode(), # ㅊ (Chieut, 220) + b"\xe1\x84\x8a".decode(), # ㅋ (Kieuk, 221) + b"\xe1\x84\x8b".decode(), # ㅌ (Tieut, 222) + b"\xe1\x84\x8c".decode(), # ㅍ (Pieup, 223) + b"\xe1\x84\x8d".decode(), # ㅎ (Hieut, 224) + b"\xe1\x85\xa1".decode(), # ㅏ (A, 225) + b"\xe1\x85\xa2".decode(), # ㅐ (Ae, 226) + b"\xe1\x85\xa3".decode(), # ㅑ (Ya, 227) + b"\xe1\x85\xa4".decode(), # ㅒ (Yae, 228) + b"\xe1\x85\xa5".decode(), # ㅓ (Eo, 229) + b"\xe1\x85\xa6".decode(), # ㅔ (E, 230) + b"\xe1\x85\xa7".decode(), # ㅕ (Yeo, 231) + b"\xe1\x85\xa8".decode(), # ㅖ (Ye, 232) + b"\xe1\x85\xa9".decode(), # ㅗ (O, 233) + b"\xe1\x85\xaa".decode(), # ㅘ (Wa, 234) + b"\xe1\x85\xab".decode(), # ㅙ (Wae, 235) + b"\xe1\x85\xac".decode(), # ㅚ (Oe, 236) + b"\xe1\x85\xad".decode(), # ㅛ (Yo, 237) + b"\xe1\x85\xae".decode(), # ㅜ (U, 238) + b"\xe1\x85\xaf".decode(), # ㅝ (Weo, 239) + b"\xe1\x85\xb0".decode(), # ㅞ (We, 240) + b"\xe1\x85\xb1".decode(), # ㅟ (Wi, 241) + b"\xe1\x85\xb2".decode(), # ㅠ (Yu, 242) + b"\xe1\x85\xb3".decode(), # ㅡ (Eu, 243) + b"\xe1\x85\xb4".decode(), # ㅢ (Ui, 244) + b"\xe1\x85\xb5".decode(), # ㅣ (I, 245) + b"\xe1\x8a\xa0".decode(), # አ (Glottal A, 246) + b"\xe1\x8a\xa1".decode(), # ኡ (Glottal U, 247) + b"\xe1\x8a\xa2".decode(), # ኢ (Glottal I, 248) + b"\xe1\x8a\xa3".decode(), # ኣ (Glottal Aa, 249) + b"\xe1\x8a\xa4".decode(), # ኤ (Glottal E, 250) + b"\xe1\x8a\xa5".decode(), # እ (Glottal Ie, 251) + b"\xe1\x8a\xa6".decode(), # ኦ (Glottal O, 252) + b"\xe1\x8a\xa7".decode(), # ኧ (Glottal Wa, 253) + b"\xe1\x8b\x88".decode(), # ወ (Wa, 254) + b"\xe1\x8b\x89".decode(), # ዉ (Wu, 255) + b"\xe1\x8b\x8a".decode(), # ዊ (Wi, 256) + b"\xe1\x8b\x8b".decode(), # ዋ (Waa, 257) + b"\xe1\x8b\x8c".decode(), # ዌ (We, 258) + b"\xe1\x8b\x8d".decode(), # ው (Wye, 259) + b"\xe1\x8b\x8e".decode(), # ዎ (Wo, 260) + b"\xe1\x8a\xb0".decode(), # ኰ (Ko, 261) + b"\xe1\x8a\xb1".decode(), # ኱ (Ku, 262) + b"\xe1\x8a\xb2".decode(), # ኲ (Ki, 263) + b"\xe1\x8a\xb3".decode(), # ኳ (Kua, 264) + b"\xe1\x8a\xb4".decode(), # ኴ (Ke, 265) + b"\xe1\x8a\xb5".decode(), # ኵ (Kwe, 266) + b"\xe1\x8a\xb6".decode(), # ኶ (Ko, 267) + b"\xe1\x8a\x90".decode(), # ጐ (Go, 268) + b"\xe1\x8a\x91".decode(), # ጑ (Gu, 269) + b"\xe1\x8a\x92".decode(), # ጒ (Gi, 270) + b"\xe1\x8a\x93".decode(), # መ (Gua, 271) + b"\xe1\x8a\x94".decode(), # ጔ (Ge, 272) + b"\xe1\x8a\x95".decode(), # ጕ (Gwe, 273) + b"\xe1\x8a\x96".decode(), # ጖ (Go, 274) + b"\xe0\xa4\x85".decode(), # अ (A, 275) + b"\xe0\xa4\x86".decode(), # आ (Aa, 276) + b"\xe0\xa4\x87".decode(), # इ (I, 277) + b"\xe0\xa4\x88".decode(), # ई (Ii, 278) + b"\xe0\xa4\x89".decode(), # उ (U, 279) + b"\xe0\xa4\x8a".decode(), # ऊ (Uu, 280) + b"\xe0\xa4\x8b".decode(), # ऋ (R, 281) + b"\xe0\xa4\x8f".decode(), # ए (E, 282) + b"\xe0\xa4\x90".decode(), # ऐ (Ai, 283) + b"\xe0\xa4\x93".decode(), # ओ (O, 284) + b"\xe0\xa4\x94".decode(), # औ (Au, 285) + b"\xe0\xa4\x95".decode(), # क (Ka, 286) + b"\xe0\xa4\x96".decode(), # ख (Kha, 287) + b"\xe0\xa4\x97".decode(), # ग (Ga, 288) + b"\xe0\xa4\x98".decode(), # घ (Gha, 289) + b"\xe0\xa4\x99".decode(), # ङ (Nga, 290) + b"\xe0\xa4\x9a".decode(), # च (Cha, 291) + b"\xe0\xa4\x9b".decode(), # छ (Chha, 292) + b"\xe0\xa4\x9c".decode(), # ज (Ja, 293) + b"\xe0\xa4\x9d".decode(), # झ (Jha, 294) + b"\xe0\xa4\x9e".decode(), # ञ (Nya, 295) + b"\xe0\xa4\x9f".decode(), # ट (Ta, 296) + b"\xe0\xa4\xa0".decode(), # ठ (Tha, 297) + b"\xe0\xa4\xa1".decode(), # ड (Da, 298) + b"\xe0\xa4\xa2".decode(), # ढ (Dha, 299) + b"\xe0\xa4\xa3".decode(), # ण (Na, 300) + b"\xe0\xa4\xa4".decode(), # त (Ta, 301) + b"\xe0\xa4\xa5".decode(), # थ (Tha, 302) + b"\xe0\xa4\xa6".decode(), # द (Da, 303) + b"\xe0\xa4\xa7".decode(), # ध (Dha, 304) + b"\xe0\xa4\xa8".decode(), # न (Na, 305) + b"\xe0\xa4\xaa".decode(), # प (Pa, 306) + b"\xe0\xa4\xab".decode(), # फ (Pha, 307) + b"\xe0\xa4\xac".decode(), # ब (Ba, 308) + b"\xe0\xa4\xad".decode(), # भ (Bha, 309) + b"\xe0\xa4\xae".decode(), # म (Ma, 310) + b"\xe0\xa4\xaf".decode(), # य (Ya, 311) + b"\xe0\xa4\xb0".decode(), # र (Ra, 312) + b"\xe0\xa4\xb2".decode(), # ल (La, 313) + b"\xe0\xa4\xb5".decode(), # व (Va, 314) + b"\xe0\xa4\xb6".decode(), # श (Sha, 315) + b"\xe0\xa4\xb7".decode(), # ष (Ssa, 316) + b"\xe0\xa4\xb8".decode(), # स (Sa, 317) + b"\xe0\xa4\xb9".decode(), # ह (Ha, 318) + b"\xe3\x82\xa2".decode(), # ア (A, 319) + b"\xe3\x82\xa4".decode(), # イ (I, 320) + b"\xe3\x82\xa6".decode(), # ウ (U, 321) + b"\xe3\x82\xa8".decode(), # エ (E, 322) + b"\xe3\x82\xaa".decode(), # オ (O, 323) + b"\xe3\x82\xab".decode(), # カ (Ka, 324) + b"\xe3\x82\xad".decode(), # キ (Ki, 325) + b"\xe3\x82\xaf".decode(), # ク (Ku, 326) + b"\xe3\x82\xb1".decode(), # ケ (Ke, 327) + b"\xe3\x82\xb3".decode(), # コ (Ko, 328) + b"\xe3\x82\xb5".decode(), # サ (Sa, 329) + b"\xe3\x82\xb7".decode(), # シ (Shi, 330) + b"\xe3\x82\xb9".decode(), # ス (Su, 331) + b"\xe3\x82\xbb".decode(), # セ (Se, 332) + b"\xe3\x82\xbd".decode(), # ソ (So, 333) + b"\xe3\x82\xbf".decode(), # タ (Ta, 334) + b"\xe3\x83\x81".decode(), # チ (Chi, 335) + b"\xe3\x83\x84".decode(), # ツ (Tsu, 336) + b"\xe3\x83\x86".decode(), # テ (Te, 337) + b"\xe3\x83\x88".decode(), # ト (To, 338) + b"\xe3\x83\x8a".decode(), # ナ (Na, 339) + b"\xe3\x83\x8b".decode(), # ニ (Ni, 340) + b"\xe3\x83\x8c".decode(), # ヌ (Nu, 341) + b"\xe3\x83\x8d".decode(), # ネ (Ne, 342) + b"\xe3\x83\x8e".decode(), # ノ (No, 343) + b"\xe3\x83\x8f".decode(), # ハ (Ha, 344) + b"\xe3\x83\x92".decode(), # ヒ (Hi, 345) + b"\xe3\x83\x95".decode(), # フ (Fu, 346) + b"\xe3\x83\x98".decode(), # ヘ (He, 347) + b"\xe3\x83\x9b".decode(), # ホ (Ho, 348) + b"\xe3\x83\x9e".decode(), # マ (Ma, 349) + b"\xe3\x83\x9f".decode(), # ミ (Mi, 350) + b"\xe3\x83\xa0".decode(), # ム (Mu, 351) + b"\xe3\x83\xa1".decode(), # メ (Me, 352) + b"\xe3\x83\xa2".decode(), # モ (Mo, 353) + b"\xe3\x83\xa4".decode(), # ヤ (Ya, 354) + b"\xe3\x83\xa6".decode(), # ユ (Yu, 355) + b"\xe3\x83\xa8".decode(), # ヨ (Yo, 356) + b"\xe3\x83\xa9".decode(), # ラ (Ra, 357) + b"\xe3\x83\xaa".decode(), # リ (Ri, 358) + b"\xe3\x83\xab".decode(), # ル (Ru, 359) + b"\xe3\x83\xac".decode(), # レ (Re, 360) + b"\xe3\x83\xad".decode(), # ロ (Ro, 361) + b"\xe3\x83\xaf".decode(), # ワ (Wa, 362) + b"\xe3\x83\xb2".decode(), # ヲ (Wo, 363) + b"\xe3\x83\xb3".decode(), # ン (N, 364) + b"\xe2\xb4\xb0".decode(), # ⴰ (Ya, 365) + b"\xe2\xb4\xb1".decode(), # ⴱ (Yab, 366) + b"\xe2\xb4\xb2".decode(), # ⴲ (Yabh, 367) + b"\xe2\xb4\xb3".decode(), # ⴳ (Yag, 368) + b"\xe2\xb4\xb4".decode(), # ⴴ (Yagh, 369) + b"\xe2\xb4\xb5".decode(), # ⴵ (Yaj, 370) + b"\xe2\xb4\xb6".decode(), # ⴶ (Yach, 371) + b"\xe2\xb4\xb7".decode(), # ⴷ (Yad, 372) + b"\xe2\xb4\xb8".decode(), # ⴸ (Yadh, 373) + b"\xe2\xb4\xb9".decode(), # ⴹ (Yadh, emphatic, 374) + b"\xe2\xb4\xba".decode(), # ⴺ (Yaz, 375) + b"\xe2\xb4\xbb".decode(), # ⴻ (Yazh, 376) + b"\xe2\xb4\xbc".decode(), # ⴼ (Yaf, 377) + b"\xe2\xb4\xbd".decode(), # ⴽ (Yak, 378) + b"\xe2\xb4\xbe".decode(), # ⴾ (Yak, variant, 379) + b"\xe2\xb4\xbf".decode(), # ⴿ (Yaq, 380) + b"\xe2\xb5\x80".decode(), # ⵀ (Yah, 381) + b"\xe2\xb5\x81".decode(), # ⵁ (Yahh, 382) + b"\xe2\xb5\x82".decode(), # ⵂ (Yahl, 383) + b"\xe2\xb5\x83".decode(), # ⵃ (Yahm, 384) + b"\xe2\xb5\x84".decode(), # ⵄ (Yayn, 385) + b"\xe2\xb5\x85".decode(), # ⵅ (Yakh, 386) + b"\xe2\xb5\x86".decode(), # ⵆ (Yakl, 387) + b"\xe2\xb5\x87".decode(), # ⵇ (Yahq, 388) + b"\xe2\xb5\x88".decode(), # ⵈ (Yash, 389) + b"\xe2\xb5\x89".decode(), # ⵉ (Yi, 390) + b"\xe2\xb5\x8a".decode(), # ⵊ (Yij, 391) + b"\xe2\xb5\x8b".decode(), # ⵋ (Yizh, 392) + b"\xe2\xb5\x8c".decode(), # ⵌ (Yink, 393) + b"\xe2\xb5\x8d".decode(), # ⵍ (Yal, 394) + b"\xe2\xb5\x8e".decode(), # ⵎ (Yam, 395) + b"\xe2\xb5\x8f".decode(), # ⵏ (Yan, 396) + b"\xe2\xb5\x90".decode(), # ⵐ (Yang, 397) + b"\xe2\xb5\x91".decode(), # ⵑ (Yany, 398) + b"\xe2\xb5\x92".decode(), # ⵒ (Yap, 399) + b"\xe2\xb5\x93".decode(), # ⵓ (Yu, 400) + b"\xe0\xb6\x85".decode(), # අ (A, 401) + b"\xe0\xb6\x86".decode(), # ආ (Aa, 402) + b"\xe0\xb6\x87".decode(), # ඉ (I, 403) + b"\xe0\xb6\x88".decode(), # ඊ (Ii, 404) + b"\xe0\xb6\x89".decode(), # උ (U, 405) + b"\xe0\xb6\x8a".decode(), # ඌ (Uu, 406) + b"\xe0\xb6\x8b".decode(), # ඍ (R, 407) + b"\xe0\xb6\x8c".decode(), # ඎ (Rr, 408) + b"\xe0\xb6\x8f".decode(), # ඏ (L, 409) + b"\xe0\xb6\x90".decode(), # ඐ (Ll, 410) + b"\xe0\xb6\x91".decode(), # එ (E, 411) + b"\xe0\xb6\x92".decode(), # ඒ (Ee, 412) + b"\xe0\xb6\x93".decode(), # ඓ (Ai, 413) + b"\xe0\xb6\x94".decode(), # ඔ (O, 414) + b"\xe0\xb6\x95".decode(), # ඕ (Oo, 415) + b"\xe0\xb6\x96".decode(), # ඖ (Au, 416) + b"\xe0\xb6\x9a".decode(), # ක (Ka, 417) + b"\xe0\xb6\x9b".decode(), # ඛ (Kha, 418) + b"\xe0\xb6\x9c".decode(), # ග (Ga, 419) + b"\xe0\xb6\x9d".decode(), # ඝ (Gha, 420) + b"\xe0\xb6\x9e".decode(), # ඞ (Nga, 421) + b"\xe0\xb6\x9f".decode(), # ච (Cha, 422) + b"\xe0\xb6\xa0".decode(), # ඡ (Chha, 423) + b"\xe0\xb6\xa1".decode(), # ජ (Ja, 424) + b"\xe0\xb6\xa2".decode(), # ඣ (Jha, 425) + b"\xe0\xb6\xa3".decode(), # ඤ (Nya, 426) + b"\xe0\xb6\xa4".decode(), # ට (Ta, 427) + b"\xe0\xb6\xa5".decode(), # ඥ (Tha, 428) + b"\xe0\xb6\xa6".decode(), # ඦ (Da, 429) + b"\xe0\xb6\xa7".decode(), # ට (Dha, 430) + b"\xe0\xb6\xa8".decode(), # ඨ (Na, 431) + b"\xe0\xb6\xaa".decode(), # ඪ (Pa, 432) + b"\xe0\xb6\xab".decode(), # ණ (Pha, 433) + b"\xe0\xb6\xac".decode(), # ඬ (Ba, 434) + b"\xe0\xb6\xad".decode(), # ත (Bha, 435) + b"\xe0\xb6\xae".decode(), # ථ (Ma, 436) + b"\xe0\xb6\xaf".decode(), # ද (Ya, 437) + b"\xe0\xb6\xb0".decode(), # ධ (Ra, 438) ] NETWORK_EXPLORER_MAP = { diff --git a/bittensor_cli/src/bittensor/utils.py b/bittensor_cli/src/bittensor/utils.py index b24cea568..992c2a2e3 100644 --- a/bittensor_cli/src/bittensor/utils.py +++ b/bittensor_cli/src/bittensor/utils.py @@ -31,6 +31,7 @@ if TYPE_CHECKING: from bittensor_cli.src.bittensor.chain_data import SubnetHyperparameters + from rich.prompt import PromptBase console = Console() json_console = Console() @@ -1007,7 +1008,7 @@ def retry_prompt( rejection_text: str, default="", show_default=False, - prompt_type=Prompt.ask, + prompt_type: "PromptBase.ask" = Prompt.ask, ): """ Allows for asking prompts again if they do not meet a certain criteria (as defined in `rejection`) diff --git a/bittensor_cli/src/commands/subnets/subnets.py b/bittensor_cli/src/commands/subnets/subnets.py index fac969791..f5eb939c3 100644 --- a/bittensor_cli/src/commands/subnets/subnets.py +++ b/bittensor_cli/src/commands/subnets/subnets.py @@ -10,7 +10,7 @@ from rich.table import Column, Table from rich import box -from bittensor_cli.src import COLOR_PALETTE +from bittensor_cli.src import COLOR_PALETTE, Constants from bittensor_cli.src.bittensor.balances import Balance from bittensor_cli.src.bittensor.extrinsics.registration import ( register_extrinsic, @@ -34,6 +34,7 @@ prompt_for_identity, get_subnet_name, unlock_key, + blocks_to_duration, json_console, ) @@ -2308,3 +2309,112 @@ async def get_identity( else: console.print(table) return identity + + +async def get_start_schedule( + subtensor: "SubtensorInterface", + netuid: int, +) -> None: + """Fetch and display existing emission schedule information.""" + + if not await subtensor.subnet_exists(netuid): + print_error(f"Subnet {netuid} does not exist.") + return None + + registration_block = await subtensor.query( + module="SubtensorModule", + storage_function="NetworkRegisteredAt", + params=[netuid], + ) + min_blocks_to_start = Constants.emission_start_schedule + current_block = await subtensor.substrate.get_block_number() + + potential_start_block = registration_block + min_blocks_to_start + if current_block < potential_start_block: + blocks_to_wait = potential_start_block - current_block + time_to_wait = blocks_to_duration(blocks_to_wait) + + console.print( + f"[blue]Subnet {netuid}[/blue]:\n" + f"[blue]Registered at:[/blue] {registration_block}\n" + f"[blue]Minimum start block:[/blue] {potential_start_block}\n" + f"[blue]Current block:[/blue] {current_block}\n" + f"[blue]Blocks remaining:[/blue] {blocks_to_wait}\n" + f"[blue]Time to wait:[/blue] {time_to_wait}" + ) + else: + console.print( + f"[blue]Subnet {netuid}[/blue]:\n" + f"[blue]Registered at:[/blue] {registration_block}\n" + f"[blue]Current block:[/blue] {current_block}\n" + f"[blue]Minimum start block:[/blue] {potential_start_block}\n" + f"[dark_sea_green3]Emission schedule can be started[/dark_sea_green3]" + ) + + return + + +async def start_subnet( + wallet: "Wallet", + subtensor: "SubtensorInterface", + netuid: int, + prompt: bool = False, +) -> bool: + """Start a subnet's emission schedule""" + + if not await subtensor.subnet_exists(netuid): + print_error(f"Subnet {netuid} does not exist.") + return False + + subnet_owner = await subtensor.query( + module="SubtensorModule", + storage_function="SubnetOwner", + params=[netuid], + ) + if subnet_owner != wallet.coldkeypub.ss58_address: + print_error(":cross_mark: This wallet doesn't own the specified subnet.") + return False + + if prompt: + if not Confirm.ask( + f"Are you sure you want to start subnet {netuid}'s emission schedule?" + ): + return False + + if not unlock_key(wallet).success: + return False + + with console.status( + f":satellite: Starting subnet {netuid}'s emission schedule...", spinner="earth" + ): + start_call = await subtensor.substrate.compose_call( + call_module="SubtensorModule", + call_function="start_call", + call_params={"netuid": netuid}, + ) + + signed_ext = await subtensor.substrate.create_signed_extrinsic( + call=start_call, + keypair=wallet.coldkey, + ) + + response = await subtensor.substrate.submit_extrinsic( + extrinsic=signed_ext, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + + if await response.is_success: + console.print( + f":white_heavy_check_mark: [green]Successfully started subnet {netuid}'s emission schedule.[/green]" + ) + return True + else: + error_msg = format_error_message(await response.error_message) + if "FirstEmissionBlockNumberAlreadySet" in error_msg: + console.print(f"[dark_sea_green3]Subnet {netuid} already has an emission schedule.[/dark_sea_green3]") + return True + + await get_start_schedule(subtensor, netuid) + print_error(f":cross_mark: Failed to start subnet: {error_msg}") + return False diff --git a/bittensor_cli/version.py b/bittensor_cli/version.py index d66077f5b..65e2ca413 100644 --- a/bittensor_cli/version.py +++ b/bittensor_cli/version.py @@ -3,18 +3,20 @@ def version_as_int(version): - _core_version = re.match(r"^\d+\.\d+\.\d+", version).group(0) - _version_split = _core_version.split(".") - __version_info__ = tuple(int(part) for part in _version_split) - _version_int_base = 1000 - assert max(__version_info__) < _version_int_base + match = re.match(r"^\d+\.\d+\.\d+", version) + if not match: + raise ValueError(f"Invalid version format: {version}") + core_version = match.group(0) + version_split = core_version.split(".") + version_info = tuple(int(part) for part in version_split) + version_int_base = 1000 + assert max(version_info) < version_int_base - __version_as_int__: int = sum( - e * (_version_int_base**i) for i, e in enumerate(reversed(__version_info__)) + version_as_int_: int = sum( + e * (version_int_base**i) for i, e in enumerate(reversed(version_info)) ) - assert __version_as_int__ < 2**31 # fits in int32 - __new_signature_version__ = 360 - return __version_as_int__ + assert version_as_int_ < 2**31 # fits in int32 + return version_as_int_ __version__ = importlib.metadata.version("bittensor-cli") diff --git a/pyproject.toml b/pyproject.toml index 840807700..c9843b421 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "bittensor-cli" -version = "9.3.0" +version = "9.4.0" description = "Bittensor CLI" readme = "README.md" authors = [ @@ -39,7 +39,7 @@ dependencies = [ [project.optional-dependencies] cuda = [ - "torch>=1.13.1,<2.6.0", + "torch>=1.13.1,<3.0", ] [project.urls] diff --git a/tests/e2e_tests/test_staking_sudo.py b/tests/e2e_tests/test_staking_sudo.py index b34b05b17..0877f64be 100644 --- a/tests/e2e_tests/test_staking_sudo.py +++ b/tests/e2e_tests/test_staking_sudo.py @@ -298,7 +298,7 @@ def test_staking(local_chain, wallet_setup): # Parse all hyperparameters and single out max_burn in TAO all_hyperparams = hyperparams.stdout.splitlines() - max_burn_tao = all_hyperparams[22].split()[2].strip('\u200e') + max_burn_tao = all_hyperparams[22].split()[2].strip("\u200e") # Assert max_burn is 100 TAO from default assert Balance.from_tao(float(max_burn_tao)) == Balance.from_tao(100.0) @@ -359,7 +359,7 @@ def test_staking(local_chain, wallet_setup): # Parse updated hyperparameters all_updated_hyperparams = updated_hyperparams.stdout.splitlines() - updated_max_burn_tao = all_updated_hyperparams[22].split()[2].strip('\u200e') + updated_max_burn_tao = all_updated_hyperparams[22].split()[2].strip("\u200e") # Assert max_burn is now 10 TAO assert Balance.from_tao(float(updated_max_burn_tao)) == Balance.from_tao(10) diff --git a/tests/e2e_tests/test_unstaking.py b/tests/e2e_tests/test_unstaking.py index c9f99796c..02b25489c 100644 --- a/tests/e2e_tests/test_unstaking.py +++ b/tests/e2e_tests/test_unstaking.py @@ -1,8 +1,10 @@ +import asyncio import json import re from bittensor_cli.src.bittensor.balances import Balance +from btcli.tests.e2e_tests.utils import set_storage_extrinsic def test_unstaking(local_chain, wallet_setup): """ @@ -34,6 +36,19 @@ def test_unstaking(local_chain, wallet_setup): wallet_path_bob ) + # Call to make Alice root owner + items = [ + ( + bytes.fromhex("658faa385070e074c85bf6b568cf055536e3e82152c8758267395fe524fbbd160000"), + bytes.fromhex("d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d") + ) + ] + asyncio.run(set_storage_extrinsic( + local_chain, + wallet=wallet_alice, + items=items, + )) + # Create first subnet (netuid = 2) result = exec_command_alice( command="subnets", @@ -98,6 +113,65 @@ def test_unstaking(local_chain, wallet_setup): ) assert "✅ Registered subnetwork with netuid: 3" in result.stdout + # Start emission schedule for subnets + start_call_netuid_0 = exec_command_alice( + command="subnets", + sub_command="start", + extra_args=[ + "--netuid", + "0", + "--wallet-name", + wallet_alice.name, + "--no-prompt", + "--chain", + "ws://127.0.0.1:9945", + "--wallet-path", + wallet_path_alice, + ], + ) + assert ( + "Successfully started subnet 0's emission schedule." + in start_call_netuid_0.stdout + ) + start_call_netuid_2 = exec_command_alice( + command="subnets", + sub_command="start", + extra_args=[ + "--netuid", + "2", + "--wallet-name", + wallet_alice.name, + "--no-prompt", + "--chain", + "ws://127.0.0.1:9945", + "--wallet-path", + wallet_path_alice, + ], + ) + assert ( + "Successfully started subnet 2's emission schedule." + in start_call_netuid_2.stdout + ) + + start_call_netuid_3 = exec_command_alice( + command="subnets", + sub_command="start", + extra_args=[ + "--netuid", + "3", + "--wallet-name", + wallet_alice.name, + "--no-prompt", + "--chain", + "ws://127.0.0.1:9945", + "--wallet-path", + wallet_path_alice, + ], + ) + assert ( + "Successfully started subnet 3's emission schedule." + in start_call_netuid_3.stdout + ) # Register Bob in one subnet register_result = exec_command_bob( command="subnets", diff --git a/tests/e2e_tests/utils.py b/tests/e2e_tests/utils.py index 490ae5941..07f2fbd93 100644 --- a/tests/e2e_tests/utils.py +++ b/tests/e2e_tests/utils.py @@ -3,7 +3,7 @@ import shutil import subprocess import sys -from typing import List, Tuple, TYPE_CHECKING +from typing import TYPE_CHECKING, Optional from bittensor_cli.cli import CLIManager from bittensor_wallet import Keypair, Wallet @@ -27,9 +27,10 @@ def setup_wallet(uri: str): def exec_command( command: str, sub_command: str, - extra_args: List[str] = [], - inputs: List[str] = None, + extra_args: Optional[list[str]] = None, + inputs: list[str] = None, ): + extra_args = extra_args or [] cli_manager = CLIManager() # Capture stderr separately from stdout runner = CliRunner(mix_stderr=False) @@ -57,13 +58,15 @@ def exec_command( return keypair, wallet, wallet_path, exec_command -def extract_coldkey_balance(cleaned_text: str, wallet_name: str, coldkey_address: str) -> dict: +def extract_coldkey_balance( + cleaned_text: str, wallet_name: str, coldkey_address: str +) -> dict: """ Extracts the free, staked, and total balances for a given wallet name and coldkey address from the input string. Args: - text (str): The input string from wallet list command. + cleaned_text (str): The input string from wallet list command. wallet_name (str): The name of the wallet. coldkey_address (str): The coldkey address. @@ -72,10 +75,8 @@ def extract_coldkey_balance(cleaned_text: str, wallet_name: str, coldkey_address each containing the corresponding balance as a Balance object. Returns a dictionary with all zeros if the wallet name or coldkey address is not found. """ - cleaned_text = cleaned_text.replace('\u200e', '') - pattern = ( - rf"{wallet_name}\s+{coldkey_address}\s+([\d,]+\.\d+)\s*τ" # Free Balance - ) + cleaned_text = cleaned_text.replace("\u200e", "") + pattern = rf"{wallet_name}\s+{coldkey_address}\s+([\d,]+\.\d+)\s*τ" # Free Balance match = re.search(pattern, cleaned_text) @@ -153,8 +154,8 @@ def validate_wallet_inspect( text: str, coldkey: str, balance: float, - delegates: List[Tuple[str, float, bool]], - hotkeys_netuid: List[Tuple[str, str, float, bool]], + delegates: list[tuple[str, float, bool]], + hotkeys_netuid: list[tuple[str, str, float, bool]], ): # TODO: Handle stake in Balance format as well """ @@ -299,3 +300,45 @@ async def call_add_proposal( ) return await response.is_success + + +async def set_storage_extrinsic( + substrate: "AsyncSubstrateInterface", + wallet: "Wallet", + items: list[tuple[bytes, bytes]], +) -> bool: + """Sets storage items using sudo permissions. + + Args: + subtensor: initialized SubtensorInterface object + wallet: bittensor wallet object with sudo permissions + items: List of (key, value) tuples where both key and value are bytes + + Returns: + bool: True if successful, False otherwise + """ + + storage_call = await substrate.compose_call( + call_module="System", call_function="set_storage", call_params={"items": items} + ) + + sudo_call = await substrate.compose_call( + call_module="Sudo", call_function="sudo", call_params={"call": storage_call} + ) + + extrinsic = await substrate.create_signed_extrinsic( + call=sudo_call, + keypair=wallet.coldkey, + ) + response = await substrate.submit_extrinsic( + extrinsic, + wait_for_inclusion=True, + wait_for_finalization=True, + ) + + if not response: + print(response) + else: + print(":white_heavy_check_mark: [dark_sea_green_3]Success[/dark_sea_green_3]") + + return response