Skip to content

Commit 621523a

Browse files
committed
Fix unstaking 99.999% and add tests
1 parent 7b0d84f commit 621523a

File tree

3 files changed

+210
-89
lines changed

3 files changed

+210
-89
lines changed

pallets/subtensor/src/tests/move_stake.rs

Lines changed: 110 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::*;
33
use approx::assert_abs_diff_eq;
44
use frame_support::{assert_err, assert_noop, assert_ok};
55
use sp_core::{Get, U256};
6-
use substrate_fixed::types::I96F32;
6+
use substrate_fixed::types::{I96F32, U64F64};
77

88
// 1. test_do_move_success
99
// Description: Test a successful move of stake between two hotkeys in the same subnet
@@ -1641,3 +1641,112 @@ fn test_stake_transfers_disabled_validate() {
16411641
assert_ok!(result3);
16421642
});
16431643
}
1644+
1645+
#[test]
1646+
// RUST_LOG=info cargo test --package pallet-subtensor --lib -- tests::staking::test_move_stake_specific_stake_into_subnet_fail --exact --show-output
1647+
fn test_move_stake_specific_stake_into_subnet_fail() {
1648+
new_test_ext(1).execute_with(|| {
1649+
let sn_owner_coldkey = U256::from(55453);
1650+
1651+
let hotkey_account_id = U256::from(533453);
1652+
let coldkey_account_id = U256::from(55454);
1653+
let hotkey_owner_account_id = U256::from(533454);
1654+
1655+
let existing_shares: U64F64 =
1656+
U64F64::from_num(161_986_254).saturating_div(U64F64::from_num(u64::MAX));
1657+
let existing_stake = 36_711_495_953;
1658+
1659+
let tao_in = 2_409_892_148_947;
1660+
let alpha_in = 15_358_708_513_716;
1661+
1662+
let tao_staked = 200_000_000;
1663+
1664+
//add network
1665+
let netuid: u16 = add_dynamic_network(&sn_owner_coldkey, &sn_owner_coldkey);
1666+
1667+
let origin_netuid: u16 = add_dynamic_network(&sn_owner_coldkey, &sn_owner_coldkey);
1668+
1669+
// Register hotkey on netuid
1670+
register_ok_neuron(netuid, hotkey_account_id, hotkey_owner_account_id, 0);
1671+
// Register hotkey on origin netuid
1672+
register_ok_neuron(origin_netuid, hotkey_account_id, hotkey_owner_account_id, 0);
1673+
1674+
// Check we have zero staked
1675+
assert_eq!(
1676+
SubtensorModule::get_total_stake_for_hotkey(&hotkey_account_id),
1677+
0
1678+
);
1679+
1680+
// Set a hotkey pool for the hotkey on destination subnet
1681+
let mut hotkey_pool = SubtensorModule::get_alpha_share_pool(hotkey_account_id, netuid);
1682+
hotkey_pool.update_value_for_one(&hotkey_owner_account_id, 1234); // Doesn't matter, will be overridden
1683+
1684+
// Adjust the total hotkey stake and shares to match the existing values
1685+
TotalHotkeyShares::<Test>::insert(hotkey_account_id, netuid, existing_shares);
1686+
TotalHotkeyAlpha::<Test>::insert(hotkey_account_id, netuid, existing_stake);
1687+
1688+
// Make the hotkey a delegate
1689+
Delegates::<Test>::insert(hotkey_account_id, 0);
1690+
1691+
// Setup Subnet pool
1692+
SubnetAlphaIn::<Test>::insert(netuid, alpha_in);
1693+
SubnetTAO::<Test>::insert(netuid, tao_in);
1694+
1695+
// Give TAO balance to coldkey
1696+
SubtensorModule::add_balance_to_coldkey_account(
1697+
&coldkey_account_id,
1698+
tao_staked + 1_000_000_000,
1699+
);
1700+
1701+
// Setup Subnet pool for origin netuid
1702+
SubnetAlphaIn::<Test>::insert(origin_netuid, alpha_in + 10_000_000);
1703+
SubnetTAO::<Test>::insert(origin_netuid, tao_in + 10_000_000);
1704+
1705+
// Add stake as new hotkey
1706+
assert_ok!(SubtensorModule::add_stake(
1707+
RuntimeOrigin::signed(coldkey_account_id),
1708+
hotkey_account_id,
1709+
origin_netuid,
1710+
tao_staked,
1711+
),);
1712+
let alpha_to_move = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(
1713+
&hotkey_account_id,
1714+
&coldkey_account_id,
1715+
origin_netuid,
1716+
);
1717+
1718+
// Move stake to destination subnet
1719+
assert_ok!(SubtensorModule::move_stake(
1720+
RuntimeOrigin::signed(coldkey_account_id),
1721+
hotkey_account_id,
1722+
hotkey_account_id,
1723+
origin_netuid,
1724+
netuid,
1725+
alpha_to_move,
1726+
));
1727+
1728+
// Check that the stake has been moved
1729+
assert_eq!(
1730+
SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(
1731+
&hotkey_account_id,
1732+
&coldkey_account_id,
1733+
origin_netuid
1734+
),
1735+
0
1736+
);
1737+
let fee = DefaultStakingFee::<Test>::get();
1738+
let alpha_fee: I96F32 = I96F32::from_num(fee) / SubtensorModule::get_alpha_price(netuid);
1739+
let expected_value = I96F32::from_num(alpha_to_move)
1740+
* SubtensorModule::get_alpha_price(origin_netuid)
1741+
/ SubtensorModule::get_alpha_price(netuid);
1742+
assert_abs_diff_eq!(
1743+
SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(
1744+
&hotkey_account_id,
1745+
&coldkey_account_id,
1746+
netuid
1747+
),
1748+
(expected_value - alpha_fee).to_num::<u64>(),
1749+
epsilon = (expected_value / 1000).to_num::<u64>()
1750+
);
1751+
});
1752+
}

pallets/subtensor/src/tests/staking.rs

Lines changed: 88 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -3692,111 +3692,121 @@ fn test_add_stake_specific_stake_into_subnet_fail() {
36923692
});
36933693
}
36943694

3695+
// cargo test --package pallet-subtensor --lib -- tests::staking::test_remove_99_999_per_cent_stake_removes_all --exact --show-output
36953696
#[test]
3696-
// RUST_LOG=info cargo test --package pallet-subtensor --lib -- tests::staking::test_move_stake_specific_stake_into_subnet_fail --exact --show-output
3697-
fn test_move_stake_specific_stake_into_subnet_fail() {
3697+
fn test_remove_99_9991_per_cent_stake_removes_all() {
3698+
// When we remove stake, the total issuance of the balances pallet should not change
3699+
// this is because the stake should be part of the coldkey account balance (reserved/locked)
3700+
// then the removed stake just becomes free balance
36983701
new_test_ext(1).execute_with(|| {
3699-
let sn_owner_coldkey = U256::from(55453);
3700-
3701-
let hotkey_account_id = U256::from(533453);
3702-
let coldkey_account_id = U256::from(55454);
3703-
let hotkey_owner_account_id = U256::from(533454);
3704-
3705-
let existing_shares: U64F64 =
3706-
U64F64::from_num(161_986_254).saturating_div(U64F64::from_num(u64::MAX));
3707-
let existing_stake = 36_711_495_953;
3708-
3709-
let tao_in = 2_409_892_148_947;
3710-
let alpha_in = 15_358_708_513_716;
3711-
3712-
let tao_staked = 200_000_000;
3702+
let subnet_owner_coldkey = U256::from(1);
3703+
let subnet_owner_hotkey = U256::from(2);
3704+
let hotkey_account_id = U256::from(581337);
3705+
let coldkey_account_id = U256::from(81337);
3706+
let amount = 10_000_000_000;
3707+
let netuid: u16 = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey);
3708+
let fee = DefaultStakingFee::<Test>::get();
3709+
register_ok_neuron(netuid, hotkey_account_id, coldkey_account_id, 192213123);
37133710

3714-
//add network
3715-
let netuid: u16 = add_dynamic_network(&sn_owner_coldkey, &sn_owner_coldkey);
3711+
// Give it some $$$ in his coldkey balance
3712+
SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, amount);
37163713

3717-
let origin_netuid: u16 = add_dynamic_network(&sn_owner_coldkey, &sn_owner_coldkey);
3714+
// Stake to hotkey account, and check if the result is ok
3715+
assert_ok!(SubtensorModule::add_stake(
3716+
RuntimeOrigin::signed(coldkey_account_id),
3717+
hotkey_account_id,
3718+
netuid,
3719+
amount
3720+
));
37183721

3719-
// Register hotkey on netuid
3720-
register_ok_neuron(netuid, hotkey_account_id, hotkey_owner_account_id, 0);
3721-
// Register hotkey on origin netuid
3722-
register_ok_neuron(origin_netuid, hotkey_account_id, hotkey_owner_account_id, 0);
3722+
// Remove 99.9991% stake
3723+
let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(
3724+
&hotkey_account_id,
3725+
&coldkey_account_id,
3726+
netuid,
3727+
);
3728+
assert_ok!(SubtensorModule::remove_stake(
3729+
RuntimeOrigin::signed(coldkey_account_id),
3730+
hotkey_account_id,
3731+
netuid,
3732+
(U64F64::from_num(alpha) * U64F64::from_num(0.999991)).to_num::<u64>()
3733+
));
37233734

3724-
// Check we have zero staked
3735+
// Check that all alpha was unstaked and all TAO balance was returned (less fees)
3736+
assert_abs_diff_eq!(
3737+
SubtensorModule::get_coldkey_balance(&coldkey_account_id),
3738+
amount - fee * 2,
3739+
epsilon = 10000,
3740+
);
37253741
assert_eq!(
37263742
SubtensorModule::get_total_stake_for_hotkey(&hotkey_account_id),
37273743
0
37283744
);
3729-
3730-
// Set a hotkey pool for the hotkey on destination subnet
3731-
let mut hotkey_pool = SubtensorModule::get_alpha_share_pool(hotkey_account_id, netuid);
3732-
hotkey_pool.update_value_for_one(&hotkey_owner_account_id, 1234); // Doesn't matter, will be overridden
3733-
3734-
// Adjust the total hotkey stake and shares to match the existing values
3735-
TotalHotkeyShares::<Test>::insert(hotkey_account_id, netuid, existing_shares);
3736-
TotalHotkeyAlpha::<Test>::insert(hotkey_account_id, netuid, existing_stake);
3737-
3738-
// Make the hotkey a delegate
3739-
Delegates::<Test>::insert(hotkey_account_id, 0);
3740-
3741-
// Setup Subnet pool
3742-
SubnetAlphaIn::<Test>::insert(netuid, alpha_in);
3743-
SubnetTAO::<Test>::insert(netuid, tao_in);
3744-
3745-
// Give TAO balance to coldkey
3746-
SubtensorModule::add_balance_to_coldkey_account(
3745+
let new_alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(
3746+
&hotkey_account_id,
37473747
&coldkey_account_id,
3748-
tao_staked + 1_000_000_000,
3748+
netuid,
37493749
);
3750+
assert_eq!(new_alpha, 0);
3751+
});
3752+
}
3753+
3754+
// cargo test --package pallet-subtensor --lib -- tests::staking::test_remove_99_9989_per_cent_stake_leaves_a_little --exact --show-output
3755+
#[test]
3756+
fn test_remove_99_9989_per_cent_stake_leaves_a_little() {
3757+
// When we remove stake, the total issuance of the balances pallet should not change
3758+
// this is because the stake should be part of the coldkey account balance (reserved/locked)
3759+
// then the removed stake just becomes free balance
3760+
new_test_ext(1).execute_with(|| {
3761+
let subnet_owner_coldkey = U256::from(1);
3762+
let subnet_owner_hotkey = U256::from(2);
3763+
let hotkey_account_id = U256::from(581337);
3764+
let coldkey_account_id = U256::from(81337);
3765+
let amount = 10_000_000_000;
3766+
let netuid: u16 = add_dynamic_network(&subnet_owner_hotkey, &subnet_owner_coldkey);
3767+
let fee = DefaultStakingFee::<Test>::get();
3768+
register_ok_neuron(netuid, hotkey_account_id, coldkey_account_id, 192213123);
37503769

3751-
// Setup Subnet pool for origin netuid
3752-
SubnetAlphaIn::<Test>::insert(origin_netuid, alpha_in + 10_000_000);
3753-
SubnetTAO::<Test>::insert(origin_netuid, tao_in + 10_000_000);
3770+
// Give it some $$$ in his coldkey balance
3771+
SubtensorModule::add_balance_to_coldkey_account(&coldkey_account_id, amount);
37543772

3755-
// Add stake as new hotkey
3773+
// Stake to hotkey account, and check if the result is ok
37563774
assert_ok!(SubtensorModule::add_stake(
37573775
RuntimeOrigin::signed(coldkey_account_id),
37583776
hotkey_account_id,
3759-
origin_netuid,
3760-
tao_staked,
3761-
),);
3762-
let alpha_to_move = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(
3777+
netuid,
3778+
amount
3779+
));
3780+
3781+
// Remove 99.9989% stake
3782+
let alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(
37633783
&hotkey_account_id,
37643784
&coldkey_account_id,
3765-
origin_netuid,
3785+
netuid,
37663786
);
3767-
3768-
// Move stake to destination subnet
3769-
assert_ok!(SubtensorModule::move_stake(
3787+
assert_ok!(SubtensorModule::remove_stake(
37703788
RuntimeOrigin::signed(coldkey_account_id),
37713789
hotkey_account_id,
3772-
hotkey_account_id,
3773-
origin_netuid,
37743790
netuid,
3775-
alpha_to_move,
3791+
(U64F64::from_num(alpha) * U64F64::from_num(0.99)).to_num::<u64>()
37763792
));
37773793

3778-
// Check that the stake has been moved
3779-
assert_eq!(
3780-
SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(
3781-
&hotkey_account_id,
3782-
&coldkey_account_id,
3783-
origin_netuid
3784-
),
3785-
0
3794+
// Check that all alpha was unstaked and 99% TAO balance was returned (less fees)
3795+
assert_abs_diff_eq!(
3796+
SubtensorModule::get_coldkey_balance(&coldkey_account_id),
3797+
(amount as f64 * 0.99) as u64 - fee * 2,
3798+
epsilon = amount / 1000,
37863799
);
3787-
let fee = DefaultStakingFee::<Test>::get();
3788-
let alpha_fee: I96F32 = I96F32::from_num(fee) / SubtensorModule::get_alpha_price(netuid);
3789-
let expected_value = I96F32::from_num(alpha_to_move)
3790-
* SubtensorModule::get_alpha_price(origin_netuid)
3791-
/ SubtensorModule::get_alpha_price(netuid);
37923800
assert_abs_diff_eq!(
3793-
SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(
3794-
&hotkey_account_id,
3795-
&coldkey_account_id,
3796-
netuid
3797-
),
3798-
(expected_value - alpha_fee).to_num::<u64>(),
3799-
epsilon = (expected_value / 1000).to_num::<u64>()
3801+
SubtensorModule::get_total_stake_for_hotkey(&hotkey_account_id),
3802+
(amount as f64 * 0.01) as u64,
3803+
epsilon = amount / 1000,
3804+
);
3805+
let new_alpha = SubtensorModule::get_stake_for_hotkey_and_coldkey_on_subnet(
3806+
&hotkey_account_id,
3807+
&coldkey_account_id,
3808+
netuid,
38003809
);
3810+
assert_abs_diff_eq!(new_alpha, (alpha as f64 * 0.01) as u64, epsilon = 10);
38013811
});
38023812
}

primitives/share-pool/src/lib.rs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -124,16 +124,14 @@ where
124124
let current_share: U64F64 = self.state_ops.get_share(key);
125125
let denominator: U64F64 = self.state_ops.get_denominator();
126126
let initial_value: i64 = self.get_value(key) as i64;
127-
128-
// First, update shared value
129-
self.update_value_for_all(update);
130-
let new_shared_value: U64F64 = self.state_ops.get_shared_value();
127+
let mut actual_update: i64 = update;
131128

132129
// Then, update this key's share
133130
if denominator == 0 {
134131
// Initialize the pool. The first key gets all.
135-
self.state_ops.set_denominator(new_shared_value);
136-
self.state_ops.set_share(key, new_shared_value);
132+
let update_fixed: U64F64 = U64F64::saturating_from_num(update);
133+
self.state_ops.set_denominator(update_fixed);
134+
self.state_ops.set_share(key, update_fixed);
137135
} else {
138136
let shares_per_update: I64F64 =
139137
self.get_shares_per_update(update, &shared_value, &denominator);
@@ -168,15 +166,19 @@ where
168166
// yes, precision is low, just remove all
169167
new_share = U64F64::saturating_from_num(0);
170168
new_denominator = denominator.saturating_sub(current_share);
169+
actual_update = initial_value.neg();
171170
}
172171

173172
self.state_ops.set_denominator(new_denominator);
174173
self.state_ops.set_share(key, new_share);
175174
}
176175
}
177176

178-
let final_value: i64 = self.get_value(key) as i64;
179-
final_value.saturating_sub(initial_value)
177+
// Update shared value
178+
self.update_value_for_all(actual_update);
179+
180+
// Return actual udate
181+
actual_update
180182
}
181183
}
182184

@@ -335,8 +337,8 @@ mod tests {
335337

336338
// First to stake gets all accumulated emission if there are no other stakers
337339
// (which is artificial situation because there will be no emissions if there is no stake)
338-
assert!((value1 - 1_001_000_000_000_000).abs() < 10);
339-
assert!((value2 - 1_000_000_000_000).abs() < 10);
340+
assert!((value1 - 1_001_000_000_000_000).abs() < 100);
341+
assert!((value2 - 1_000_000_000_000).abs() < 100);
340342
}
341343

342344
// cargo test --package share-pool --lib -- tests::test_denom_high_precision_many_small_unstakes --exact --show-output

0 commit comments

Comments
 (0)