Skip to content

Commit d48447d

Browse files
Migrate allocator role to sudo key (#161)
## Summary - Adds spec 29 runtime migration that reads current sudo key from `Sudo::Key` and makes it the sole governance allocator. - Whitelists the sudo key, funds its registration burn from DAO treasury, and registers it through the normal `torus0` registration path using the fixed allocator name `new_allocator`, reusing prior allocator URL/metadata. - Unstakes prior allocator outbound stake back to free balance before deregistration. - Moves existing non-allocator stake targeting prior allocator(s) to the sudo allocator through `torus0::stake::transfer_stake` before old allocator agents are deregistered. - Bumps runtime spec version 28 -> 29 and governance storage version 5 -> 6. - Updates changelog entries. ## Live-state checks - Current mainnet has 1 allocator distinct from sudo. - Current mainnet has 447 inbound stake edges to the old allocator, and `StakingTo` / `StakedBy` keysets match for those edges. - Current mainnet has 1 old-allocator outbound edge: old allocator self-stake, amount 452734767. The migration unstakes this to the old allocator free balance before deregistration. - Current mainnet has no existing agent named `new_allocator`. - Current sudo key is not already registered as an agent. ## Verification - `cargo fmt` - `SKIP_WASM_BUILD=1 cargo check -p torus-runtime` - `cargo clippy -p torus-runtime -p pallet-governance -p pallet-torus0 --tests -- -D warnings` - `SKIP_WASM_BUILD=1 cargo test -p torus-runtime -p pallet-governance -p pallet-torus0` - `SKIP_WASM_BUILD=1 cargo test --workspace --exclude torus-client --exclude torus-mcp` - `just try-runtime-upgrade-mainnet` ## Note Full `just check`/`just test` and local pre-push hook hit `torus-client` build.rs DNS resolution for `api.testnet.torus.network`. The runtime-focused checks and mainnet try-runtime upgrade pass. Co-authored-by: Honza <honza@renlabs.dev>
1 parent 60f1069 commit d48447d

5 files changed

Lines changed: 160 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,18 @@
11
# Changelog
22

3+
## Spec 29
4+
5+
This release migrates the allocator role to the current sudo key.
6+
7+
### Allocator Migration
8+
9+
- Runtime `spec_version` is bumped from `28` to `29`.
10+
- Governance storage version is bumped from `5` to `6`.
11+
- The migration reads the current sudo key and makes it the only governance allocator.
12+
- The sudo allocator is whitelisted, funded with the current registration burn from the DAO treasury, and registered through the normal `torus0` registration path with the fixed name `new_allocator`.
13+
- Existing stake to prior allocators is moved to the sudo allocator before prior allocator agents are deregistered; prior allocator outbound stake is unstaked back to free balance first.
14+
- Prior allocator agents are deregistered through the `torus0` deregistration path after their inbound stake has moved.
15+
316
## Spec 27
417

518
This release introduces an explicit admin-controlled authority rotation path for the solo-chain runtime, replacing ad hoc emergency storage mutation workflows with a dedicated runtime extrinsic.

docs/changes/spec-29.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Spec 29 Changelog
2+
3+
This release migrates the allocator role to the current sudo key.
4+
5+
## 1. Extrinsics
6+
7+
No extrinsics were added, changed, or removed.
8+
9+
## 2. Events
10+
11+
No events were added, changed, or removed.
12+
13+
## 3. Storage Items
14+
15+
No storage items were added, changed, or removed.
16+
17+
## 4. Runtime Migration
18+
19+
- Runtime `spec_version` is bumped from `28` to `29`.
20+
- `governance` storage version is bumped from `5` to `6`.
21+
- The migration reads the current sudo key and makes it the only governance allocator.
22+
- The governance allocator set is replaced with the sudo key.
23+
- The sudo allocator is whitelisted, funded with the current registration burn from the DAO treasury, and registered through the normal torus0 registration path with the fixed name `new_allocator`.
24+
- Stake to prior allocators is moved to the sudo allocator before prior allocator agents are deregistered; prior allocator outbound stake is unstaked back to free balance first.
25+
- Prior allocator agents are deregistered through the torus0 deregistration path after their inbound stake has moved.

pallets/governance/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ use crate::{
4141
pub mod pallet {
4242
#![allow(clippy::too_many_arguments)]
4343

44-
const STORAGE_VERSION: StorageVersion = StorageVersion::new(5);
44+
const STORAGE_VERSION: StorageVersion = StorageVersion::new(6);
4545

4646
use pallet_permission0_api::{CuratorPermissions, Permission0Api, Permission0CuratorApi};
4747
use polkadot_sdk::sp_core::ConstBool;

runtime/src/lib.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use sp_version::RuntimeVersion;
2222
pub mod apis;
2323
pub mod benchmarks;
2424
pub mod configs;
25+
pub mod migrations;
2526
pub mod precompiles;
2627

2728
impl_opaque_keys! {
@@ -37,7 +38,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
3738
spec_name: create_runtime_str!("torus-runtime"),
3839
impl_name: create_runtime_str!("torus-runtime"),
3940
authoring_version: 1,
40-
spec_version: 28,
41+
spec_version: 29,
4142
impl_version: 1,
4243
apis: apis::RUNTIME_API_VERSIONS,
4344
transaction_version: 1,
@@ -82,7 +83,7 @@ pub type SignedPayload = sp_runtime::generic::SignedPayload<RuntimeCall, SignedE
8283
/// All migrations of the runtime, aside from the ones declared in the pallets.
8384
///
8485
/// This can be a tuple of types, each implementing `OnRuntimeUpgrade`.
85-
type Migrations = ();
86+
type Migrations = (migrations::MigrateAllocator,);
8687

8788
/// Executive: handles dispatch to the various modules.
8889
pub type RuntimeExecutive = frame_executive::Executive<

runtime/src/migrations.rs

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
use alloc::vec::Vec;
2+
3+
use polkadot_sdk::{
4+
frame_support::{
5+
pallet_prelude::*,
6+
storage_alias,
7+
traits::{Currency, ExistenceRequirement, OnRuntimeUpgrade, StorageVersion},
8+
},
9+
frame_system,
10+
sp_weights::Weight,
11+
};
12+
13+
use crate::Runtime;
14+
15+
type AccountId = <Runtime as frame_system::Config>::AccountId;
16+
type Agent = pallet_torus0::agent::Agent<Runtime>;
17+
18+
#[storage_alias(pallet_name)]
19+
type Key = StorageValue<polkadot_sdk::pallet_sudo::Pallet<Runtime>, AccountId, OptionQuery>;
20+
21+
/// Moves the allocator role to the current sudo key.
22+
pub struct MigrateAllocator;
23+
24+
impl OnRuntimeUpgrade for MigrateAllocator {
25+
fn on_runtime_upgrade() -> Weight {
26+
if StorageVersion::get::<pallet_governance::Pallet<Runtime>>() == StorageVersion::new(5) {
27+
if let Some(new_allocator) = sudo_key() {
28+
migrate_allocator(new_allocator);
29+
}
30+
31+
StorageVersion::new(6).put::<pallet_governance::Pallet<Runtime>>();
32+
}
33+
34+
Weight::zero()
35+
}
36+
}
37+
38+
fn sudo_key() -> Option<AccountId> {
39+
Key::get()
40+
}
41+
42+
fn migrate_allocator(new_allocator: AccountId) {
43+
let old_allocators = pallet_governance::Allocators::<Runtime>::iter_keys()
44+
.filter(|allocator| allocator != &new_allocator)
45+
.collect::<Vec<_>>();
46+
47+
let old_agent = old_allocators
48+
.iter()
49+
.find_map(pallet_torus0::Agents::<Runtime>::get);
50+
51+
pallet_governance::Whitelist::<Runtime>::insert(&new_allocator, ());
52+
replace_allocators(&new_allocator);
53+
register_allocator(&new_allocator, old_agent.as_ref());
54+
transfer_allocator_stake(&old_allocators, &new_allocator);
55+
56+
for old_allocator in old_allocators {
57+
let _ = pallet_torus0::agent::deregister::<Runtime>(old_allocator);
58+
}
59+
}
60+
61+
fn register_allocator(new_allocator: &AccountId, old_agent: Option<&Agent>) {
62+
if pallet_torus0::Agents::<Runtime>::contains_key(new_allocator) {
63+
return;
64+
}
65+
66+
if let Some(old_agent) = old_agent {
67+
fund_registration_burn(new_allocator);
68+
let _ = pallet_torus0::agent::register::<Runtime>(
69+
new_allocator.clone(),
70+
b"new_allocator".to_vec(),
71+
old_agent.url.to_vec(),
72+
old_agent.metadata.to_vec(),
73+
);
74+
}
75+
}
76+
77+
fn replace_allocators(new_allocator: &AccountId) {
78+
let _ = pallet_governance::Allocators::<Runtime>::clear(u32::MAX, None);
79+
pallet_governance::Allocators::<Runtime>::insert(new_allocator, ());
80+
}
81+
82+
fn fund_registration_burn(new_allocator: &AccountId) {
83+
let _ = <Runtime as pallet_torus0::Config>::Currency::transfer(
84+
&pallet_governance::DaoTreasuryAddress::<Runtime>::get(),
85+
new_allocator,
86+
pallet_torus0::Burn::<Runtime>::get(),
87+
ExistenceRequirement::AllowDeath,
88+
);
89+
}
90+
91+
fn transfer_allocator_stake(old_allocators: &[AccountId], new_allocator: &AccountId) {
92+
if !pallet_torus0::Agents::<Runtime>::contains_key(new_allocator) {
93+
return;
94+
}
95+
96+
let allocator_stakes = pallet_torus0::StakingTo::<Runtime>::iter()
97+
.filter(|(staker, _, _)| old_allocators.contains(staker))
98+
.collect::<Vec<_>>();
99+
100+
for (old_allocator, staked, amount) in allocator_stakes {
101+
let _ = pallet_torus0::stake::remove_stake::<Runtime>(old_allocator, staked, amount);
102+
}
103+
104+
let stakes = pallet_torus0::StakingTo::<Runtime>::iter()
105+
.filter(|(staker, staked, _)| {
106+
old_allocators.contains(staked) && !old_allocators.contains(staker)
107+
})
108+
.collect::<Vec<_>>();
109+
110+
for (staker, old_allocator, amount) in stakes {
111+
let _ = pallet_torus0::stake::transfer_stake::<Runtime>(
112+
staker,
113+
old_allocator,
114+
new_allocator.clone(),
115+
amount,
116+
);
117+
}
118+
}

0 commit comments

Comments
 (0)