bittensor-cli 9.23.0rc1__tar.gz → 9.23.0rc2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/PKG-INFO +1 -1
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/cli.py +112 -79
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/__init__.py +31 -10
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/bittensor/subtensor_interface.py +32 -7
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/bittensor/utils.py +2 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/stake/children_hotkeys.py +14 -4
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/subnets/subnets.py +3 -2
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/sudo.py +136 -11
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/wallets.py +3 -1
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/weights.py +30 -1
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/pyproject.toml +1 -1
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/README.md +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/__init__.py +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/doc_generation_helper.py +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/bittensor/__init__.py +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/bittensor/balances.py +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/bittensor/chain_data.py +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/bittensor/extrinsics/__init__.py +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/bittensor/extrinsics/mev_shield.py +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/bittensor/extrinsics/registration.py +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/bittensor/extrinsics/root.py +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/bittensor/extrinsics/serving.py +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/bittensor/extrinsics/transfer.py +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/bittensor/locks.py +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/bittensor/minigraph.py +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/bittensor/networking.py +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/bittensor/templates/main-filters.j2 +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/bittensor/templates/main-header.j2 +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/bittensor/templates/neuron-details.j2 +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/bittensor/templates/price-multi.j2 +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/bittensor/templates/price-single.j2 +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/bittensor/templates/subnet-details-header.j2 +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/bittensor/templates/subnet-details.j2 +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/bittensor/templates/subnet-metrics.j2 +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/bittensor/templates/subnets-table.j2 +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/bittensor/templates/table.j2 +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/bittensor/templates/view.css +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/bittensor/templates/view.j2 +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/bittensor/templates/view.js +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/__init__.py +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/axon/__init__.py +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/axon/axon.py +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/crowd/__init__.py +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/crowd/contribute.py +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/crowd/contributors.py +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/crowd/create.py +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/crowd/dissolve.py +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/crowd/refund.py +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/crowd/update.py +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/crowd/utils.py +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/crowd/view.py +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/lock/__init__.py +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/lock/add.py +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/lock/common.py +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/lock/list.py +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/lock/mode.py +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/lock/move.py +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/proxy.py +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/stake/__init__.py +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/stake/add.py +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/stake/auto_staking.py +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/stake/claim.py +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/stake/list.py +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/stake/move.py +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/stake/remove.py +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/stake/wizard.py +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/subnets/__init__.py +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/subnets/mechanisms.py +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/subnets/price.py +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/view.py +0 -0
- {bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/version.py +0 -0
|
@@ -1198,6 +1198,9 @@ class CLIManager:
|
|
|
1198
1198
|
self.sudo_app.command("trim", rich_help_panel=HELP_PANELS["SUDO"]["CONFIG"])(
|
|
1199
1199
|
self.sudo_trim
|
|
1200
1200
|
)
|
|
1201
|
+
self.sudo_app.command(
|
|
1202
|
+
"trigger-epoch", rich_help_panel=HELP_PANELS["SUDO"]["CONFIG"]
|
|
1203
|
+
)(self.sudo_trigger_epoch)
|
|
1201
1204
|
self.sudo_app.command(
|
|
1202
1205
|
"stake-burn", rich_help_panel=HELP_PANELS["SUDO"]["CONFIG"]
|
|
1203
1206
|
)(self.sudo_stake_burn)
|
|
@@ -7329,7 +7332,6 @@ class CLIManager:
|
|
|
7329
7332
|
console.print("Available hyperparameters:\n")
|
|
7330
7333
|
|
|
7331
7334
|
# Create a table to show hyperparameters with descriptions
|
|
7332
|
-
|
|
7333
7335
|
param_table = Table(
|
|
7334
7336
|
Column("[white]#", style="dim", width=4),
|
|
7335
7337
|
Column("[white]HYPERPARAMETER", style=COLORS.SU.HYPERPARAMETER),
|
|
@@ -7377,19 +7379,13 @@ class CLIManager:
|
|
|
7377
7379
|
description = metadata.get("description", "No description available.")
|
|
7378
7380
|
docs_link = metadata.get("docs_link", "")
|
|
7379
7381
|
if docs_link:
|
|
7380
|
-
# Show description text followed by clickable blue [link] at the end
|
|
7381
7382
|
console.print(
|
|
7382
7383
|
f"{description} [bright_blue underline link=https://{docs_link}]link[/]"
|
|
7383
7384
|
)
|
|
7384
7385
|
else:
|
|
7385
7386
|
console.print(f"{description}")
|
|
7386
|
-
side_effects
|
|
7387
|
-
if side_effects:
|
|
7387
|
+
if side_effects := metadata.get("side_effects", ""):
|
|
7388
7388
|
console.print(f"[dim]Side Effects:[/dim] {side_effects}")
|
|
7389
|
-
if docs_link:
|
|
7390
|
-
console.print(
|
|
7391
|
-
f"[dim]📚 Docs:[/dim] [link]https://{docs_link}[/link]\n"
|
|
7392
|
-
)
|
|
7393
7389
|
|
|
7394
7390
|
if param_name in ["alpha_high", "alpha_low"]:
|
|
7395
7391
|
if not prompt:
|
|
@@ -7398,16 +7394,13 @@ class CLIManager:
|
|
|
7398
7394
|
"They must be set together via the alpha_values parameter."
|
|
7399
7395
|
)
|
|
7400
7396
|
if json_output:
|
|
7401
|
-
|
|
7402
|
-
{
|
|
7397
|
+
json_console.print_json(
|
|
7398
|
+
data={
|
|
7403
7399
|
"success": False,
|
|
7404
7400
|
"err_msg": err_msg,
|
|
7405
7401
|
"extrinsic_identifier": None,
|
|
7406
|
-
}
|
|
7407
|
-
ensure_ascii=True,
|
|
7402
|
+
}
|
|
7408
7403
|
)
|
|
7409
|
-
sys.stdout.write(json_str + "\n")
|
|
7410
|
-
sys.stdout.flush()
|
|
7411
7404
|
else:
|
|
7412
7405
|
print_error(
|
|
7413
7406
|
f"[{COLORS.SU.HYPERPARAM}]alpha_high[/{COLORS.SU.HYPERPARAM}] and "
|
|
@@ -7419,23 +7412,20 @@ class CLIManager:
|
|
|
7419
7412
|
low_val = FloatPrompt.ask(f"Enter the new value for {arg__('alpha_low')}")
|
|
7420
7413
|
high_val = FloatPrompt.ask(f"Enter the new value for {arg__('alpha_high')}")
|
|
7421
7414
|
param_value = f"{low_val},{high_val}"
|
|
7422
|
-
|
|
7415
|
+
elif param_name == "yuma_version":
|
|
7423
7416
|
if not prompt:
|
|
7424
7417
|
err_msg = (
|
|
7425
7418
|
"yuma_version is set using a different hyperparameter (yuma3_enabled), "
|
|
7426
7419
|
"and thus cannot be set with `--no-prompt`"
|
|
7427
7420
|
)
|
|
7428
7421
|
if json_output:
|
|
7429
|
-
|
|
7430
|
-
{
|
|
7422
|
+
json_console.print_json(
|
|
7423
|
+
data={
|
|
7431
7424
|
"success": False,
|
|
7432
7425
|
"err_msg": err_msg,
|
|
7433
7426
|
"extrinsic_identifier": None,
|
|
7434
|
-
}
|
|
7435
|
-
ensure_ascii=True,
|
|
7427
|
+
}
|
|
7436
7428
|
)
|
|
7437
|
-
sys.stdout.write(json_str + "\n")
|
|
7438
|
-
sys.stdout.flush()
|
|
7439
7429
|
else:
|
|
7440
7430
|
print_error(
|
|
7441
7431
|
f"[{COLORS.SU.HYPERPARAM}]yuma_version[/{COLORS.SU.HYPERPARAM}]"
|
|
@@ -7456,22 +7446,45 @@ class CLIManager:
|
|
|
7456
7446
|
param_value = "true" if question == "enable" else "false"
|
|
7457
7447
|
else:
|
|
7458
7448
|
return False
|
|
7449
|
+
elif param_name == "activity_cutoff":
|
|
7450
|
+
err_msg = (
|
|
7451
|
+
"activity_cutoff is now derived from activity_cutoff_factor "
|
|
7452
|
+
"(cutoff blocks = factor × tempo ÷ 1000) and can no longer be set "
|
|
7453
|
+
"directly. Set activity_cutoff_factor instead (per-mille units; "
|
|
7454
|
+
"1000 = one full tempo)."
|
|
7455
|
+
)
|
|
7456
|
+
if json_output:
|
|
7457
|
+
json_console.print_json(
|
|
7458
|
+
data={
|
|
7459
|
+
"success": False,
|
|
7460
|
+
"err_msg": err_msg,
|
|
7461
|
+
"extrinsic_identifier": None,
|
|
7462
|
+
}
|
|
7463
|
+
)
|
|
7464
|
+
else:
|
|
7465
|
+
print_error(
|
|
7466
|
+
f"[{COLORS.SU.HYPERPARAM}]activity_cutoff[/{COLORS.SU.HYPERPARAM}] "
|
|
7467
|
+
f"is now derived from "
|
|
7468
|
+
f"[{COLORS.SU.HYPERPARAM}]activity_cutoff_factor[/{COLORS.SU.HYPERPARAM}] "
|
|
7469
|
+
f"(cutoff blocks = factor × tempo ÷ 1000). Set "
|
|
7470
|
+
f"[{COLORS.SU.HYPERPARAM}]activity_cutoff_factor[/{COLORS.SU.HYPERPARAM}] "
|
|
7471
|
+
f"instead (per-mille units; 1000 = one full tempo)."
|
|
7472
|
+
)
|
|
7473
|
+
return False
|
|
7474
|
+
|
|
7459
7475
|
if param_name == "subnet_is_active":
|
|
7460
7476
|
err_msg = (
|
|
7461
7477
|
"subnet_is_active is set by using the 'btcli subnets start' command, "
|
|
7462
7478
|
"not via sudo set"
|
|
7463
7479
|
)
|
|
7464
7480
|
if json_output:
|
|
7465
|
-
|
|
7466
|
-
{
|
|
7481
|
+
json_console.print_json(
|
|
7482
|
+
data={
|
|
7467
7483
|
"success": False,
|
|
7468
7484
|
"err_msg": err_msg,
|
|
7469
7485
|
"extrinsic_identifier": None,
|
|
7470
|
-
}
|
|
7471
|
-
ensure_ascii=True,
|
|
7486
|
+
}
|
|
7472
7487
|
)
|
|
7473
|
-
sys.stdout.write(json_str + "\n")
|
|
7474
|
-
sys.stdout.flush()
|
|
7475
7488
|
else:
|
|
7476
7489
|
print_error(
|
|
7477
7490
|
f"[{COLORS.SU.HYPERPARAM}]subnet_is_active[/{COLORS.SU.HYPERPARAM}] "
|
|
@@ -7506,60 +7519,29 @@ class CLIManager:
|
|
|
7506
7519
|
f"param_name: {param_name}\n"
|
|
7507
7520
|
f"param_value: {param_value}"
|
|
7508
7521
|
)
|
|
7522
|
+
|
|
7523
|
+
result, err_msg, ext_id = self._run_command(
|
|
7524
|
+
sudo.sudo_set_hyperparameter(
|
|
7525
|
+
wallet=wallet,
|
|
7526
|
+
subtensor=self.initialize_chain(network),
|
|
7527
|
+
netuid=netuid,
|
|
7528
|
+
proxy=proxy,
|
|
7529
|
+
param_name=param_name,
|
|
7530
|
+
param_value=param_value,
|
|
7531
|
+
normalize=normalize_value,
|
|
7532
|
+
prompt=prompt,
|
|
7533
|
+
json_output=json_output,
|
|
7534
|
+
)
|
|
7535
|
+
)
|
|
7509
7536
|
if json_output:
|
|
7510
|
-
|
|
7511
|
-
|
|
7512
|
-
|
|
7513
|
-
|
|
7514
|
-
|
|
7515
|
-
|
|
7516
|
-
proxy=proxy,
|
|
7517
|
-
param_name=param_name,
|
|
7518
|
-
param_value=param_value,
|
|
7519
|
-
normalize=normalize_value,
|
|
7520
|
-
prompt=prompt,
|
|
7521
|
-
json_output=json_output,
|
|
7522
|
-
)
|
|
7523
|
-
)
|
|
7524
|
-
json_str = json.dumps(
|
|
7525
|
-
{
|
|
7526
|
-
"success": result,
|
|
7527
|
-
"err_msg": err_msg,
|
|
7528
|
-
"extrinsic_identifier": ext_id,
|
|
7529
|
-
},
|
|
7530
|
-
ensure_ascii=True,
|
|
7531
|
-
)
|
|
7532
|
-
sys.stdout.write(json_str + "\n")
|
|
7533
|
-
sys.stdout.flush()
|
|
7534
|
-
return result
|
|
7535
|
-
except Exception as e:
|
|
7536
|
-
# Ensure JSON output even on exceptions
|
|
7537
|
-
json_str = json.dumps(
|
|
7538
|
-
{
|
|
7539
|
-
"success": False,
|
|
7540
|
-
"err_msg": str(e),
|
|
7541
|
-
"extrinsic_identifier": None,
|
|
7542
|
-
},
|
|
7543
|
-
ensure_ascii=True,
|
|
7544
|
-
)
|
|
7545
|
-
sys.stdout.write(json_str + "\n")
|
|
7546
|
-
sys.stdout.flush()
|
|
7547
|
-
raise
|
|
7548
|
-
else:
|
|
7549
|
-
result, err_msg, ext_id = self._run_command(
|
|
7550
|
-
sudo.sudo_set_hyperparameter(
|
|
7551
|
-
wallet=wallet,
|
|
7552
|
-
subtensor=self.initialize_chain(network),
|
|
7553
|
-
netuid=netuid,
|
|
7554
|
-
proxy=proxy,
|
|
7555
|
-
param_name=param_name,
|
|
7556
|
-
param_value=param_value,
|
|
7557
|
-
normalize=normalize_value,
|
|
7558
|
-
prompt=prompt,
|
|
7559
|
-
json_output=json_output,
|
|
7560
|
-
)
|
|
7537
|
+
json_console.print_json(
|
|
7538
|
+
data={
|
|
7539
|
+
"success": result,
|
|
7540
|
+
"err_msg": err_msg,
|
|
7541
|
+
"extrinsic_identifier": ext_id,
|
|
7542
|
+
}
|
|
7561
7543
|
)
|
|
7562
|
-
|
|
7544
|
+
return result
|
|
7563
7545
|
|
|
7564
7546
|
def sudo_get(
|
|
7565
7547
|
self,
|
|
@@ -7838,6 +7820,57 @@ class CLIManager:
|
|
|
7838
7820
|
)
|
|
7839
7821
|
)
|
|
7840
7822
|
|
|
7823
|
+
def sudo_trigger_epoch(
|
|
7824
|
+
self,
|
|
7825
|
+
network: Optional[list[str]] = Options.network,
|
|
7826
|
+
wallet_name: Optional[str] = Options.wallet_name,
|
|
7827
|
+
wallet_path: Optional[str] = Options.wallet_path,
|
|
7828
|
+
wallet_hotkey: Optional[str] = Options.wallet_hotkey,
|
|
7829
|
+
netuid: int = Options.netuid,
|
|
7830
|
+
proxy: Optional[str] = Options.proxy,
|
|
7831
|
+
quiet: bool = Options.quiet,
|
|
7832
|
+
verbose: bool = Options.verbose,
|
|
7833
|
+
json_output: bool = Options.json_output,
|
|
7834
|
+
prompt: bool = Options.prompt,
|
|
7835
|
+
decline: bool = Options.decline,
|
|
7836
|
+
period: int = Options.period,
|
|
7837
|
+
):
|
|
7838
|
+
"""
|
|
7839
|
+
Manually triggers an epoch for a subnet you own.
|
|
7840
|
+
|
|
7841
|
+
The epoch fires after the chain's admin freeze window has elapsed, during which
|
|
7842
|
+
admin operations on the subnet are locked. This is rate-limited on-chain and
|
|
7843
|
+
fails if a trigger is already pending, the next automatic epoch is imminent, or
|
|
7844
|
+
commit-reveal is enabled on the subnet (disable it first via
|
|
7845
|
+
'btcli sudo set --param commit_reveal_weights_enabled --value false').
|
|
7846
|
+
|
|
7847
|
+
EXAMPLE
|
|
7848
|
+
[green]$[/green] btcli sudo trigger-epoch --netuid 95 --wallet-name my_wallet --wallet-hotkey my_hotkey
|
|
7849
|
+
"""
|
|
7850
|
+
self.verbosity_handler(quiet, verbose, json_output, prompt)
|
|
7851
|
+
proxy = self.is_valid_proxy_name_or_ss58(proxy, False)
|
|
7852
|
+
|
|
7853
|
+
wallet = self.wallet_ask(
|
|
7854
|
+
wallet_name,
|
|
7855
|
+
wallet_path,
|
|
7856
|
+
wallet_hotkey,
|
|
7857
|
+
ask_for=[WO.NAME, WO.PATH],
|
|
7858
|
+
validate=WV.WALLET,
|
|
7859
|
+
)
|
|
7860
|
+
self._run_command(
|
|
7861
|
+
sudo.trigger_epoch(
|
|
7862
|
+
subtensor=self.initialize_chain(network),
|
|
7863
|
+
wallet=wallet,
|
|
7864
|
+
netuid=netuid,
|
|
7865
|
+
period=period,
|
|
7866
|
+
proxy=proxy,
|
|
7867
|
+
json_output=json_output,
|
|
7868
|
+
prompt=prompt,
|
|
7869
|
+
decline=decline,
|
|
7870
|
+
quiet=quiet,
|
|
7871
|
+
)
|
|
7872
|
+
)
|
|
7873
|
+
|
|
7841
7874
|
def sudo_stake_burn(
|
|
7842
7875
|
self,
|
|
7843
7876
|
network: Optional[list[str]] = Options.network,
|
|
@@ -584,10 +584,10 @@ HYPERPARAMS = {
|
|
|
584
584
|
"kappa": ("sudo_set_kappa", RootSudoOnly.TRUE),
|
|
585
585
|
"immunity_period": ("sudo_set_immunity_period", RootSudoOnly.FALSE),
|
|
586
586
|
"min_allowed_weights": ("sudo_set_min_allowed_weights", RootSudoOnly.FALSE),
|
|
587
|
-
"tempo": ("
|
|
587
|
+
"tempo": ("set_tempo", RootSudoOnly.COMPLICATED),
|
|
588
588
|
"weights_version": ("sudo_set_weights_version_key", RootSudoOnly.FALSE),
|
|
589
589
|
"weights_rate_limit": ("sudo_set_weights_set_rate_limit", RootSudoOnly.TRUE),
|
|
590
|
-
"
|
|
590
|
+
"activity_cutoff_factor": ("set_activity_cutoff_factor", RootSudoOnly.COMPLICATED),
|
|
591
591
|
"target_regs_per_interval": (
|
|
592
592
|
"sudo_set_target_registrations_per_interval",
|
|
593
593
|
RootSudoOnly.TRUE,
|
|
@@ -635,6 +635,10 @@ HYPERPARAMS = {
|
|
|
635
635
|
"alpha_low": ("", RootSudoOnly.FALSE), # Derived from alpha_values
|
|
636
636
|
#
|
|
637
637
|
"subnet_is_active": ("", RootSudoOnly.FALSE), # Set via btcli subnets start
|
|
638
|
+
"activity_cutoff": (
|
|
639
|
+
"",
|
|
640
|
+
RootSudoOnly.FALSE,
|
|
641
|
+
), # Derived: activity_cutoff_factor × tempo / 1000
|
|
638
642
|
"yuma_version": ("", RootSudoOnly.FALSE), # Related to yuma3_enabled
|
|
639
643
|
"max_allowed_uids": ("sudo_set_max_allowed_uids", RootSudoOnly.FALSE),
|
|
640
644
|
"burn_increase_mult": ("sudo_set_burn_increase_mult", RootSudoOnly.FALSE),
|
|
@@ -645,9 +649,20 @@ HYPERPARAMS = {
|
|
|
645
649
|
),
|
|
646
650
|
}
|
|
647
651
|
|
|
648
|
-
# Maps a hyperparameter to a non-default pallet for sudo set calls.
|
|
649
|
-
#
|
|
650
|
-
HYPERPARAMS_MODULE: dict[str, str] = {
|
|
652
|
+
# Maps a hyperparameter to a non-default pallet for sudo set calls. Hyperparameters
|
|
653
|
+
# not listed here live in the default pallet (AdminUtils).
|
|
654
|
+
HYPERPARAMS_MODULE: dict[str, str] = {
|
|
655
|
+
"tempo": "SubtensorModule",
|
|
656
|
+
"activity_cutoff_factor": "SubtensorModule",
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
# Hyperparameters whose root-sudo path uses a different extrinsic than the owner path.
|
|
660
|
+
# Maps btcli name -> (pallet, extrinsic) for the call wrapped in Sudo. Owner-side
|
|
661
|
+
# set_tempo is bounded to [360, 50400] and rate-limited; root's sudo_set_tempo accepts
|
|
662
|
+
# any u16 value.
|
|
663
|
+
HYPERPARAMS_ROOT_EXTRINSIC: dict[str, tuple[str, str]] = {
|
|
664
|
+
"tempo": ("AdminUtils", "sudo_set_tempo"),
|
|
665
|
+
}
|
|
651
666
|
|
|
652
667
|
# Hyperparameter metadata: descriptions, side-effects, ownership, and documentation links
|
|
653
668
|
HYPERPARAMS_METADATA = {
|
|
@@ -670,9 +685,9 @@ HYPERPARAMS_METADATA = {
|
|
|
670
685
|
"docs_link": "docs.learnbittensor.org/subnets/subnet-hyperparameters#minallowedweights",
|
|
671
686
|
},
|
|
672
687
|
"tempo": {
|
|
673
|
-
"description": "Number of blocks between epoch transitions",
|
|
674
|
-
"side_effects": "Lower tempo means more frequent updates but higher chain load. Higher tempo reduces frequency but may slow responsiveness.",
|
|
675
|
-
"owner_settable":
|
|
688
|
+
"description": "Number of blocks between automatic epoch transitions. Owner-settable between 360 and 50400 blocks (rate-limited to one change per 360 blocks); root can set any value via sudo.",
|
|
689
|
+
"side_effects": "Lower tempo means more frequent updates but higher chain load. Higher tempo reduces frequency but may slow responsiveness. Changing tempo resets the epoch cycle, so the next epoch fires a full tempo after the change.",
|
|
690
|
+
"owner_settable": True,
|
|
676
691
|
"docs_link": "docs.learnbittensor.org/subnets/subnet-hyperparameters#tempo",
|
|
677
692
|
},
|
|
678
693
|
"weights_version": {
|
|
@@ -688,8 +703,14 @@ HYPERPARAMS_METADATA = {
|
|
|
688
703
|
"docs_link": "docs.learnbittensor.org/subnets/subnet-hyperparameters#weightsratelimit--commitmentratelimit",
|
|
689
704
|
},
|
|
690
705
|
"activity_cutoff": {
|
|
691
|
-
"description": "
|
|
692
|
-
"side_effects": "Lower values
|
|
706
|
+
"description": "Effective validator inactivity cutoff in blocks, computed as activity_cutoff_factor × tempo ÷ 1000. Read-only; set activity_cutoff_factor instead.",
|
|
707
|
+
"side_effects": "Lower values prune inactive validators more aggressively; higher values keep more validators active.",
|
|
708
|
+
"owner_settable": False,
|
|
709
|
+
"docs_link": "docs.learnbittensor.org/subnets/subnet-hyperparameters#activitycutoff",
|
|
710
|
+
},
|
|
711
|
+
"activity_cutoff_factor": {
|
|
712
|
+
"description": "Tolerated validator inactivity as per-mille of tempo (1000 = one full tempo). Effective cutoff in blocks = factor × tempo ÷ 1000. Allowed range: 1000 to 50000.",
|
|
713
|
+
"side_effects": "Lower factors prune inactive validators more aggressively; higher factors tolerate longer inactivity. The effective cutoff scales with tempo.",
|
|
693
714
|
"owner_settable": True,
|
|
694
715
|
"docs_link": "docs.learnbittensor.org/subnets/subnet-hyperparameters#activitycutoff",
|
|
695
716
|
},
|
|
@@ -113,12 +113,14 @@ class SubtensorInterface:
|
|
|
113
113
|
if (use_disk_cache or os.getenv("DISK_CACHE", "1") == "1")
|
|
114
114
|
else AsyncSubstrateInterface
|
|
115
115
|
)
|
|
116
|
-
self.substrate =
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
116
|
+
self.substrate: AsyncSubstrateInterface | DiskCachedAsyncSubstrateInterface = (
|
|
117
|
+
substrate_class(
|
|
118
|
+
url=self.chain_endpoint,
|
|
119
|
+
ss58_format=SS58_FORMAT,
|
|
120
|
+
type_registry=TYPE_REGISTRY,
|
|
121
|
+
chain_name="Bittensor",
|
|
122
|
+
ws_shutdown_timer=None,
|
|
123
|
+
)
|
|
122
124
|
)
|
|
123
125
|
|
|
124
126
|
def __str__(self):
|
|
@@ -1653,6 +1655,29 @@ class SubtensorInterface:
|
|
|
1653
1655
|
|
|
1654
1656
|
return SubnetHyperparameters.from_any(result)
|
|
1655
1657
|
|
|
1658
|
+
async def get_next_epoch_start_block(
|
|
1659
|
+
self, netuid: int, block_hash: Optional[str] = None
|
|
1660
|
+
) -> Optional[int]:
|
|
1661
|
+
"""
|
|
1662
|
+
Returns the block number at which the subnet's next epoch is expected to fire,
|
|
1663
|
+
considering both the automatic schedule and any pending owner-triggered epoch.
|
|
1664
|
+
It does not account for per-block epoch-cap deferrals, which can push the
|
|
1665
|
+
actual firing block slightly later.
|
|
1666
|
+
|
|
1667
|
+
Returns `None` if the subnet does not run epochs (tempo == 0) or if the chain
|
|
1668
|
+
does not expose the runtime API (runtimes without dynamic tempo).
|
|
1669
|
+
"""
|
|
1670
|
+
try:
|
|
1671
|
+
result = await self.query_runtime_api(
|
|
1672
|
+
runtime_api="SubnetInfoRuntimeApi",
|
|
1673
|
+
method="get_next_epoch_start_block",
|
|
1674
|
+
params=[netuid],
|
|
1675
|
+
block_hash=block_hash,
|
|
1676
|
+
)
|
|
1677
|
+
except (ValueError, SubstrateRequestException):
|
|
1678
|
+
return None
|
|
1679
|
+
return None if result is None else int(result)
|
|
1680
|
+
|
|
1656
1681
|
async def get_subnet_mechanisms(
|
|
1657
1682
|
self, netuid: int, block_hash: Optional[str] = None
|
|
1658
1683
|
) -> int:
|
|
@@ -2680,7 +2705,7 @@ class SubtensorInterface:
|
|
|
2680
2705
|
for idx, (_, result) in enumerate(batch_claimable):
|
|
2681
2706
|
hotkey = unique_hotkeys[idx]
|
|
2682
2707
|
if result:
|
|
2683
|
-
for netuid, rate in result:
|
|
2708
|
+
for netuid, rate in result.items():
|
|
2684
2709
|
if hotkey not in claimable_rates:
|
|
2685
2710
|
claimable_rates[hotkey] = {}
|
|
2686
2711
|
claimable_rates[hotkey][netuid] = fixed_to_float(rate, frac_bits=32)
|
|
@@ -855,6 +855,8 @@ def normalize_hyperparameters(
|
|
|
855
855
|
"min_childkey_take": u16_normalized_float,
|
|
856
856
|
"min_burn": Balance.from_rao,
|
|
857
857
|
"max_burn": Balance.from_rao,
|
|
858
|
+
# Per-mille of tempo; normalized to the factor itself (1.0 = one full tempo).
|
|
859
|
+
"activity_cutoff_factor": lambda value: value / 1000,
|
|
858
860
|
}
|
|
859
861
|
|
|
860
862
|
normalized_values: list[tuple[str, str, str]] = []
|
|
@@ -42,15 +42,25 @@ async def get_childkey_completion_block(
|
|
|
42
42
|
tempo_query = subtensor.get_hyperparameter(
|
|
43
43
|
param_name="Tempo", netuid=netuid, block_hash=bh
|
|
44
44
|
)
|
|
45
|
-
block_number, blocks_since_last_step, tempo = await asyncio.gather(
|
|
45
|
+
block_number, blocks_since_last_step, tempo, next_epoch = await asyncio.gather(
|
|
46
46
|
subtensor.substrate.get_block_number(block_hash=bh),
|
|
47
47
|
blocks_since_last_step_query,
|
|
48
48
|
tempo_query,
|
|
49
|
+
subtensor.get_next_epoch_start_block(netuid, block_hash=bh),
|
|
49
50
|
)
|
|
50
51
|
cooldown = block_number + 7200
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
if next_epoch is not None and tempo:
|
|
53
|
+
# Dynamic-tempo scheduler: epochs fire at next_epoch, then every tempo blocks.
|
|
54
|
+
if next_epoch >= cooldown:
|
|
55
|
+
next_epoch_after_cooldown = next_epoch
|
|
56
|
+
else:
|
|
57
|
+
epochs_until_cooldown = (cooldown - next_epoch + tempo - 1) // tempo
|
|
58
|
+
next_epoch_after_cooldown = next_epoch + epochs_until_cooldown * tempo
|
|
59
|
+
else:
|
|
60
|
+
# Legacy modulo scheduler (chains without the dynamic-tempo runtime).
|
|
61
|
+
blocks_left_in_tempo = tempo - blocks_since_last_step
|
|
62
|
+
next_tempo = block_number + blocks_left_in_tempo
|
|
63
|
+
next_epoch_after_cooldown = (cooldown - next_tempo) % (tempo + 1) + cooldown
|
|
54
64
|
return block_number, next_epoch_after_cooldown
|
|
55
65
|
|
|
56
66
|
|
{bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/subnets/subnets.py
RENAMED
|
@@ -932,9 +932,10 @@ async def subnets_list(
|
|
|
932
932
|
subnet.blocks_since_last_step - prev_blocks_since_last_step
|
|
933
933
|
)
|
|
934
934
|
else:
|
|
935
|
-
# Tempo restarted
|
|
935
|
+
# Tempo restarted; the epoch period is exactly tempo blocks
|
|
936
|
+
# under the dynamic-tempo scheduler.
|
|
936
937
|
block_change = (
|
|
937
|
-
subnet.blocks_since_last_step + subnet.tempo
|
|
938
|
+
subnet.blocks_since_last_step + subnet.tempo
|
|
938
939
|
) - prev_blocks_since_last_step
|
|
939
940
|
if block_change > 0:
|
|
940
941
|
block_change_text = f" [pale_green3](+{block_change})[/pale_green3]"
|
|
@@ -14,6 +14,7 @@ from bittensor_cli.src import (
|
|
|
14
14
|
HYPERPARAMS,
|
|
15
15
|
HYPERPARAMS_MODULE,
|
|
16
16
|
HYPERPARAMS_METADATA,
|
|
17
|
+
HYPERPARAMS_ROOT_EXTRINSIC,
|
|
17
18
|
RootSudoOnly,
|
|
18
19
|
COLOR_PALETTE,
|
|
19
20
|
)
|
|
@@ -67,6 +68,15 @@ def allowed_value(
|
|
|
67
68
|
"""
|
|
68
69
|
try:
|
|
69
70
|
if not isinstance(value, bool):
|
|
71
|
+
if param == "activity_cutoff_factor":
|
|
72
|
+
# Bounds mirror MIN/MAX_ACTIVITY_CUTOFF_FACTOR_MILLI in subtensor.
|
|
73
|
+
factor = int(value)
|
|
74
|
+
if not 1_000 <= factor <= 50_000:
|
|
75
|
+
return (
|
|
76
|
+
False,
|
|
77
|
+
"between 1000 and 50000 (per-mille units; 1000 = one full tempo)",
|
|
78
|
+
)
|
|
79
|
+
return True, factor
|
|
70
80
|
if param == "alpha_values":
|
|
71
81
|
# Split the string into individual values
|
|
72
82
|
alpha_low_str, alpha_high_str = value.split(",")
|
|
@@ -380,6 +390,7 @@ def search_metadata(
|
|
|
380
390
|
"immunity_period": int,
|
|
381
391
|
"commit_reveal_period": int,
|
|
382
392
|
"adjustment_interval": int,
|
|
393
|
+
"factor_milli": int,
|
|
383
394
|
"max_validators": int,
|
|
384
395
|
"min_allowed_weights": int,
|
|
385
396
|
"rho": int,
|
|
@@ -395,6 +406,7 @@ def search_metadata(
|
|
|
395
406
|
"bool": string_to_bool,
|
|
396
407
|
"u16": string_to_u16,
|
|
397
408
|
"i16": string_to_i16,
|
|
409
|
+
"u32": int,
|
|
398
410
|
"u64": string_to_u64,
|
|
399
411
|
"MechId": int,
|
|
400
412
|
"U64F64": string_to_u64f64,
|
|
@@ -404,6 +416,7 @@ def search_metadata(
|
|
|
404
416
|
"bool": "bool",
|
|
405
417
|
"u16": "float",
|
|
406
418
|
"i16": "float (signed)",
|
|
419
|
+
"u32": "int",
|
|
407
420
|
"u64": "float",
|
|
408
421
|
"U64F64": "decimal",
|
|
409
422
|
"TaoBalance": "Tao (float)",
|
|
@@ -598,13 +611,17 @@ async def set_hyperparameter_extrinsic(
|
|
|
598
611
|
arbitrary_extrinsic = False
|
|
599
612
|
|
|
600
613
|
extrinsic, sudo_ = HYPERPARAMS.get(parameter, ("", RootSudoOnly.FALSE))
|
|
614
|
+
# Resolve pallet and root-path override before `parameter` is potentially
|
|
615
|
+
# reassigned to the extrinsic name in normalize mode.
|
|
616
|
+
pallet = HYPERPARAMS_MODULE.get(parameter) or DEFAULT_PALLET
|
|
617
|
+
root_override = HYPERPARAMS_ROOT_EXTRINSIC.get(parameter)
|
|
601
618
|
call_params = {"netuid": netuid}
|
|
602
619
|
if normalize and parameter != "alpha_values":
|
|
603
620
|
parameter = extrinsic
|
|
604
621
|
extrinsic = None
|
|
605
622
|
if not extrinsic:
|
|
606
623
|
arbitrary_extrinsic, call_params = search_metadata(
|
|
607
|
-
parameter, value, netuid, subtensor.substrate.metadata
|
|
624
|
+
parameter, value, netuid, subtensor.substrate.metadata, pallet_name=pallet
|
|
608
625
|
)
|
|
609
626
|
extrinsic = parameter
|
|
610
627
|
if not arbitrary_extrinsic:
|
|
@@ -624,7 +641,6 @@ async def set_hyperparameter_extrinsic(
|
|
|
624
641
|
|
|
625
642
|
substrate = subtensor.substrate
|
|
626
643
|
msg_value = value if not arbitrary_extrinsic else call_params
|
|
627
|
-
pallet = HYPERPARAMS_MODULE.get(parameter) or DEFAULT_PALLET
|
|
628
644
|
|
|
629
645
|
if not arbitrary_extrinsic:
|
|
630
646
|
extrinsic_params = await substrate.get_metadata_call_function(
|
|
@@ -665,13 +681,28 @@ async def set_hyperparameter_extrinsic(
|
|
|
665
681
|
call_params=call_params,
|
|
666
682
|
block_hash=block_hash,
|
|
667
683
|
)
|
|
668
|
-
|
|
669
|
-
|
|
684
|
+
|
|
685
|
+
async def sudo_wrapped() -> GenericCall:
|
|
686
|
+
# Root-sudo path: some hyperparams use a dedicated root extrinsic (e.g.
|
|
687
|
+
# AdminUtils.sudo_set_tempo) instead of Sudo-wrapping the owner call.
|
|
688
|
+
inner_call = call_
|
|
689
|
+
if root_override:
|
|
690
|
+
root_pallet, root_extrinsic = root_override
|
|
691
|
+
inner_call = await substrate.compose_call(
|
|
692
|
+
call_module=root_pallet,
|
|
693
|
+
call_function=root_extrinsic,
|
|
694
|
+
call_params=call_params,
|
|
695
|
+
block_hash=block_hash,
|
|
696
|
+
)
|
|
697
|
+
return await substrate.compose_call(
|
|
670
698
|
call_module="Sudo",
|
|
671
699
|
call_function="sudo",
|
|
672
|
-
call_params={"call":
|
|
700
|
+
call_params={"call": inner_call},
|
|
673
701
|
block_hash=block_hash,
|
|
674
702
|
)
|
|
703
|
+
|
|
704
|
+
if sudo_ is RootSudoOnly.TRUE:
|
|
705
|
+
call = await sudo_wrapped()
|
|
675
706
|
elif sudo_ is RootSudoOnly.COMPLICATED:
|
|
676
707
|
if not prompt:
|
|
677
708
|
# In no-prompt mode, owners should take the owner path; non-owners
|
|
@@ -684,12 +715,7 @@ async def set_hyperparameter_extrinsic(
|
|
|
684
715
|
quiet=quiet,
|
|
685
716
|
)
|
|
686
717
|
if to_sudo_or_not_to_sudo:
|
|
687
|
-
call = await
|
|
688
|
-
call_module="Sudo",
|
|
689
|
-
call_function="sudo",
|
|
690
|
-
call_params={"call": call_},
|
|
691
|
-
block_hash=block_hash,
|
|
692
|
-
)
|
|
718
|
+
call = await sudo_wrapped()
|
|
693
719
|
else:
|
|
694
720
|
if subnet_owner != coldkey_ss58:
|
|
695
721
|
err_msg = "This wallet doesn't own the specified subnet."
|
|
@@ -1600,3 +1626,102 @@ async def trim(
|
|
|
1600
1626
|
await print_extrinsic_id(ext_receipt)
|
|
1601
1627
|
print_success(f"[dark_sea_green3]{msg}[/dark_sea_green3]")
|
|
1602
1628
|
return True
|
|
1629
|
+
|
|
1630
|
+
|
|
1631
|
+
async def trigger_epoch(
|
|
1632
|
+
wallet: Wallet,
|
|
1633
|
+
subtensor: "SubtensorInterface",
|
|
1634
|
+
netuid: int,
|
|
1635
|
+
proxy: Optional[str],
|
|
1636
|
+
period: int,
|
|
1637
|
+
prompt: bool,
|
|
1638
|
+
decline: bool,
|
|
1639
|
+
quiet: bool,
|
|
1640
|
+
json_output: bool,
|
|
1641
|
+
) -> bool:
|
|
1642
|
+
"""
|
|
1643
|
+
Manually triggers an epoch for a subnet owned by the wallet.
|
|
1644
|
+
|
|
1645
|
+
The epoch is scheduled to fire after the chain's admin freeze window, during
|
|
1646
|
+
which admin operations on the subnet are locked. Rate-limited on-chain, and
|
|
1647
|
+
rejected if a trigger is already pending, the next automatic epoch is
|
|
1648
|
+
imminent, or commit-reveal is enabled on the subnet.
|
|
1649
|
+
"""
|
|
1650
|
+
coldkey_ss58 = proxy or wallet.coldkeypub.ss58_address
|
|
1651
|
+
print_verbose("Confirming subnet owner")
|
|
1652
|
+
block_hash = await subtensor.substrate.get_chain_head()
|
|
1653
|
+
subnet_owner = await subtensor.query(
|
|
1654
|
+
module="SubtensorModule",
|
|
1655
|
+
storage_function="SubnetOwner",
|
|
1656
|
+
params=[netuid],
|
|
1657
|
+
block_hash=block_hash,
|
|
1658
|
+
)
|
|
1659
|
+
if subnet_owner != coldkey_ss58:
|
|
1660
|
+
err_msg = "This wallet doesn't own the specified subnet."
|
|
1661
|
+
if json_output:
|
|
1662
|
+
json_console.print_json(data={"success": False, "message": err_msg})
|
|
1663
|
+
else:
|
|
1664
|
+
print_error(err_msg)
|
|
1665
|
+
return False
|
|
1666
|
+
if prompt and not json_output:
|
|
1667
|
+
if not confirm_action(
|
|
1668
|
+
f"You are about to manually trigger an epoch on SN{netuid}. "
|
|
1669
|
+
f"This locks admin operations on the subnet until the epoch fires.",
|
|
1670
|
+
default=False,
|
|
1671
|
+
decline=decline,
|
|
1672
|
+
quiet=quiet,
|
|
1673
|
+
):
|
|
1674
|
+
print_error("User aborted.")
|
|
1675
|
+
return False
|
|
1676
|
+
call = await subtensor.substrate.compose_call(
|
|
1677
|
+
call_module="SubtensorModule",
|
|
1678
|
+
call_function="trigger_epoch",
|
|
1679
|
+
call_params={"netuid": netuid},
|
|
1680
|
+
block_hash=block_hash,
|
|
1681
|
+
)
|
|
1682
|
+
with console.status(
|
|
1683
|
+
f":satellite: Triggering epoch on subnet "
|
|
1684
|
+
f"[{COLOR_PALETTE.G.SUBHEAD}]{netuid}[/{COLOR_PALETTE.G.SUBHEAD}] ...",
|
|
1685
|
+
spinner="earth",
|
|
1686
|
+
):
|
|
1687
|
+
success, err_msg, ext_receipt = await subtensor.sign_and_send_extrinsic(
|
|
1688
|
+
call=call, wallet=wallet, era={"period": period}, proxy=proxy
|
|
1689
|
+
)
|
|
1690
|
+
if not success:
|
|
1691
|
+
if json_output:
|
|
1692
|
+
json_console.print_json(
|
|
1693
|
+
data={
|
|
1694
|
+
"success": False,
|
|
1695
|
+
"message": err_msg,
|
|
1696
|
+
"extrinsic_identifier": None,
|
|
1697
|
+
}
|
|
1698
|
+
)
|
|
1699
|
+
else:
|
|
1700
|
+
print_error(err_msg)
|
|
1701
|
+
return False
|
|
1702
|
+
else:
|
|
1703
|
+
ext_id = await ext_receipt.get_extrinsic_identifier()
|
|
1704
|
+
fires_at = None
|
|
1705
|
+
try:
|
|
1706
|
+
for event in await ext_receipt.triggered_events:
|
|
1707
|
+
if event["event_id"] == "EpochTriggered":
|
|
1708
|
+
fires_at = event["attributes"]["fires_at"]
|
|
1709
|
+
except KeyError:
|
|
1710
|
+
# The trigger still succeeded; the fires_at block is just a nice-to-have.
|
|
1711
|
+
pass
|
|
1712
|
+
msg = f"Epoch triggered on SN{netuid}"
|
|
1713
|
+
if fires_at is not None:
|
|
1714
|
+
msg += f"; it will fire at block {fires_at} at the earliest"
|
|
1715
|
+
if json_output:
|
|
1716
|
+
json_console.print_json(
|
|
1717
|
+
data={
|
|
1718
|
+
"success": True,
|
|
1719
|
+
"message": msg,
|
|
1720
|
+
"fires_at": fires_at,
|
|
1721
|
+
"extrinsic_identifier": ext_id,
|
|
1722
|
+
}
|
|
1723
|
+
)
|
|
1724
|
+
else:
|
|
1725
|
+
await print_extrinsic_id(ext_receipt)
|
|
1726
|
+
print_success(f"[dark_sea_green3]{msg}[/dark_sea_green3]")
|
|
1727
|
+
return True
|
|
@@ -1239,7 +1239,9 @@ async def overview(
|
|
|
1239
1239
|
validator_trust = nn.validator_trust
|
|
1240
1240
|
incentive = nn.incentive
|
|
1241
1241
|
dividends = nn.dividends
|
|
1242
|
-
|
|
1242
|
+
# Per block: the epoch period is exactly tempo blocks under the
|
|
1243
|
+
# dynamic-tempo scheduler (guard against tempo == 0).
|
|
1244
|
+
emission = int(nn.emission / max(subnet_tempo, 1) * 1e9)
|
|
1243
1245
|
last_update = int(block - nn.last_update)
|
|
1244
1246
|
validator_permit = nn.validator_permit
|
|
1245
1247
|
row = [
|
|
@@ -152,10 +152,38 @@ class SetWeightsExtrinsic:
|
|
|
152
152
|
|
|
153
153
|
return success, message, ext_id
|
|
154
154
|
|
|
155
|
+
async def _seconds_until_reveal(self, reveal_period: int) -> int:
|
|
156
|
+
"""
|
|
157
|
+
Estimates the seconds until the reveal window opens. Commits made in epoch E
|
|
158
|
+
become revealable in epoch E + reveal_period, i.e. after the next epoch
|
|
159
|
+
boundary plus (reveal_period - 1) full tempos. Best-effort: owner tempo
|
|
160
|
+
changes, manually triggered epochs, or per-block epoch deferrals can shift
|
|
161
|
+
the actual boundary.
|
|
162
|
+
"""
|
|
163
|
+
bh = await self.subtensor.substrate.get_chain_head()
|
|
164
|
+
current_block, tempo, next_epoch = await asyncio.gather(
|
|
165
|
+
self.subtensor.substrate.get_block_number(block_hash=bh),
|
|
166
|
+
self.subtensor.get_hyperparameter(
|
|
167
|
+
param_name="Tempo", netuid=self.netuid, block_hash=bh
|
|
168
|
+
),
|
|
169
|
+
self.subtensor.get_next_epoch_start_block(self.netuid, block_hash=bh),
|
|
170
|
+
)
|
|
171
|
+
tempo = int(tempo)
|
|
172
|
+
if next_epoch is not None:
|
|
173
|
+
blocks_until_next_epoch = max(next_epoch - current_block, 0)
|
|
174
|
+
else:
|
|
175
|
+
# Legacy modulo scheduler (chains without the dynamic-tempo runtime).
|
|
176
|
+
remainder = (current_block + self.netuid + 1) % (tempo + 1)
|
|
177
|
+
blocks_until_next_epoch = (tempo + 1 - remainder) % (tempo + 1)
|
|
178
|
+
blocks_until_reveal = (
|
|
179
|
+
blocks_until_next_epoch + max(reveal_period - 1, 0) * tempo
|
|
180
|
+
)
|
|
181
|
+
return blocks_until_reveal * 12
|
|
182
|
+
|
|
155
183
|
async def _commit_reveal(
|
|
156
184
|
self, weight_uids: list[int], weight_vals: list[int]
|
|
157
185
|
) -> tuple[bool, str, Optional[str]]:
|
|
158
|
-
|
|
186
|
+
reveal_period = int(
|
|
159
187
|
await self.subtensor.get_hyperparameter(
|
|
160
188
|
param_name="get_commit_reveal_period",
|
|
161
189
|
netuid=self.netuid,
|
|
@@ -174,6 +202,7 @@ class SetWeightsExtrinsic:
|
|
|
174
202
|
)
|
|
175
203
|
|
|
176
204
|
if commit_success:
|
|
205
|
+
interval = await self._seconds_until_reveal(reveal_period)
|
|
177
206
|
current_time = datetime.now().astimezone().replace(microsecond=0)
|
|
178
207
|
reveal_time = (current_time + timedelta(seconds=interval)).isoformat()
|
|
179
208
|
cli_retry_cmd = f"--netuid {self.netuid} --uids {weight_uids} --weights {self.weights} --reveal-using-salt {self.salt}"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/bittensor/chain_data.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/bittensor/extrinsics/root.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/bittensor/minigraph.py
RENAMED
|
File without changes
|
{bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/bittensor/networking.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/bittensor/templates/table.j2
RENAMED
|
File without changes
|
{bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/bittensor/templates/view.css
RENAMED
|
File without changes
|
{bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/bittensor/templates/view.j2
RENAMED
|
File without changes
|
{bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/bittensor/templates/view.js
RENAMED
|
File without changes
|
|
File without changes
|
{bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/axon/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/crowd/__init__.py
RENAMED
|
File without changes
|
{bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/crowd/contribute.py
RENAMED
|
File without changes
|
{bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/crowd/contributors.py
RENAMED
|
File without changes
|
{bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/crowd/create.py
RENAMED
|
File without changes
|
{bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/crowd/dissolve.py
RENAMED
|
File without changes
|
{bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/crowd/refund.py
RENAMED
|
File without changes
|
{bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/crowd/update.py
RENAMED
|
File without changes
|
{bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/crowd/utils.py
RENAMED
|
File without changes
|
{bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/crowd/view.py
RENAMED
|
File without changes
|
{bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/lock/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/lock/common.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/stake/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/stake/auto_staking.py
RENAMED
|
File without changes
|
{bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/stake/claim.py
RENAMED
|
File without changes
|
{bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/stake/list.py
RENAMED
|
File without changes
|
{bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/stake/move.py
RENAMED
|
File without changes
|
{bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/stake/remove.py
RENAMED
|
File without changes
|
{bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/stake/wizard.py
RENAMED
|
File without changes
|
{bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/subnets/__init__.py
RENAMED
|
File without changes
|
{bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/subnets/mechanisms.py
RENAMED
|
File without changes
|
{bittensor_cli-9.23.0rc1 → bittensor_cli-9.23.0rc2}/bittensor_cli/src/commands/subnets/price.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|