Skip to content

Commit f704b6a

Browse files
authored
Merge pull request #1382 from opentensor/feat/burn-owner-uid
Burn miner emission to SNOwner hotkey
2 parents 69e3a43 + 22fa65e commit f704b6a

File tree

6 files changed

+236
-7
lines changed

6 files changed

+236
-7
lines changed

pallets/subtensor/src/coinbase/run_coinbase.rs

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -266,8 +266,6 @@ impl<T: Config> Pallet<T> {
266266
pending_swapped,
267267
owner_cut
268268
);
269-
// Setup.
270-
let zero: I96F32 = asfloat!(0.0);
271269

272270
// Run the epoch.
273271
let hotkey_emission: Vec<(T::AccountId, u64, u64)> =
@@ -297,6 +295,25 @@ impl<T: Config> Pallet<T> {
297295
log::debug!("incentives: {:?}", incentives);
298296
log::debug!("dividends: {:?}", dividends);
299297

298+
Self::distribute_dividends_and_incentives(
299+
netuid,
300+
pending_tao,
301+
owner_cut,
302+
incentives,
303+
dividends,
304+
);
305+
}
306+
307+
pub fn distribute_dividends_and_incentives(
308+
netuid: u16,
309+
pending_tao: u64,
310+
owner_cut: u64,
311+
incentives: BTreeMap<T::AccountId, u64>,
312+
dividends: BTreeMap<T::AccountId, I96F32>,
313+
) {
314+
// Setup.
315+
let zero: I96F32 = asfloat!(0.0);
316+
300317
// Accumulate root divs and alpha_divs. For each hotkey we compute their
301318
// local and root dividend proportion based on their alpha_stake/root_stake
302319
let mut total_root_divs: I96F32 = asfloat!(0);
@@ -375,8 +392,19 @@ impl<T: Config> Pallet<T> {
375392

376393
// Distribute mining incentives.
377394
for (hotkey, incentive) in incentives {
378-
// Increase stake for miner.
379395
log::debug!("incentives: hotkey: {:?}", incentive);
396+
397+
if let Ok(owner_hotkey) = SubnetOwnerHotkey::<T>::try_get(netuid) {
398+
if hotkey == owner_hotkey {
399+
log::debug!(
400+
"incentives: hotkey: {:?} is SN owner hotkey, skipping {:?}",
401+
hotkey,
402+
incentive
403+
);
404+
continue; // Skip/burn miner-emission for SN owner hotkey.
405+
}
406+
}
407+
// Increase stake for miner.
380408
Self::increase_stake_for_hotkey_and_coldkey_on_subnet(
381409
&hotkey.clone(),
382410
&Owner::<T>::get(hotkey.clone()),

pallets/subtensor/src/epoch/math.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,33 @@ pub fn inplace_mask_diag(matrix: &mut [Vec<I32F32>]) {
569569
});
570570
}
571571

572+
// Mask out the diagonal of the input matrix in-place, except for the diagonal entry at except_index.
573+
#[allow(dead_code)]
574+
pub fn inplace_mask_diag_except_index(matrix: &mut [Vec<I32F32>], except_index: u16) {
575+
let Some(first_row) = matrix.first() else {
576+
return;
577+
};
578+
if first_row.is_empty() {
579+
return;
580+
}
581+
assert_eq!(matrix.len(), first_row.len());
582+
583+
let diag_at_index = matrix
584+
.get(except_index as usize)
585+
.and_then(|row| row.get(except_index as usize))
586+
.cloned();
587+
588+
inplace_mask_diag(matrix);
589+
590+
matrix.get_mut(except_index as usize).map(|row| {
591+
row.get_mut(except_index as usize).map(|value| {
592+
if let Some(diag_at_index) = diag_at_index {
593+
*value = diag_at_index;
594+
}
595+
})
596+
});
597+
}
598+
572599
// Return a new sparse matrix that replaces masked rows with an empty vector placeholder.
573600
#[allow(dead_code)]
574601
pub fn mask_rows_sparse(
@@ -604,6 +631,29 @@ pub fn mask_diag_sparse(sparse_matrix: &[Vec<(u16, I32F32)>]) -> Vec<Vec<(u16, I
604631
.collect()
605632
}
606633

634+
// Return a new sparse matrix with a masked out diagonal of input sparse matrix,
635+
// except for the diagonal entry at except_index.
636+
#[allow(dead_code)]
637+
pub fn mask_diag_sparse_except_index(
638+
sparse_matrix: &[Vec<(u16, I32F32)>],
639+
except_index: u16,
640+
) -> Vec<Vec<(u16, I32F32)>> {
641+
sparse_matrix
642+
.iter()
643+
.enumerate()
644+
.map(|(i, sparse_row)| {
645+
sparse_row
646+
.iter()
647+
.filter(|(j, _)| {
648+
// Is not a diagonal OR is the diagonal at except_index
649+
i != (*j as usize) || (i == except_index as usize && *j == except_index)
650+
})
651+
.copied()
652+
.collect()
653+
})
654+
.collect()
655+
}
656+
607657
// Remove cells from sparse matrix where the mask function of two vectors is true.
608658
#[allow(dead_code, clippy::indexing_slicing)]
609659
pub fn vec_mask_sparse_matrix(

pallets/subtensor/src/epoch/run_epoch.rs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,9 @@ impl<T: Config> Pallet<T> {
111111
// == Weights ==
112112
// =============
113113

114+
// Get owner uid.
115+
let owner_uid: Option<u16> = Self::get_owner_uid(netuid);
116+
114117
// Access network weights row unnormalized.
115118
let mut weights: Vec<Vec<I32F32>> = Self::get_weights(netuid);
116119
log::trace!("W:\n{:?}\n", &weights);
@@ -119,7 +122,13 @@ impl<T: Config> Pallet<T> {
119122
inplace_mask_rows(&validator_forbids, &mut weights);
120123
log::trace!("W (permit): {:?}", &weights);
121124

122-
// Remove self-weight by masking diagonal.
125+
// Remove self-weight by masking diagonal; keep owner_uid self-weight.
126+
if let Some(owner_uid) = owner_uid {
127+
inplace_mask_diag_except_index(&mut weights, owner_uid);
128+
} else {
129+
inplace_mask_diag(&mut weights);
130+
}
131+
123132
inplace_mask_diag(&mut weights);
124133
log::trace!("W (permit+diag):\n{:?}\n", &weights);
125134

@@ -454,6 +463,8 @@ impl<T: Config> Pallet<T> {
454463
// == Weights ==
455464
// =============
456465

466+
let owner_uid: Option<u16> = Self::get_owner_uid(netuid);
467+
457468
// Access network weights row unnormalized.
458469
let mut weights: Vec<Vec<(u16, I32F32)>> = Self::get_weights_sparse(netuid);
459470
log::trace!("Weights: {:?}", &weights);
@@ -462,8 +473,12 @@ impl<T: Config> Pallet<T> {
462473
weights = mask_rows_sparse(&validator_forbids, &weights);
463474
log::trace!("Weights (permit): {:?}", &weights);
464475

465-
// Remove self-weight by masking diagonal.
466-
weights = mask_diag_sparse(&weights);
476+
// Remove self-weight by masking diagonal; keep owner_uid self-weight.
477+
if let Some(owner_uid) = owner_uid {
478+
weights = mask_diag_sparse_except_index(&weights, owner_uid);
479+
} else {
480+
weights = mask_diag_sparse(&weights);
481+
}
467482
log::trace!("Weights (permit+diag): {:?}", &weights);
468483

469484
// Remove weights referring to deregistered neurons.

pallets/subtensor/src/tests/coinbase.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
use super::mock::*;
33

44
use crate::*;
5+
use alloc::collections::BTreeMap;
56
use approx::assert_abs_diff_eq;
67
use frame_support::assert_ok;
78
use sp_core::U256;
@@ -1439,3 +1440,51 @@ fn test_get_root_children_drain_with_half_take() {
14391440
// );
14401441
// });
14411442
// }
1443+
1444+
// SKIP_WASM_BUILD=1 RUST_LOG=debug cargo test --package pallet-subtensor --lib -- tests::coinbase::test_incentive_to_subnet_owner_is_burned --exact --show-output --nocapture
1445+
#[test]
1446+
fn test_incentive_to_subnet_owner_is_burned() {
1447+
new_test_ext(1).execute_with(|| {
1448+
let subnet_owner_ck = U256::from(0);
1449+
let subnet_owner_hk = U256::from(1);
1450+
1451+
let other_ck = U256::from(2);
1452+
let other_hk = U256::from(3);
1453+
1454+
let netuid = add_dynamic_network(&subnet_owner_hk, &subnet_owner_ck);
1455+
1456+
let pending_tao: u64 = 1_000_000_000;
1457+
let owner_cut: u64 = 0;
1458+
let mut incentives: BTreeMap<U256, u64> = BTreeMap::new();
1459+
let mut dividends: BTreeMap<U256, I96F32> = BTreeMap::new();
1460+
1461+
// Give incentive to other_hk
1462+
incentives.insert(other_hk, 10_000_000);
1463+
1464+
// Give incentives to subnet_owner_hk
1465+
incentives.insert(subnet_owner_hk, 10_000_000);
1466+
1467+
// Verify stake before
1468+
let subnet_owner_stake_before =
1469+
SubtensorModule::get_stake_for_hotkey_on_subnet(&subnet_owner_hk, netuid);
1470+
assert_eq!(subnet_owner_stake_before, 0);
1471+
let other_stake_before = SubtensorModule::get_stake_for_hotkey_on_subnet(&other_hk, netuid);
1472+
assert_eq!(other_stake_before, 0);
1473+
1474+
// Distribute dividends and incentives
1475+
SubtensorModule::distribute_dividends_and_incentives(
1476+
netuid,
1477+
pending_tao,
1478+
owner_cut,
1479+
incentives,
1480+
dividends,
1481+
);
1482+
1483+
// Verify stake after
1484+
let subnet_owner_stake_after =
1485+
SubtensorModule::get_stake_for_hotkey_on_subnet(&subnet_owner_hk, netuid);
1486+
assert_eq!(subnet_owner_stake_after, 0);
1487+
let other_stake_after = SubtensorModule::get_stake_for_hotkey_on_subnet(&other_hk, netuid);
1488+
assert!(other_stake_after > 0);
1489+
});
1490+
}

pallets/subtensor/src/tests/math.rs

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,13 @@ fn assert_sparse_mat_compare(
5858
) {
5959
assert!(ma.len() == mb.len());
6060
for row in 0..ma.len() {
61-
assert!(ma[row].len() == mb[row].len());
61+
assert!(
62+
ma[row].len() == mb[row].len(),
63+
"row: {}, ma: {:?}, mb: {:?}",
64+
row,
65+
ma[row],
66+
mb[row]
67+
);
6268
for j in 0..ma[row].len() {
6369
assert!(ma[row][j].0 == mb[row][j].0); // u16
6470
assert_float_compare(ma[row][j].1, mb[row][j].1, epsilon) // I32F32
@@ -1034,6 +1040,27 @@ fn test_math_inplace_mask_diag() {
10341040
);
10351041
}
10361042

1043+
#[test]
1044+
fn test_math_inplace_mask_diag_except_index() {
1045+
let vector: Vec<f32> = vec![1., 2., 3., 4., 5., 6., 7., 8., 9.];
1046+
let rows = 3;
1047+
1048+
for i in 0..rows {
1049+
let mut target: Vec<f32> = vec![0., 2., 3., 4., 0., 6., 7., 8., 0.];
1050+
let row = i * rows;
1051+
let col = i;
1052+
target[row + col] = vector[row + col];
1053+
1054+
let mut mat = vec_to_mat_fixed(&vector, rows, false);
1055+
inplace_mask_diag_except_index(&mut mat, i as u16);
1056+
assert_mat_compare(
1057+
&mat,
1058+
&vec_to_mat_fixed(&target, rows, false),
1059+
I32F32::from_num(0),
1060+
);
1061+
}
1062+
}
1063+
10371064
#[test]
10381065
fn test_math_mask_rows_sparse() {
10391066
let input: Vec<f32> = vec![1., 2., 3., 4., 5., 6., 7., 8., 9.];
@@ -1105,6 +1132,58 @@ fn test_math_mask_diag_sparse() {
11051132
);
11061133
}
11071134

1135+
#[test]
1136+
fn test_math_mask_diag_sparse_except_index() {
1137+
let rows = 3;
1138+
1139+
let vector: Vec<f32> = vec![1., 2., 3., 4., 5., 6., 7., 8., 9.];
1140+
let mat = vec_to_sparse_mat_fixed(&vector, rows, false);
1141+
1142+
for i in 0..rows {
1143+
let mut target: Vec<f32> = vec![0., 2., 3., 4., 0., 6., 7., 8., 0.];
1144+
let row = i * rows;
1145+
let col = i;
1146+
target[row + col] = vector[row + col];
1147+
1148+
let result = mask_diag_sparse_except_index(&mat, i as u16);
1149+
let target_as_mat = vec_to_sparse_mat_fixed(&target, rows, false);
1150+
1151+
assert_sparse_mat_compare(&result, &target_as_mat, I32F32::from_num(0));
1152+
}
1153+
1154+
let vector: Vec<f32> = vec![1., 0., 0., 0., 5., 0., 0., 0., 9.];
1155+
let mat = vec_to_sparse_mat_fixed(&vector, rows, false);
1156+
1157+
for i in 0..rows {
1158+
let mut target: Vec<f32> = vec![0., 0., 0., 0., 0., 0., 0., 0., 0.];
1159+
let row = i * rows;
1160+
let col = i;
1161+
target[row + col] = vector[row + col];
1162+
1163+
let result = mask_diag_sparse_except_index(&mat, i as u16);
1164+
let target_as_mat = vec_to_sparse_mat_fixed(&target, rows, false);
1165+
assert_eq!(result.len(), target_as_mat.len());
1166+
1167+
assert_sparse_mat_compare(&result, &target_as_mat, I32F32::from_num(0));
1168+
}
1169+
1170+
for i in 0..rows {
1171+
let vector: Vec<f32> = vec![0., 0., 0., 0., 0., 0., 0., 0., 0.];
1172+
let mat = vec_to_sparse_mat_fixed(&vector, rows, false);
1173+
1174+
let mut target: Vec<f32> = vec![0., 0., 0., 0., 0., 0., 0., 0., 0.];
1175+
let row = i * rows;
1176+
let col = i;
1177+
target[row + col] = vector[row + col];
1178+
1179+
let result = mask_diag_sparse_except_index(&mat, i as u16);
1180+
let target_as_mat = vec_to_sparse_mat_fixed(&target, rows, false);
1181+
assert_eq!(result.len(), target_as_mat.len());
1182+
1183+
assert_sparse_mat_compare(&result, &target_as_mat, I32F32::from_num(0));
1184+
}
1185+
}
1186+
11081187
#[test]
11091188
fn test_math_vec_mask_sparse_matrix() {
11101189
let vector: Vec<f32> = vec![1., 2., 3., 4., 5., 6., 7., 8., 9.];

pallets/subtensor/src/utils/misc.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -733,4 +733,12 @@ impl<T: Config> Pallet<T> {
733733
SubnetOwnerHotkey::<T>::insert(netuid, hotkey.clone());
734734
Self::deposit_event(Event::SubnetOwnerHotkeySet(netuid, hotkey.clone()));
735735
}
736+
737+
// Get the uid of the Owner Hotkey for a subnet.
738+
pub fn get_owner_uid(netuid: u16) -> Option<u16> {
739+
match SubnetOwnerHotkey::<T>::try_get(netuid) {
740+
Ok(owner_hotkey) => Uids::<T>::get(netuid, &owner_hotkey),
741+
Err(_) => None,
742+
}
743+
}
736744
}

0 commit comments

Comments
 (0)