Skip to content

Commit 86fdc36

Browse files
authored
Merge pull request #1166 from opentensor/fix/total-stake-migration
Fix TotalIssuance on migration and TotalStake check
2 parents f7f1b66 + d39a6ea commit 86fdc36

File tree

7 files changed

+166
-82
lines changed

7 files changed

+166
-82
lines changed

pallets/subtensor/src/macros/hooks.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,9 @@ mod hooks {
8282

8383
#[cfg(feature = "try-runtime")]
8484
fn try_state(_n: BlockNumberFor<T>) -> Result<(), sp_runtime::TryRuntimeError> {
85-
Self::check_accounting_invariants()?;
85+
Self::check_total_issuance()?;
86+
// Disabled: https://github.com/opentensor/subtensor/pull/1166
87+
// Self::check_total_stake()?;
8688
Ok(())
8789
}
8890
}

pallets/subtensor/src/migrations/migrate_init_total_issuance.rs

Lines changed: 42 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use super::*;
2-
use frame_support::pallet_prelude::OptionQuery;
3-
use frame_support::{pallet_prelude::Identity, storage_alias};
2+
use frame_support::pallet_prelude::{Identity, OptionQuery, Weight};
3+
use frame_support::storage_alias;
44
use sp_std::vec::Vec;
55

66
// TODO: Implement comprehensive tests for this migration
@@ -14,10 +14,46 @@ pub mod deprecated_loaded_emission_format {
1414
StorageMap<Pallet<T>, Identity, u16, Vec<(AccountIdOf<T>, u64)>, OptionQuery>;
1515
}
1616

17+
pub(crate) fn migrate_init_total_issuance<T: Config>() -> Weight {
18+
// Calculate the total locked tokens across all subnets
19+
let subnets_len = crate::SubnetLocked::<T>::iter().count() as u64;
20+
let total_subnet_locked: u64 =
21+
crate::SubnetLocked::<T>::iter().fold(0, |acc, (_, v)| acc.saturating_add(v));
22+
23+
// Retrieve the total balance of all accounts
24+
let total_account_balances = <<T as crate::Config>::Currency as fungible::Inspect<
25+
<T as frame_system::Config>::AccountId,
26+
>>::total_issuance();
27+
28+
// Get the total stake from the system
29+
let total_stake = crate::TotalStake::<T>::get();
30+
31+
// Retrieve the previous total issuance for logging purposes
32+
let prev_total_issuance = crate::TotalIssuance::<T>::get();
33+
34+
// Calculate the new total issuance
35+
let new_total_issuance = total_account_balances
36+
.saturating_add(total_stake)
37+
.saturating_add(total_subnet_locked);
38+
39+
// Update the total issuance in storage
40+
crate::TotalIssuance::<T>::put(new_total_issuance);
41+
42+
// Log the change in total issuance
43+
log::info!(
44+
"Subtensor Pallet Total Issuance Updated: previous: {:?}, new: {:?}",
45+
prev_total_issuance,
46+
new_total_issuance
47+
);
48+
49+
// Return the weight of the operation
50+
// We performed subnets_len + 5 reads and 1 write
51+
<T as frame_system::Config>::DbWeight::get().reads_writes(subnets_len.saturating_add(5), 1)
52+
}
53+
1754
pub mod initialise_total_issuance {
1855
use frame_support::pallet_prelude::Weight;
19-
use frame_support::traits::{fungible, OnRuntimeUpgrade};
20-
use sp_core::Get;
56+
use frame_support::traits::OnRuntimeUpgrade;
2157

2258
use crate::*;
2359

@@ -33,41 +69,7 @@ pub mod initialise_total_issuance {
3369
///
3470
/// Returns the weight of the migration operation.
3571
fn on_runtime_upgrade() -> Weight {
36-
// Calculate the total locked tokens across all subnets
37-
let subnets_len = crate::SubnetLocked::<T>::iter().count() as u64;
38-
let total_subnet_locked: u64 =
39-
crate::SubnetLocked::<T>::iter().fold(0, |acc, (_, v)| acc.saturating_add(v));
40-
41-
// Retrieve the total balance of all accounts
42-
let total_account_balances = <<T as crate::Config>::Currency as fungible::Inspect<
43-
<T as frame_system::Config>::AccountId,
44-
>>::total_issuance();
45-
46-
// Get the total stake from the system
47-
let total_stake = crate::TotalStake::<T>::get();
48-
49-
// Retrieve the previous total issuance for logging purposes
50-
let prev_total_issuance = crate::TotalIssuance::<T>::get();
51-
52-
// Calculate the new total issuance
53-
let new_total_issuance = total_account_balances
54-
.saturating_add(total_stake)
55-
.saturating_add(total_subnet_locked);
56-
57-
// Update the total issuance in storage
58-
crate::TotalIssuance::<T>::put(new_total_issuance);
59-
60-
// Log the change in total issuance
61-
log::info!(
62-
"Subtensor Pallet Total Issuance Updated: previous: {:?}, new: {:?}",
63-
prev_total_issuance,
64-
new_total_issuance
65-
);
66-
67-
// Return the weight of the operation
68-
// We performed subnets_len + 5 reads and 1 write
69-
<T as frame_system::Config>::DbWeight::get()
70-
.reads_writes(subnets_len.saturating_add(5), 1)
72+
super::migrate_init_total_issuance::<T>()
7173
}
7274

7375
/// Performs post-upgrade checks to ensure the migration was successful.
@@ -76,7 +78,7 @@ pub mod initialise_total_issuance {
7678
#[cfg(feature = "try-runtime")]
7779
fn post_upgrade(_state: Vec<u8>) -> Result<(), sp_runtime::TryRuntimeError> {
7880
// Verify that all accounting invariants are satisfied after the migration
79-
crate::Pallet::<T>::check_accounting_invariants()?;
81+
crate::Pallet::<T>::check_total_issuance()?;
8082
Ok(())
8183
}
8284
}

pallets/subtensor/src/migrations/migrate_rao.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
use super::*;
21
use alloc::string::String;
2+
33
use frame_support::IterableStorageMap;
44
use frame_support::{traits::Get, weights::Weight};
5-
use log;
65
use sp_runtime::format;
76
use substrate_fixed::types::U64F64;
87

8+
use super::*;
9+
use crate::subnets::subnet::POOL_INITIAL_TAO;
10+
911
pub fn migrate_rao<T: Config>() -> Weight {
1012
let migration_name = b"migrate_rao".to_vec();
1113

@@ -69,12 +71,12 @@ pub fn migrate_rao<T: Config>() -> Weight {
6971
TokenSymbol::<T>::insert(netuid, Pallet::<T>::get_symbol_for_subnet(0));
7072
continue;
7173
}
72-
let owner: T::AccountId = SubnetOwner::<T>::get(netuid);
73-
let lock: u64 = SubnetLocked::<T>::get(netuid);
74+
let owner = SubnetOwner::<T>::get(netuid);
75+
let lock = SubnetLocked::<T>::get(netuid);
7476

7577
// Put initial TAO from lock into subnet TAO and produce numerically equal amount of Alpha
7678
// The initial TAO is the locked amount, with a minimum of 1 RAO and a cap of 100 TAO.
77-
let pool_initial_tao = 100_000_000_000.min(lock.max(1));
79+
let pool_initial_tao = POOL_INITIAL_TAO.min(lock.max(1));
7880

7981
let remaining_lock = lock.saturating_sub(pool_initial_tao);
8082
// Refund the owner for the remaining lock.
@@ -127,6 +129,10 @@ pub fn migrate_rao<T: Config>() -> Weight {
127129
// TargetStakesPerInterval::<T>::put(10); (DEPRECATED)
128130
}
129131

132+
// update `TotalIssuance`, because currency issuance (`T::Currency`) has changed due to lock
133+
// refunds above
134+
weight = weight.saturating_add(migrate_init_total_issuance::migrate_init_total_issuance::<T>());
135+
130136
// Mark the migration as completed
131137
HasMigrationRun::<T>::insert(&migration_name, true);
132138
weight = weight.saturating_add(T::DbWeight::get().writes(1));

pallets/subtensor/src/subnets/subnet.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ use super::*;
22
use frame_support::IterableStorageMap;
33
use sp_core::Get;
44

5+
pub(crate) const POOL_INITIAL_TAO: u64 = 100_000_000_000;
6+
57
impl<T: Config> Pallet<T> {
68
/// Retrieves the unique identifier (UID) for the root network.
79
///
@@ -235,7 +237,7 @@ impl<T: Config> Pallet<T> {
235237

236238
// Put initial TAO from lock into subnet TAO and produce numerically equal amount of Alpha
237239
// The initial TAO is the locked amount, with a minimum of 1 RAO and a cap of 100 TAO.
238-
let pool_initial_tao = 100_000_000_000.min(actual_tao_lock_amount.max(1));
240+
let pool_initial_tao = POOL_INITIAL_TAO.min(actual_tao_lock_amount.max(1));
239241

240242
let actual_tao_lock_amount_less_pool_tao =
241243
actual_tao_lock_amount.saturating_sub(pool_initial_tao);

pallets/subtensor/src/utils/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ use super::*;
22
pub mod identity;
33
pub mod misc;
44
pub mod rate_limiting;
5+
#[cfg(feature = "try-runtime")]
56
pub mod try_state;
Lines changed: 48 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,72 @@
1+
use frame_support::traits::fungible::Inspect;
2+
13
use super::*;
4+
use crate::subnets::subnet::POOL_INITIAL_TAO;
25

36
impl<T: Config> Pallet<T> {
4-
/// Checks if the accounting invariants for [`TotalStake`], [`TotalSubnetLocked`], and [`TotalIssuance`] are correct.
5-
///
6-
/// This function verifies that:
7-
/// 1. The sum of all stakes matches the [`TotalStake`].
8-
/// 2. The [`TotalSubnetLocked`] is correctly calculated.
9-
/// 3. The [`TotalIssuance`] equals the sum of currency issuance, total stake, and total subnet locked.
10-
///
11-
/// # Returns
12-
///
13-
/// Returns `Ok(())` if all invariants are correct, otherwise returns an error.
14-
#[cfg(feature = "try-runtime")]
15-
pub fn check_accounting_invariants() -> Result<(), sp_runtime::TryRuntimeError> {
16-
use frame_support::traits::fungible::Inspect;
17-
18-
// Calculate the total staked amount
19-
let mut total_staked: u64 = 0;
20-
for (_hotkey, _coldkey, stake) in Stake::<T>::iter() {
21-
total_staked = total_staked.saturating_add(stake);
22-
}
23-
24-
// Verify that the calculated total stake matches the stored TotalStake
25-
ensure!(
26-
total_staked == TotalStake::<T>::get(),
27-
"TotalStake does not match total staked",
28-
);
29-
7+
/// Checks [`TotalIssuance`] equals the sum of currency issuance, total stake, and total subnet
8+
/// locked.
9+
pub(crate) fn check_total_issuance() -> Result<(), sp_runtime::TryRuntimeError> {
3010
// Get the total subnet locked amount
31-
let total_subnet_locked: u64 = Self::get_total_subnet_locked();
11+
let total_subnet_locked = Self::get_total_subnet_locked();
3212

3313
// Get the total currency issuance
34-
let currency_issuance: u64 = T::Currency::total_issuance();
14+
let currency_issuance = T::Currency::total_issuance();
3515

3616
// Calculate the expected total issuance
37-
let expected_total_issuance: u64 = currency_issuance
38-
.saturating_add(total_staked)
17+
let expected_total_issuance = currency_issuance
18+
.saturating_add(TotalStake::<T>::get())
3919
.saturating_add(total_subnet_locked);
4020

4121
// Verify the diff between calculated TI and actual TI is less than delta
4222
//
4323
// These values can be off slightly due to float rounding errors.
4424
// They are corrected every runtime upgrade.
45-
const DELTA: u64 = 1000;
46-
let diff = if TotalIssuance::<T>::get() > expected_total_issuance {
47-
TotalIssuance::<T>::get().checked_sub(expected_total_issuance)
25+
let delta = 1000;
26+
let total_issuance = TotalIssuance::<T>::get();
27+
28+
let diff = if total_issuance > expected_total_issuance {
29+
total_issuance.checked_sub(expected_total_issuance)
4830
} else {
49-
expected_total_issuance.checked_sub(TotalIssuance::<T>::get())
31+
expected_total_issuance.checked_sub(total_issuance)
5032
}
5133
.expect("LHS > RHS");
34+
5235
ensure!(
53-
diff <= DELTA,
36+
diff <= delta,
5437
"TotalIssuance diff greater than allowable delta",
5538
);
5639

5740
Ok(())
5841
}
42+
43+
/// Checks the sum of all stakes matches the [`TotalStake`].
44+
#[allow(dead_code)]
45+
pub(crate) fn check_total_stake() -> Result<(), sp_runtime::TryRuntimeError> {
46+
// Calculate the total staked amount
47+
let total_staked = SubnetTAO::<T>::iter().fold(0u64, |acc, (netuid, stake)| {
48+
let acc = acc.saturating_add(stake);
49+
50+
if netuid == Self::get_root_netuid() {
51+
// root network doesn't have initial pool TAO
52+
acc
53+
} else {
54+
acc.saturating_sub(POOL_INITIAL_TAO)
55+
}
56+
});
57+
58+
log::warn!(
59+
"total_staked: {}, TotalStake: {}",
60+
total_staked,
61+
TotalStake::<T>::get()
62+
);
63+
64+
// Verify that the calculated total stake matches the stored TotalStake
65+
ensure!(
66+
total_staked == TotalStake::<T>::get(),
67+
"TotalStake does not match total staked",
68+
);
69+
70+
Ok(())
71+
}
5972
}

scripts/try-runtime-upgrade.sh

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#!/usr/bin/env bash
2+
3+
# Tries runtime upgrade (via try-runtime).
4+
#
5+
# Usage:
6+
# try-runtime-upgrade.sh [-p <runtime-path>] [-u <live-chain-url>] [-s <snapshot-path>]
7+
#
8+
# Dependencies:
9+
# - rust toolchain
10+
# - try-runtime-cli
11+
12+
set -eou pipefail
13+
14+
runtime_wasm_path="./target/release/wbuild/node-subtensor-runtime/node_subtensor_runtime.compact.wasm"
15+
live_chain_url="wss://dev.chain.opentensor.ai:443"
16+
snapshot_path=""
17+
18+
parse_args() {
19+
u_provided=false
20+
21+
while getopts "r:u:s:" opt; do
22+
case "${opt}" in
23+
r) runtime_wasm_path="${OPTARG}" ;;
24+
u)
25+
live_chain_url="${OPTARG}"
26+
u_provided=true
27+
;;
28+
s) snapshot_path="${OPTARG}" ;;
29+
*) echo "Usage: $(basename "$0") [-r <runtime-path>] [-u <live-chain-url>] [-s <snapshot-path>]" && exit 1 ;;
30+
esac
31+
done
32+
33+
# Prevent specifying URI if snapshot is specified
34+
if [ -n "$snapshot_path" ] && [ "$u_provided" = true ]; then
35+
echo "Error: Either live URI or snapshot path should be specified, but not both."
36+
exit 1
37+
fi
38+
}
39+
40+
build_runtime() {
41+
cargo build -p node-subtensor-runtime --release --features "metadata-hash,try-runtime"
42+
}
43+
44+
do_try_runtime() {
45+
if [ -n "$snapshot_path" ]; then
46+
chain_state="snap --path $snapshot_path"
47+
else
48+
chain_state="live --uri $live_chain_url"
49+
fi
50+
51+
eval "try-runtime --runtime $runtime_wasm_path on-runtime-upgrade \
52+
--no-weight-warnings --disable-spec-version-check --disable-idempotency-checks --checks=all \
53+
$chain_state"
54+
}
55+
56+
parse_args "$@"
57+
build_runtime
58+
do_try_runtime

0 commit comments

Comments
 (0)