use std::time::Instant;
use cosmwasm_std::testing::mock_info;
use cosmwasm_std::{Addr, Api, DepsMut, Env, Storage};
use rand::{distributions::Uniform, rngs::StdRng, seq::SliceRandom, thread_rng, Rng, SeedableRng};
use substrate_fixed::transcendental::{cos, ln, sqrt, PI};
use substrate_fixed::types::{I32F32, I64F64};
use crate::contract::{execute, get_economy};
use crate::epoch::{epoch, get_bonds};
use crate::msg::ExecuteMsg;
use crate::registration::create_work_for_block_number;
use crate::root::{get_subnet_emission_value, set_emission_values};
use crate::staking::{get_total_stake_for_hotkey, increase_stake_on_coldkey_hotkey_account};
use crate::test_helpers::{
add_balance_to_coldkey_account, add_network, instantiate_contract, pow_register_ok_neuron,
run_step_to_block, set_weights, step_block,
};
use crate::tests::block_step::epoch_dense;
use crate::uids::{append_neuron, get_hotkey_for_net_and_uid, get_subnetwork_n};
use crate::utils::{
get_activity_cutoff, get_consensus_for_uid, get_dividends_for_uid, get_emission_for_uid,
get_incentive_for_uid, get_max_allowed_uids, get_max_allowed_validators, get_rank_for_uid,
get_trust_for_uid, get_validator_permit_for_uid, set_activity_cutoff, set_difficulty,
set_max_allowed_uids, set_max_allowed_validators, set_max_registrations_per_block,
set_max_weight_limit, set_min_allowed_weights, set_min_difficulty,
set_target_registrations_per_interval, set_weights_set_rate_limit,
};
pub fn fixed(val: f32) -> I32F32 {
I32F32::from_num(val)
}
pub fn fixed_to_u16(x: I32F32) -> u16 {
x.to_num::<u16>()
}
pub fn fixed_proportion_to_u16(x: I32F32) -> u16 {
fixed_to_u16(x * I32F32::from_num(u16::MAX))
}
// Normalizes (sum to 1 except 0) the input vector directly in-place.
#[allow(dead_code)]
pub fn inplace_normalize(x: &mut Vec<I32F32>) {
let x_sum: I32F32 = x.iter().sum();
if x_sum == I32F32::from_num(0.0 as f32) {
return;
}
for i in 0..x.len() {
x[i] = x[i] / x_sum;
}
}
// Inplace normalize the passed positive integer weights so that they sum to u16 max value.
fn normalize_weights(mut weights: Vec<u16>) -> Vec<u16> {
let sum: u64 = weights.iter().map(|x| *x as u64).sum();
if sum == 0 {
return weights;
}
weights.iter_mut().for_each(|x| {
*x = (*x as u64 * u16::max_value() as u64 / sum) as u16;
});
return weights;
}
// Return as usize an I32F32 ratio of a usize input, avoiding the 0% and 100% extremes.
fn non_extreme_fixed_ratio(ratio: I32F32, total: usize) -> usize {
if total == 0 {
return total;
}
let mut subset: usize = (ratio * I32F32::from_num(total)).to_num::<usize>();
if subset == 0 {
subset = 1;
} else if subset == total {
subset = total - 1;
}
return subset;
}
// Box-Muller Transform converting two uniform random samples to a normal random sample.
fn normal(size: usize, rng: &mut StdRng, dist: &Uniform<u16>) -> Vec<I32F32> {
let max = I32F32::from_num(u16::MAX);
let two = I32F32::from_num(2);
let eps = I32F32::from_num(0.000001);
let pi = I32F32::from_num(PI);
let uniform_u16: Vec<u16> = (0..(2 * size)).map(|_| rng.sample(&dist)).collect();
let uniform: Vec<I32F32> = uniform_u16
.iter()
.map(|&x| I32F32::from_num(x) / max)
.collect();
let mut normal: Vec<I32F32> = vec![I32F32::from_num(0); size as usize];
for i in 0..size {
let u1: I32F32 = uniform[i] + eps;
let u2: I32F32 = uniform[i + size] + eps;
normal[i] = sqrt::<I32F32, I32F32>(-two * ln::<I32F32, I32F32>(u1).expect("")).expect("")
* cos(two * pi * u2);
}
normal
}
// Returns validators and servers uids with either blockwise, regular, or random interleaving.
fn distribute_nodes(
validators_n: usize,
network_n: usize,
interleave: usize,
) -> (Vec<u16>, Vec<u16>) {
let mut validators: Vec<u16> = vec![];
let mut servers: Vec<u16> = vec![];
if interleave == 0 {
// blockwise [validator_block, server_block]
validators = (0..validators_n as u16).collect();
servers = (validators_n as u16..network_n as u16).collect();
} else if interleave == 1 {
// regular interleaving [val, srv, srv, ..., srv, val, srv, srv, ..., srv, val, srv, ..., srv]
(validators, servers) = (0..network_n as u16)
.collect::<Vec<u16>>()
.iter()
.partition(|&i| *i as usize % (network_n / validators_n) == 0);
} else if interleave == 2 {
// random interleaving
let mut permuted_uids: Vec<u16> = (0..network_n as u16).collect();
permuted_uids.shuffle(&mut thread_rng());
validators = permuted_uids[0..validators_n as usize].into();
servers = permuted_uids[validators_n as usize..network_n as usize].into();
}
return (validators, servers);
}
#[allow(dead_code)]
fn uid_stats(store: &dyn Storage, netuid: u16, uid: u16) {
log::info!(
"stake: {:?}",
get_total_stake_for_hotkey(store, &Addr::unchecked((1000 + uid).to_string()))
);
log::info!("rank: {:?}", get_rank_for_uid(store, netuid, uid));
log::info!("trust: {:?}", get_trust_for_uid(store, netuid, uid));
log::info!("consensus: {:?}", get_consensus_for_uid(store, netuid, uid));
log::info!("incentive: {:?}", get_incentive_for_uid(store, netuid, uid));
log::info!("dividend: {:?}", get_dividends_for_uid(store, netuid, uid));
log::info!("emission: {:?}", get_emission_for_uid(store, netuid, uid));
}
fn init_run_epochs(
mut deps: DepsMut,
mut env: &mut Env,
netuid: u16,
n: u16,
validators: &Vec<u16>,
servers: &Vec<u16>,
epochs: u16,
stake_per_validator: u64,
server_self: bool,
input_stake: &Vec<u64>,
use_input_stake: bool,
input_weights: &Vec<Vec<(u16, u16)>>,
use_input_weights: bool,
random_weights: bool,
random_seed: u64,
sparse: bool,
) {
// let (mut deps, mut env) = instantiate_contract();
// === Create the network
add_network(deps.storage, netuid, u16::MAX - 1, 0); // set higher tempo to avoid built-in epoch, then manual epoch instead
// === Register uids
set_max_allowed_uids(deps.storage, netuid, n);
for key in 0..n {
let stake: u64;
if use_input_stake {
stake = input_stake[key as usize];
} else {
stake = if validators.contains(&key) {
stake_per_validator
} else {
0
}; // only validators receive stake
}
// let stake: u64 = 1; // alternative test: all nodes receive stake, should be same outcome, except stake
add_balance_to_coldkey_account(&Addr::unchecked((1000 + key).to_string()), stake);
append_neuron(
deps.storage,
deps.api,
netuid,
&(Addr::unchecked((1000 + key).to_string())),
0,
)
.unwrap();
increase_stake_on_coldkey_hotkey_account(
deps.storage,
&Addr::unchecked((1000 + key).to_string()),
&Addr::unchecked((1000 + key).to_string()),
stake as u64,
);
}
assert_eq!(get_subnetwork_n(deps.storage, netuid), n);
// === Issue validator permits
set_max_allowed_validators(deps.storage, netuid, validators.len() as u16);
assert_eq!(
get_max_allowed_validators(deps.storage, netuid),
validators.len() as u16
);
epoch(
deps.storage,
deps.api,
netuid,
1_000_000_000,
env.block.height,
)
.unwrap(); // run first epoch to set allowed validators
step_block(deps.branch(), &mut env).unwrap(); // run to next block to ensure weights are set on nodes after their registration block
// === Set weights
let mut rng = StdRng::seed_from_u64(random_seed); // constant seed so weights over multiple runs are equal
let range = Uniform::new(0, u16::MAX);
let mut weights: Vec<u16> = vec![u16::MAX / n; servers.len() as usize];
for uid in validators {
if random_weights {
weights = (0..servers.len()).map(|_| rng.sample(&range)).collect();
weights = normalize_weights(weights);
// assert_eq!(weights.iter().map(|x| *x as u64).sum::<u64>(), u16::MAX as u64); // normalized weight sum not always u16::MAX
}
if use_input_weights {
let sparse_weights = input_weights[*uid as usize].clone();
weights = sparse_weights.iter().map(|(_, w)| *w).collect();
let srvs: Vec<u16> = sparse_weights.iter().map(|(s, _)| *s).collect();
let msg = ExecuteMsg::SetWeights {
netuid,
dests: srvs.clone(),
weights: weights.clone(),
version_key: 0,
};
let info = mock_info((1000 + uid).to_string().as_str(), &[]);
let res = execute(deps.branch(), env.clone(), info, msg);
assert_eq!(res.is_ok(), true);
} else {
let msg = ExecuteMsg::SetWeights {
netuid,
dests: servers.clone(),
weights: weights.clone(),
version_key: 0,
};
let info = mock_info((1000 + uid).to_string().as_str(), &[]);
let res = execute(deps.branch(), env.clone(), info, msg);
assert_eq!(res.is_ok(), true);
}
}
for uid in servers {
if server_self {
let msg = ExecuteMsg::SetWeights {
netuid,
dests: vec![*uid as u16],
weights: vec![u16::MAX],
version_key: 0,
}; // server self-weight
let info = mock_info((1000 + uid).to_string().as_str(), &[]);
let res = execute(deps.branch(), env.clone(), info, msg);
assert_eq!(res.is_ok(), true);
}
}
// === Run the epochs.
log::info!("Start {epochs} epoch(s)");
let start = Instant::now();
for _ in 0..epochs {
if sparse {
epoch(
deps.storage,
deps.api,
netuid,
1_000_000_000,
env.block.height,
)
.unwrap();
} else {
epoch_dense(deps.storage, netuid, 1_000_000_000, env.block.height);
}
}
let duration = start.elapsed();
log::info!(
"Time elapsed in (sparse={sparse}) epoch() is: {:?}",
duration
);
// let bonds = get_bonds(&deps.storage, netuid );
// for (uid, node) in vec![ (validators[0], "validator"), (servers[0], "server") ] {
// log::info!("\n{node}" );
// uid_stats(netuid, uid);
// log::info!("bonds: {:?} (on validator), {:?} (on server)", bonds[uid as usize][0], bonds[uid as usize][servers[0] as usize]);
// }
}
// Generate a random graph that is split into a major and minor set, each setting specific weight on itself and the complement on the other.
fn split_graph(
major_stake: I32F32,
major_weight: I32F32,
minor_weight: I32F32,
weight_stddev: I32F32,
validators_n: usize,
network_n: usize,
interleave: usize,
) -> (
Vec<u16>,
Vec<u16>,
Vec<u16>,
Vec<u16>,
Vec<u16>,
Vec<u16>,
Vec<u64>,
Vec<Vec<(u16, u16)>>,
I32F32,
) {
let servers_n: usize = network_n - validators_n;
let major_servers_n: usize = non_extreme_fixed_ratio(major_stake, servers_n);
let major_validators_n: usize = non_extreme_fixed_ratio(major_stake, validators_n);
let (validators, servers) = distribute_nodes(validators_n, network_n, interleave as usize);
let major_validators: Vec<u16> = (0..major_validators_n).map(|i| validators[i]).collect();
let minor_validators: Vec<u16> = (major_validators_n..validators_n)
.map(|i| validators[i])
.collect();
let major_servers: Vec<u16> = (0..major_servers_n).map(|i| servers[i]).collect();
let minor_servers: Vec<u16> = (major_servers_n..servers_n).map(|i| servers[i]).collect();
let zero: I32F32 = I32F32::from_num(0);
let one: I32F32 = I32F32::from_num(1);
let stddev: I32F32 = I32F32::from_num(0.3);
let total_stake: I64F64 = I64F64::from_num(21_000_000_000_000_000 as u64);
let mut rng = StdRng::seed_from_u64(0); // constant seed so weights over multiple runs are equal
let dist = Uniform::new(0, u16::MAX);
let mut stake: Vec<u64> = vec![0; network_n];
let mut stake_fixed: Vec<I32F32> = vec![zero; network_n];
for (ratio, vals) in vec![
(major_stake, &major_validators),
(one - major_stake, &minor_validators),
] {
let mut sample = normal(vals.len(), &mut rng, &dist)
.iter()
.map(|x: &I32F32| {
let v: I32F32 = (stddev * x) + one;
if v < zero {
zero
} else {
v
}
})
.collect();
inplace_normalize(&mut sample);
for (i, &val) in vals.iter().enumerate() {
stake[val as usize] =
(I64F64::from_num(ratio) * I64F64::from_num(sample[i]) * total_stake)
.to_num::<u64>();
stake_fixed[val as usize] =
I32F32::from_num(I64F64::from_num(ratio) * I64F64::from_num(sample[i]));
}
}
let mut weights: Vec<Vec<(u16, u16)>> = vec![vec![]; network_n as usize];
let mut weights_fixed: Vec<Vec<I32F32>> = vec![vec![zero; network_n]; network_n];
for (first, second, vals) in vec![
(major_weight, one - major_weight, &major_validators),
(one - minor_weight, minor_weight, &minor_validators),
] {
for &val in vals {
for (weight, srvs) in vec![(first, &major_servers), (second, &minor_servers)] {
let mut sample: Vec<I32F32> = normal(srvs.len(), &mut rng, &dist)
.iter()
.map(|x: &I32F32| {
let v: I32F32 = (weight_stddev * x) + one;
if v < zero {
zero
} else {
v
}
})
.collect();
inplace_normalize(&mut sample);
for (i, &srv) in srvs.iter().enumerate() {
weights[val as usize].push((srv, fixed_proportion_to_u16(weight * sample[i])));
weights_fixed[val as usize][srv as usize] = weight * sample[i];
}
}
inplace_normalize(&mut weights_fixed[val as usize]);
}
}
inplace_normalize(&mut stake_fixed);
// Calculate stake-weighted mean per server
let mut weight_mean: Vec<I32F32> = vec![zero; network_n];
for val in 0..network_n {
if stake_fixed[val] > zero {
for srv in 0..network_n {
weight_mean[srv] += stake_fixed[val] * weights_fixed[val][srv];
}
}
}
// Calculate stake-weighted absolute standard deviation
let mut weight_dev: Vec<I32F32> = vec![zero; network_n];
for val in 0..network_n {
if stake_fixed[val] > zero {
for srv in 0..network_n {
weight_dev[srv] +=
stake_fixed[val] * (weight_mean[srv] - weights_fixed[val][srv]).abs();
}
}
}
// Calculate rank-weighted mean of weight_dev
let avg_weight_dev: I32F32 =
weight_dev.iter().sum::<I32F32>() / weight_mean.iter().sum::<I32F32>();
(
validators,
servers,
major_validators,
minor_validators,
major_servers,
minor_servers,
stake,
weights,
avg_weight_dev,
)
}
// Test consensus guarantees with an epoch on a graph with 4096 nodes, of which the first 128 are validators, the graph is split into a major and minor set, each setting specific weight on itself and the complement on the other. Asserts that the major emission ratio >= major stake ratio.
// #[test]
// fn test_consensus_guarantees() {
// let netuid: u16 = 0;
// let network_n: u16 = 512;
// let validators_n: u16 = 64;
// let epochs: u16 = 1;
// let interleave = 2;
// log::info!("test_consensus_guarantees ({network_n:?}, {validators_n:?} validators)");
// for (major_stake, major_weight, minor_weight, weight_stddev) in vec![
// (0.51, 1., 1., 0.001),
// (0.51, 0.03, 0., 0.001),
// (0.51, 0.51, 0.49, 0.001),
// (0.51, 0.51, 1., 0.001),
// (0.51, 0.61, 0.8, 0.1),
// (0.6, 0.67, 0.65, 0.2),
// (0.6, 0.74, 0.77, 0.4),
// (0.6, 0.76, 0.8, 0.4),
// (0.6, 0.76, 1., 0.4),
// (0.6, 0.92, 1., 0.4),
// (0.6, 0.94, 1., 0.4),
// (0.65, 0.78, 0.85, 0.6),
// (0.7, 0.81, 0.85, 0.8),
// (0.7, 0.83, 0.85, 1.),
// ] {
// let (
// validators,
// servers,
// major_validators,
// minor_validators,
// major_servers,
// minor_servers,
// stake,
// weights,
// _avg_weight_dev,
// ) = split_graph(
// fixed(major_stake),
// fixed(major_weight),
// fixed(minor_weight),
// fixed(weight_stddev),
// validators_n as usize,
// network_n as usize,
// interleave as usize,
// );
// new_test_ext().execute_with(|| {
// init_run_epochs(
// netuid,
// network_n,
// &validators,
// &servers,
// epochs,
// 1,
// true,
// &stake,
// true,
// &weights,
// true,
// false,
// 0,
// false,
// );
// let mut major_emission: I64F64 = I64F64::from_num(0);
// let mut minor_emission: I64F64 = I64F64::from_num(0);
// for set in vec![major_validators, major_servers] {
// for uid in set {
// major_emission +=
// I64F64::from_num(get_emission_for_uid(netuid, uid));
// }
// }
// for set in vec![minor_validators, minor_servers] {
// for uid in set {
// minor_emission +=
// I64F64::from_num(get_emission_for_uid(netuid, uid));
// }
// }
// let major_ratio: I32F32 =
// I32F32::from_num(major_emission / (major_emission + minor_emission));
// assert!(major_stake <= major_ratio);
// });
// }
// }
// Test an epoch on an empty graph.
// #[test]
// fn test_overflow() {
// new_test_ext().execute_with(|| {
// log::info!("test_overflow:");
// let netuid: u16 = 1;
// add_network(netuid, 0, 0);
// set_max_allowed_uids(netuid, 3);
// increase_stake_on_coldkey_hotkey_account(
// Addr::unchecked(1000.to_string()),
// Addr::unchecked(1000.to_string()),
// 10,
// );
// increase_stake_on_coldkey_hotkey_account(
// Addr::unchecked(1001.to_string()),
// Addr::unchecked(1001.to_string()),
// 10,
// );
// increase_stake_on_coldkey_hotkey_account(
// Addr::unchecked(1002.to_string()),
// Addr::unchecked(1002.to_string()),
// 10,
// );
// append_neuron(netuid, Addr::unchecked(1000.to_string()), 0);
// append_neuron(netuid, Addr::unchecked(1001.to_string()), 0);
// append_neuron(netuid, Addr::unchecked(1002.to_string()), 0);
// set_validator_permit_for_uid(0, 0, true);
// set_validator_permit_for_uid(0, 1, true);
// set_validator_permit_for_uid(0, 2, true);
// assert_ok!(set_weights(
// Addr::unchecked(1000.to_string())),
// netuid,
// vec![0, 1, 2],
// vec![u16::MAX / 3, u16::MAX / 3, u16::MAX],
// 0
// ));
// assert_ok!(set_weights(
// Addr::unchecked(1001.to_string())),
// netuid,
// vec![1, 2],
// vec![u16::MAX / 2, u16::MAX / 2],
// 0
// ));
// assert_ok!(set_weights(
// Addr::unchecked(1002.to_string())),
// netuid,
// vec![2],
// vec![u16::MAX],
// 0
// ));
// epoch(0, u64::MAX);
// });
// }
// Test an epoch on an empty graph.
// #[test]
// fn test_nill_epoch_subtensor() {
// new_test_ext().execute_with(|| {
// log::info!("test_nill_epoch:");
// epoch(0, 0);
// });
// }
// Test an epoch on a graph with a single item.
#[test]
fn test_1_graph() {
let (mut deps, mut env) = instantiate_contract();
log::info!("test_1_graph:");
let netuid: u16 = 2;
let coldkey = "addr0";
let hotkey = "addr0";
let uid: u16 = 0;
let stake_amount: u64 = 1;
add_network(&mut deps.storage, netuid, u16::MAX - 1, 0); // set higher tempo to avoid built-in epoch, then manual epoch instead
set_max_allowed_uids(&mut deps.storage, netuid, 1);
add_balance_to_coldkey_account(&Addr::unchecked(hotkey), stake_amount);
append_neuron(
&mut deps.storage,
&deps.api,
netuid,
&Addr::unchecked(hotkey),
0,
)
.unwrap();
increase_stake_on_coldkey_hotkey_account(
&mut deps.storage,
&Addr::unchecked(coldkey),
&Addr::unchecked(hotkey),
stake_amount,
);
assert_eq!(get_subnetwork_n(&deps.storage, netuid), 1);
// run_to_block(1);
step_block(deps.as_mut(), &mut env).unwrap(); // run to next block to ensure weights are set on nodes after their registration block
set_weights(
deps.as_mut(),
env.clone(),
hotkey,
netuid,
vec![uid as u16],
vec![u16::MAX],
0,
)
.unwrap();
// set_weights_for_testing( netuid, i as u16, vec![ ( 0, u16::MAX )]); // doesn't set update status
// set_bonds_for_testing( netuid, uid, vec![ ( 0, u16::MAX )]); // rather, bonds are calculated in epoch
set_emission_values(
&mut deps.storage,
&deps.api,
&vec![netuid],
vec![1_000_000_000],
)
.unwrap();
assert_eq!(
get_subnet_emission_value(&deps.storage, netuid),
1_000_000_000
);
epoch(
&mut deps.storage,
&deps.api,
netuid,
1_000_000_000,
env.block.height,
)
.unwrap();
assert_eq!(
get_total_stake_for_hotkey(&deps.storage, &Addr::unchecked(hotkey)),
stake_amount
);
assert_eq!(get_rank_for_uid(&deps.storage, netuid, uid), 0);
assert_eq!(get_trust_for_uid(&deps.storage, netuid, uid), 0);
assert_eq!(get_consensus_for_uid(&deps.storage, netuid, uid), 0);
assert_eq!(get_incentive_for_uid(&deps.storage, netuid, uid), 0);
assert_eq!(get_dividends_for_uid(&deps.storage, netuid, uid), 0);
assert_eq!(
get_emission_for_uid(&deps.storage, netuid, uid),
1_000_000_000
);
}
// Test an epoch on a graph with two items.
#[test]
fn test_10_graph() {
let (mut deps, mut env) = instantiate_contract();
log::info!("test_10_graph");
// Function for adding a nodes to the graph.
pub fn add_node(
store: &mut dyn Storage,
api: &dyn Api,
netuid: u16,
coldkey: Addr,
hotkey: Addr,
uid: u16,
stake_amount: u64,
) {
log::info!(
"+Add net:{:?} coldkey:{:?} hotkey:{:?} uid:{:?} stake_amount: {:?} subn: {:?}",
netuid,
coldkey,
hotkey,
uid,
stake_amount,
get_subnetwork_n(store, netuid),
);
append_neuron(store, api, netuid, &hotkey, 1).unwrap();
increase_stake_on_coldkey_hotkey_account(store, &coldkey, &hotkey, stake_amount);
assert_eq!(get_subnetwork_n(store, netuid) - 1, uid);
}
// Build the graph with 10 items
// each with 1 stake and self weights.
let n: usize = 10;
let netuid: u16 = 2;
add_network(&mut deps.storage, netuid, u16::MAX - 1, 0); // set higher tempo to avoid built-in epoch, then manual epoch instead
set_max_allowed_uids(&mut deps.storage, netuid, n as u16);
set_weights_set_rate_limit(&mut deps.storage, netuid, 0);
for i in 0..10 {
add_node(
&mut deps.storage,
&deps.api,
netuid,
Addr::unchecked(i.to_string()),
Addr::unchecked(i.to_string()),
i as u16,
1,
);
let gas = deps.storage.gas_used.borrow();
println!(
"total {:?} gas {:?} write {:?} read {:?}",
gas.total, gas.last, gas.write_cnt, gas.read_cnt
);
drop(gas);
}
assert_eq!(get_subnetwork_n(&deps.storage, netuid), 10);
env.block.height += 1; // run to next block to ensure weights are set on nodes after their registration block
for i in 0..10 {
let msg = ExecuteMsg::SetWeights {
netuid,
dests: vec![i as u16],
weights: vec![u16::MAX],
version_key: 0,
}; // server self-weight
let info = mock_info(i.to_string().as_str(), &[]);
let res = execute(deps.as_mut(), env.clone(), info, msg);
// println!("{:?} {:?}",i, res);
assert_eq!(res.is_ok(), true);
let gas = deps.storage.gas_used.borrow();
println!(
"total {:?} gas {:?} write {:?} read {:?}",
gas.total, gas.last, gas.write_cnt, gas.read_cnt
);
drop(gas);
}
// Run the epoch.
epoch(
&mut deps.storage,
&deps.api,
netuid,
1_000_000_000,
env.block.height,
)
.unwrap();
let gas = deps.storage.gas_used.borrow();
println!(
"total {:?} gas {:?} write {:?} read {:?}",
gas.total, gas.last, gas.write_cnt, gas.read_cnt
);
drop(gas);
// Check return values.
for i in 0..n {
assert_eq!(
get_total_stake_for_hotkey(&deps.storage, &Addr::unchecked(i.to_string())),
1
);
assert_eq!(get_rank_for_uid(&deps.storage, netuid, i as u16), 0);
assert_eq!(get_trust_for_uid(&deps.storage, netuid, i as u16), 0);
assert_eq!(get_consensus_for_uid(&deps.storage, netuid, i as u16), 0);
assert_eq!(get_incentive_for_uid(&deps.storage, netuid, i as u16), 0);
assert_eq!(get_dividends_for_uid(&deps.storage, netuid, i as u16), 0);
assert_eq!(
get_emission_for_uid(&deps.storage, netuid, i as u16),
99999999
);
}
}
// Test an epoch on a graph with 512 nodes, of which the first 64 are validators setting non-self weights, and the rest servers setting only self-weights.
#[test]
fn test_512_graph() {
let netuid: u16 = 2;
let network_n: u16 = 512;
let validators_n: u16 = 64;
let max_stake_per_validator: u64 = 328_125_000_000_000; // 21_000_000_000_000_000 / 64
let epochs: u16 = 3;
log::info!("test_{network_n:?}_graph ({validators_n:?} validators)");
for interleave in 0..3 {
for server_self in vec![false, true] {
// server-self weight off/on
let (validators, servers) = distribute_nodes(
validators_n as usize,
network_n as usize,
interleave as usize,
);
let server: usize = servers[0] as usize;
let validator: usize = validators[0] as usize;
let (mut deps, mut env) = instantiate_contract();
init_run_epochs(
deps.as_mut(),
&mut env,
netuid,
network_n,
&validators,
&servers,
epochs,
max_stake_per_validator,
server_self,
&vec![],
false,
&vec![],
false,
false,
0,
false,
);
let bonds = get_bonds(&deps.storage, netuid);
for uid in validators {
assert_eq!(
get_total_stake_for_hotkey(
&deps.storage,
&Addr::unchecked((1000 + uid).to_string())
),
max_stake_per_validator
);
assert_eq!(get_rank_for_uid(&deps.storage, netuid, uid), 0);
assert_eq!(get_trust_for_uid(&deps.storage, netuid, uid), 0);
assert_eq!(get_consensus_for_uid(&deps.storage, netuid, uid), 0);
assert_eq!(get_incentive_for_uid(&deps.storage, netuid, uid), 0);
assert_eq!(get_dividends_for_uid(&deps.storage, netuid, uid), 1023); // Note D = floor(1 / 64 * 65_535) = 1023
assert_eq!(get_emission_for_uid(&deps.storage, netuid, uid), 7812500); // Note E = 0.5 / 200 * 1_000_000_000 = 7_812_500
assert_eq!(bonds[uid as usize][validator], 0.0);
assert_eq!(bonds[uid as usize][server], I32F32::from_num(65_535));
// Note B_ij = floor(1 / 64 * 65_535) / 65_535 = 1023 / 65_535, then max-upscaled to 65_535
}
for uid in servers {
assert_eq!(
get_total_stake_for_hotkey(
&deps.storage,
&Addr::unchecked((1000 + uid).to_string())
),
0
);
assert_eq!(get_rank_for_uid(&deps.storage, netuid, uid), 146); // Note R = floor(1 / (512 - 64) * 65_535) = 146
assert_eq!(get_trust_for_uid(&deps.storage, netuid, uid), 65535);
assert_eq!(get_consensus_for_uid(&deps.storage, netuid, uid), 146); // Note C = floor(1 / (512 - 64) * 65_535) = 146
assert_eq!(get_incentive_for_uid(&deps.storage, netuid, uid), 146); // Note I = floor(1 / (512 - 64) * 65_535) = 146
assert_eq!(get_dividends_for_uid(&deps.storage, netuid, uid), 0);
assert_eq!(get_emission_for_uid(&deps.storage, netuid, uid), 1116071); // Note E = floor(0.5 / (512 - 64) * 1_000_000_000) = 1_116_071
assert_eq!(bonds[uid as usize][validator], 0.0);
assert_eq!(bonds[uid as usize][server], 0.0);
}
drop(deps);
drop(env);
}
}
}
// Test an epoch on a graph with 512 nodes, of which the first 64 are validators setting random non-self weights, and the rest servers setting only self-weights.
#[test]
fn test_512_graph_random_weights() {
let netuid: u16 = 2;
let network_n: u16 = 512;
let validators_n: u16 = 64;
let epochs: u16 = 1;
log::info!("test_{network_n:?}_graph_random_weights ({validators_n:?} validators)");
for interleave in 0..3 {
for server_self in vec![false, true] {
// server-self weight off/on
let (validators, servers) = distribute_nodes(
validators_n as usize,
network_n as usize,
interleave as usize,
);
let server: usize = servers[0] as usize;
let validator: usize = validators[0] as usize;
let (mut rank, mut incentive, mut dividend, mut emission, mut bondv, mut bonds): (
Vec<u16>,
Vec<u16>,
Vec<u16>,
Vec<u64>,
Vec<I32F32>,
Vec<I32F32>,
) = (vec![], vec![], vec![], vec![], vec![], vec![]);
// Dense epoch
let (mut deps, mut env) = instantiate_contract();
init_run_epochs(
deps.as_mut(),
&mut env,
netuid,
network_n,
&validators,
&servers,
epochs,
1,
server_self,
&vec![],
false,
&vec![],
false,
true,
interleave as u64,
false,
);
let bond = get_bonds(&deps.storage, netuid);
for uid in 0..network_n {
rank.push(get_rank_for_uid(&deps.storage, netuid, uid));
incentive.push(get_incentive_for_uid(&deps.storage, netuid, uid));
dividend.push(get_dividends_for_uid(&deps.storage, netuid, uid));
emission.push(get_emission_for_uid(&deps.storage, netuid, uid));
bondv.push(bond[uid as usize][validator]);
bonds.push(bond[uid as usize][server]);
}
drop(deps);
drop(env);
// Sparse epoch (same random seed as dense)
let (mut deps, mut env) = instantiate_contract();
init_run_epochs(
deps.as_mut(),
&mut env,
netuid,
network_n,
&validators,
&servers,
epochs,
1,
server_self,
&vec![],
false,
&vec![],
false,
true,
interleave as u64,
true,
);
// Assert that dense and sparse epoch results are equal
let bond = get_bonds(&deps.storage, netuid);
for uid in 0..network_n {
assert_eq!(
get_rank_for_uid(&deps.storage, netuid, uid),
rank[uid as usize]
);
assert_eq!(
get_incentive_for_uid(&deps.storage, netuid, uid),
incentive[uid as usize]
);
assert_eq!(
get_dividends_for_uid(&deps.storage, netuid, uid),
dividend[uid as usize]
);
assert_eq!(
get_emission_for_uid(&deps.storage, netuid, uid),
emission[uid as usize]
);
assert_eq!(bond[uid as usize][validator], bondv[uid as usize]);
assert_eq!(bond[uid as usize][server], bonds[uid as usize]);
}
drop(deps);
drop(env);
}
}
}
// // Test an epoch on a graph with 4096 nodes, of which the first 256 are validators setting non-self weights, and the rest servers setting only self-weights.
// TODO revisit because commented also in subtensor repo and tests fails
// #[test]
#[allow(dead_code)]
fn test_4096_graph() {
let netuid: u16 = 2;
let network_n: u16 = 4096;
let validators_n: u16 = 256;
let epochs: u16 = 2;
let max_stake_per_validator: u64 = 82_031_250_000_000;
// 21_000_000_000_000_000 / 256
log::info!("test_{network_n:?}_graph ({validators_n:?} validators)");
for interleave in 0..3 {
let (validators, servers) = distribute_nodes(
validators_n as usize,
network_n as usize,
interleave as usize,
);
let server: usize = servers[0] as usize;
let validator: usize = validators[0] as usize;
for server_self in vec![false, true] {
// server-self weight off/on
let (mut deps, mut env) = instantiate_contract();
init_run_epochs(
deps.as_mut(),
&mut env,
netuid,
network_n,
&validators,
&servers,
epochs,
max_stake_per_validator,
server_self,
&vec![],
false,
&vec![],
false,
false,
0,
true,
);
// Because of genesis init
// assert_eq!(get_total_stake(&deps.storage), 21_000_000_000_000_300);
// assert_eq!(get_total_stake(&deps.storage), 21_000_000_000_000_000);
let bonds = get_bonds(&deps.storage, netuid);
for uid in &validators {
assert_eq!(
get_total_stake_for_hotkey(
&deps.storage,
&Addr::unchecked((*uid as u64).to_string())
),
max_stake_per_validator
);
assert_eq!(get_rank_for_uid(&deps.storage, netuid, *uid), 0);
assert_eq!(get_trust_for_uid(&deps.storage, netuid, *uid), 0);
assert_eq!(get_consensus_for_uid(&deps.storage, netuid, *uid), 0);
assert_eq!(get_incentive_for_uid(&deps.storage, netuid, *uid), 0);
assert_eq!(get_dividends_for_uid(&deps.storage, netuid, *uid), 255); // Note D = floor(1 / 256 * 65_535)
assert_eq!(get_emission_for_uid(&deps.storage, netuid, *uid), 1953125); // Note E = 0.5 / 256 * 1_000_000_000 = 1953125
assert_eq!(bonds[*uid as usize][validator], 0.0);
assert_eq!(
bonds[*uid as usize][server],
I32F32::from_num(255) / I32F32::from_num(65_535)
); // Note B_ij = floor(1 / 256 * 65_535) / 65_535
}
for uid in &servers {
assert_eq!(
get_total_stake_for_hotkey(
&deps.storage,
&Addr::unchecked((*uid as u64).to_string())
),
0
);
assert_eq!(get_rank_for_uid(&deps.storage, netuid, *uid), 17); // Note R = floor(1 / (4096 - 256) * 65_535) = 17
assert_eq!(get_trust_for_uid(&deps.storage, netuid, *uid), 65535);
assert_eq!(get_consensus_for_uid(&deps.storage, netuid, *uid), 17); // Note C = floor(1 / (4096 - 256) * 65_535) = 17
assert_eq!(get_incentive_for_uid(&deps.storage, netuid, *uid), 17); // Note I = floor(1 / (4096 - 256) * 65_535) = 17
assert_eq!(get_dividends_for_uid(&deps.storage, netuid, *uid), 0);
assert_eq!(get_emission_for_uid(&deps.storage, netuid, *uid), 130208); // Note E = floor(0.5 / (4096 - 256) * 1_000_000_000) = 130208
assert_eq!(bonds[*uid as usize][validator], 0.0);
assert_eq!(bonds[*uid as usize][server], 0.0);
}
drop(deps);
drop(env);
}
}
}
// // Test an epoch_sparse on a graph with 16384 nodes, of which the first 512 are validators setting non-self weights, and the rest servers setting only self-weights.
// TODO revisit because commented also in subtensor repo and tests fails
// #[test]
#[allow(dead_code)]
fn test_16384_graph_sparse() {
let (mut deps, mut env) = instantiate_contract();
let netuid: u16 = 2;
let n: u16 = 16384;
let validators_n: u16 = 512;
let validators: Vec<u16> = (0..validators_n).collect();
let servers: Vec<u16> = (validators_n..n).collect();
let server: u16 = servers[0];
let epochs: u16 = 1;
log::info!("test_{n:?}_graph ({validators_n:?} validators)");
init_run_epochs(
deps.as_mut(),
&mut env,
netuid,
n,
&validators,
&servers,
epochs,
1,
false,
&vec![],
false,
&vec![],
false,
false,
0,
true,
);
let bonds = get_bonds(&deps.storage, netuid);
for uid in validators {
assert_eq!(
get_total_stake_for_hotkey(&deps.storage, &Addr::unchecked((1000 + uid).to_string())),
1
);
assert_eq!(get_rank_for_uid(&deps.storage, netuid, uid), 0);
assert_eq!(get_trust_for_uid(&deps.storage, netuid, uid), 0);
assert_eq!(get_consensus_for_uid(&deps.storage, netuid, uid), 438); // Note C = 0.0066928507 = (0.0066928507*65_535) = floor( 438.6159706245 )
assert_eq!(get_incentive_for_uid(&deps.storage, netuid, uid), 0);
assert_eq!(get_dividends_for_uid(&deps.storage, netuid, uid), 127); // Note D = floor(1 / 512 * 65_535) = 127
assert_eq!(get_emission_for_uid(&deps.storage, netuid, uid), 976085); // Note E = 0.5 / 512 * 1_000_000_000 = 976_562 (discrepancy)
assert_eq!(bonds[uid as usize][0], 0.0);
assert_eq!(
bonds[uid as usize][server as usize],
I32F32::from_num(127) / I32F32::from_num(65_535)
); // Note B_ij = floor(1 / 512 * 65_535) / 65_535 = 127 / 65_535
}
for uid in servers {
assert_eq!(
get_total_stake_for_hotkey(&deps.storage, &Addr::unchecked((1000 + uid).to_string())),
0
);
assert_eq!(get_rank_for_uid(&deps.storage, netuid, uid), 4); // Note R = floor(1 / (16384 - 512) * 65_535) = 4
assert_eq!(get_trust_for_uid(&deps.storage, netuid, uid), 65535);
assert_eq!(get_consensus_for_uid(&deps.storage, netuid, uid), 4); // Note C = floor(1 / (16384 - 512) * 65_535) = 4
assert_eq!(get_incentive_for_uid(&deps.storage, netuid, uid), 4); // Note I = floor(1 / (16384 - 512) * 65_535) = 4
assert_eq!(get_dividends_for_uid(&deps.storage, netuid, uid), 0);
assert_eq!(get_emission_for_uid(&deps.storage, netuid, uid), 31517); // Note E = floor(0.5 / (16384 - 512) * 1_000_000_000) = 31502 (discrepancy)
assert_eq!(bonds[uid as usize][0], 0.0);
assert_eq!(bonds[uid as usize][server as usize], 0.0);
}
}
// // Test bonds exponential moving average over a sequence of epochs.
#[test]
fn test_bonds() {
let (mut deps, mut env) = instantiate_contract();
let sparse: bool = true;
let n: u16 = 8;
let netuid: u16 = 2;
let tempo: u16 = u16::MAX - 1; // high tempo to skip automatic epochs in on_initialize, use manual epochs instead
let block_number: u64 = 0;
let max_stake: u64 = 4;
let stakes: Vec<u64> = vec![1, 2, 3, 4, 0, 0, 0, 0];
add_network(&mut deps.storage, netuid, tempo, 0);
set_max_allowed_uids(&mut deps.storage, netuid, n);
assert_eq!(get_max_allowed_uids(&deps.storage, netuid), n);
set_max_registrations_per_block(&mut deps.storage, netuid, n);
set_target_registrations_per_interval(&mut deps.storage, netuid, n);
set_weights_set_rate_limit(&mut deps.storage, netuid, 0);
set_min_allowed_weights(&mut deps.storage, netuid, 1);
set_max_weight_limit(&mut deps.storage, netuid, u16::MAX);
set_min_difficulty(&mut deps.storage, netuid, 1_000);
set_difficulty(&mut deps.storage, netuid, 1_000);
// === Register [validator1, validator2, validator3, validator4, server1, server2, server3, server4]
for key in 0..n as u64 {
add_balance_to_coldkey_account(&Addr::unchecked((1000 + key).to_string()), max_stake);
let (nonce, work): (u64, Vec<u8>) = create_work_for_block_number(
&deps.storage,
netuid,
block_number,
key * 1_000_000,
Addr::unchecked((1000 + key).to_string()).as_str(),
);
pow_register_ok_neuron(
deps.as_mut(),
env.clone(),
netuid,
block_number,
nonce,
work,
Addr::unchecked((1000 + key).to_string()).as_str(),
Addr::unchecked((1000 + key).to_string()).as_str(),
)
.unwrap();
increase_stake_on_coldkey_hotkey_account(
&mut deps.storage,
&Addr::unchecked((1000 + key).to_string()),
&Addr::unchecked((1000 + key).to_string()),
stakes[key as usize],
);
}
assert_eq!(get_max_allowed_uids(&deps.storage, netuid), n);
assert_eq!(get_subnetwork_n(&deps.storage, netuid), n);
// === Issue validator permits
set_max_allowed_validators(&mut deps.storage, netuid, n);
assert_eq!(get_max_allowed_validators(&deps.storage, netuid), n);
epoch(
&mut deps.storage,
&deps.api,
netuid,
1_000_000_000,
env.block.height,
)
.unwrap(); // run first epoch to set allowed validators
step_block(deps.as_mut(), &mut env).unwrap(); // run to next block to ensure weights are set on nodes after their registration block
// === Set weights [val->srv1: 0.1, val->srv2: 0.2, val->srv3: 0.3, val->srv4: 0.4]
for uid in 0..(n / 2) as u64 {
set_weights(
deps.as_mut(),
env.clone(),
Addr::unchecked((1000 + uid).to_string()).as_str(),
netuid,
((n / 2)..n).collect(),
vec![u16::MAX / 4, u16::MAX / 2, (u16::MAX / 4) * 3, u16::MAX],
0,
)
.unwrap();
}
if sparse {
epoch(
&mut deps.storage,
&deps.api,
netuid,
1_000_000_000,
env.block.height,
)
.unwrap();
} else {
epoch_dense(&mut deps.storage, netuid, 1_000_000_000, env.block.height);
}
/* n: 8
current_block: 1; activity_cutoff: 5000; Last update: [1, 1, 1, 1, 0, 0, 0, 0]
Inactive: [false, false, false, false, false, false, false, false]
Block at registration: [0, 0, 0, 0, 0, 0, 0, 0]
hotkeys: [(0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6), (7, 7)]
S: [0.0999999999, 0.2, 0.2999999998, 0.4, 0, 0, 0, 0]
validator_permits: [true, true, true, true, true, true, true, true]
max_allowed_validators: 8
new_validator_permits: [true, true, true, true, true, true, true, true]
S: [0.0999999999, 0.2, 0.2999999998, 0.4, 0, 0, 0, 0]
W: [[(4, 16383), (5, 32767), (6, 49149), (7, 65535)], [(4, 16383), (5, 32767), (6, 49149), (7, 65535)], [(4, 16383), (5, 32767), (6, 49149), (7, 65535)], [(4, 16383), (5, 32767), (6, 49149), (7, 65535)], [], [], [], []]
W (permit): [[(4, 16383), (5, 32767), (6, 49149), (7, 65535)], [(4, 16383), (5, 32767), (6, 49149), (7, 65535)], [(4, 16383), (5, 32767), (6, 49149), (7, 65535)], [(4, 16383), (5, 32767), (6, 49149), (7, 65535)], [], [], [], []]
W (permit+diag): [[(4, 16383), (5, 32767), (6, 49149), (7, 65535)], [(4, 16383), (5, 32767), (6, 49149), (7, 65535)], [(4, 16383), (5, 32767), (6, 49149), (7, 65535)], [(4, 16383), (5, 32767), (6, 49149), (7, 65535)], [], [], [], []]
W (permit+diag+outdate): [[(4, 16383), (5, 32767), (6, 49149), (7, 65535)], [(4, 16383), (5, 32767), (6, 49149), (7, 65535)], [(4, 16383), (5, 32767), (6, 49149), (7, 65535)], [(4, 16383), (5, 32767), (6, 49149), (7, 65535)], [], [], [], []]
W (mask+norm): [[(4, 0.0999975584), (5, 0.2000012207), (6, 0.2999926754), (7, 0.400008545)], [(4, 0.0999975584), (5, 0.2000012207), (6, 0.2999926754), (7, 0.400008545)], [(4, 0.0999975584), (5, 0.2000012207), (6, 0.2999926754), (7, 0.400008545)], [(4, 0.0999975584), (5, 0.2000012207), (6, 0.2999926754), (7, 0.400008545)], [], [], [], []]
R (before): [0, 0, 0, 0, 0.099997558, 0.2000012202, 0.2999926745, 0.4000085443]
C: [0, 0, 0, 0, 0.0999975584, 0.2000012207, 0.2999926754, 0.400008545]
W: [[(4, 0.0999975584), (5, 0.2000012207), (6, 0.2999926754), (7, 0.400008545)], [(4, 0.0999975584), (5, 0.2000012207), (6, 0.2999926754), (7, 0.400008545)], [(4, 0.0999975584), (5, 0.2000012207), (6, 0.2999926754), (7, 0.400008545)], [(4, 0.0999975584), (5, 0.2000012207), (6, 0.2999926754), (7, 0.400008545)], [], [], [], []]
Tv: [0.9999999995, 0.9999999995, 0.9999999995, 0.9999999995, 0, 0, 0, 0]
R (after): [0, 0, 0, 0, 0.099997558, 0.2000012202, 0.2999926745, 0.4000085443]
T: [0, 0, 0, 0, 1, 1, 1, 1]
I (=R): [0, 0, 0, 0, 0.0999975582, 0.2000012207, 0.2999926752, 0.4000085455]
B: [[], [], [], [], [], [], [], []]
B (outdatedmask): [[], [], [], [], [], [], [], []]
B (mask+norm): [[], [], [], [], [], [], [], []]
ฮB: [[(4, 0.0099997558), (5, 0.020000122), (6, 0.0299992673), (7, 0.0400008543)], [(4, 0.0199995115), (5, 0.040000244), (6, 0.0599985349), (7, 0.0800017088)], [(4, 0.0299992673), (5, 0.060000366), (6, 0.0899978024), (7, 0.1200025633)], [(4, 0.0399990233), (5, 0.080000488), (6, 0.11999707), (7, 0.1600034179)], [], [], [], []]
ฮB (norm): [[(4, 0.0999999996), (5, 0.0999999999), (6, 0.0999999994), (7, 0.0999999996)], [(4, 0.1999999995), (5, 0.2), (6, 0.1999999997), (7, 0.1999999997)], [(4, 0.299999999), (5, 0.2999999998), (6, 0.3), (7, 0.3)], [(4, 0.4000000013), (5, 0.4), (6, 0.4000000004), (7, 0.4000000001)], [], [], [], []]
emaB: [[(4, 0.0999999982), (5, 0.0999999985), (6, 0.099999998), (7, 0.099999998)], [(4, 0.199999999), (5, 0.1999999995), (6, 0.1999999986), (7, 0.1999999986)], [(4, 0.2999999996), (5, 0.3000000003), (6, 0.3000000012), (7, 0.3000000012)], [(4, 0.4000000027), (5, 0.4000000013), (6, 0.4000000018), (7, 0.4000000018)], [], [], [], []]
D: [0.0999999978, 0.1999999983, 0.3000000012, 0.4000000022, 0, 0, 0, 0]
nE: [0.0499999989, 0.0999999992, 0.1500000006, 0.2000000011, 0.049998779, 0.1000006103, 0.1499963375, 0.2000042726]
E: [49999998, 99999999, 150000000, 200000001, 49998779, 100000610, 149996337, 200004272]
P: [0.0499999989, 0.0999999992, 0.1500000006, 0.2000000011, 0.049998779, 0.1000006103, 0.1499963375, 0.2000042726]
emaB: [[(4, 0.2499999937), (5, 0.2499999953), (6, 0.2499999937), (7, 0.2499999937)], [(4, 0.4999999942), (5, 0.499999997), (6, 0.4999999942), (7, 0.4999999942)], [(4, 0.7499999937), (5, 0.7499999981), (6, 0.7499999995), (7, 0.7499999995)], [(4, 1), (5, 1), (6, 1), (7, 1)], [], [], [], []] */
let bonds = get_bonds(&deps.storage, netuid);
assert_eq!(bonds[0][4], 16383);
assert_eq!(bonds[1][4], 32767);
assert_eq!(bonds[2][4], 49151);
assert_eq!(bonds[3][4], 65535);
// === Set self-weight only on val1
let uid = 0;
set_weights(
deps.as_mut(),
env.clone(),
Addr::unchecked((1000 + uid).to_string()).as_str(),
netuid,
vec![uid],
vec![u16::MAX],
0,
)
.unwrap();
step_block(deps.as_mut(), &mut env).unwrap();
if sparse {
epoch(
&mut deps.storage,
&deps.api,
netuid,
1_000_000_000,
env.block.height,
)
.unwrap();
} else {
epoch_dense(&mut deps.storage, netuid, 1_000_000_000, env.block.height);
}
/* n: 8
current_block: 2
activity_cutoff: 5000
Last update: [1, 1, 1, 1, 0, 0, 0, 0]
Inactive: [false, false, false, false, false, false, false, false]
Block at registration: [0, 0, 0, 0, 0, 0, 0, 0]
hotkeys: [(0, 0), (1, 1), (2, 2), (3, 3), (4, 4), (5, 5), (6, 6), (7, 7)]
S: [0.0999999999, 0.2, 0.2999999998, 0.4, 0, 0, 0, 0]
validator_permits: [true, true, true, true, true, true, true, true]
max_allowed_validators: 8
new_validator_permits: [true, true, true, true, true, true, true, true]
S: [0.0999999999, 0.2, 0.2999999998, 0.4, 0, 0, 0, 0]
W: [[(0, 65535)], [(4, 16383), (5, 32767), (6, 49149), (7, 65535)], [(4, 16383), (5, 32767), (6, 49149), (7, 65535)], [(4, 16383), (5, 32767), (6, 49149), (7, 65535)], [], [], [], []]
W (permit): [[(0, 65535)], [(4, 16383), (5, 32767), (6, 49149), (7, 65535)], [(4, 16383), (5, 32767), (6, 49149), (7, 65535)], [(4, 16383), (5, 32767), (6, 49149), (7, 65535)], [], [], [], []]
W (permit+diag): [[], [(4, 16383), (5, 32767), (6, 49149), (7, 65535)], [(4, 16383), (5, 32767), (6, 49149), (7, 65535)], [(4, 16383), (5, 32767), (6, 49149), (7, 65535)], [], [], [], []]
W (permit+diag+outdate): [[], [(4, 16383), (5, 32767), (6, 49149), (7, 65535)], [(4, 16383), (5, 32767), (6, 49149), (7, 65535)], [(4, 16383), (5, 32767), (6, 49149), (7, 65535)], [], [], [], []]
W (mask+norm): [[], [(4, 0.0999975584), (5, 0.2000012207), (6, 0.2999926754), (7, 0.400008545)], [(4, 0.0999975584), (5, 0.2000012207), (6, 0.2999926754), (7, 0.400008545)], [(4, 0.0999975584), (5, 0.2000012207), (6, 0.2999926754), (7, 0.400008545)], [], [], [], []]
R (before): [0, 0, 0, 0, 0.0899978022, 0.1800010982, 0.2699934072, 0.36000769]
C: [0, 0, 0, 0, 0.0999975584, 0.2000012207, 0.2999926754, 0.400008545]
W: [[], [(4, 0.0999975584), (5, 0.2000012207), (6, 0.2999926754), (7, 0.400008545)], [(4, 0.0999975584), (5, 0.2000012207), (6, 0.2999926754), (7, 0.400008545)], [(4, 0.0999975584), (5, 0.2000012207), (6, 0.2999926754), (7, 0.400008545)], [], [], [], []]
Tv: [0, 0.9999999995, 0.9999999995, 0.9999999995, 0, 0, 0, 0]
R (after): [0, 0, 0, 0, 0.0899978022, 0.1800010982, 0.2699934072, 0.36000769]
T: [0, 0, 0, 0, 1, 1, 1, 1]
I (=R): [0, 0, 0, 0, 0.0999975582, 0.2000012207, 0.2999926754, 0.4000085455]
B: [[(4, 16383), (5, 16383), (6, 16383), (7, 16383)], [(4, 32767), (5, 32767), (6, 32767), (7, 32767)], [(4, 49151), (5, 49151), (6, 49151), (7, 49151)], [(4, 65535), (5, 65535), (6, 65535), (7, 65535)], [], [], [], []]
B (outdatedmask): [[(4, 16383), (5, 16383), (6, 16383), (7, 16383)], [(4, 32767), (5, 32767), (6, 32767), (7, 32767)], [(4, 49151), (5, 49151), (6, 49151), (7, 49151)], [(4, 65535), (5, 65535), (6, 65535), (7, 65535)], [], [], [], []]
B (mask+norm): [[(4, 0.0999963377), (5, 0.0999963377), (6, 0.0999963377), (7, 0.0999963377)], [(4, 0.1999987792), (5, 0.1999987792), (6, 0.1999987792), (7, 0.1999987792)], [(4, 0.3000012205), (5, 0.3000012205), (6, 0.3000012205), (7, 0.3000012205)], [(4, 0.400003662), (5, 0.400003662), (6, 0.400003662), (7, 0.400003662)], [], [], [], []]
ฮB: [[], [(4, 0.0199995115), (5, 0.040000244), (6, 0.0599985349), (7, 0.0800017088)], [(4, 0.0299992673), (5, 0.060000366), (6, 0.0899978024), (7, 0.1200025633)], [(4, 0.0399990233), (5, 0.080000488), (6, 0.11999707), (7, 0.1600034179)], [], [], [], []]
ฮB (norm): [[], [(4, 0.2222222215), (5, 0.222222222), (6, 0.2222222218), (7, 0.2222222218)], [(4, 0.3333333323), (5, 0.3333333333), (6, 0.3333333333), (7, 0.3333333333)], [(4, 0.4444444457), (5, 0.4444444443), (6, 0.4444444447), (7, 0.4444444445)], [], [], [], []]
emaB: [[(4, 0.0899967037), (5, 0.0899967037), (6, 0.0899967037), (7, 0.0899967037)], [(4, 0.2022211235), (5, 0.2022211235), (6, 0.2022211235), (7, 0.2022211235)], [(4, 0.3033344317), (5, 0.3033344317), (6, 0.3033344317), (7, 0.3033344317)], [(4, 0.4044477409), (5, 0.4044477406), (6, 0.4044477406), (7, 0.4044477406)], [], [], [], []]
D: [0.0899967032, 0.2022211233, 0.303334432, 0.404447741, 0, 0, 0, 0]
nE: [0.0449983515, 0.1011105615, 0.1516672159, 0.2022238704, 0.049998779, 0.1000006103, 0.1499963377, 0.2000042726]
E: [44998351, 101110561, 151667215, 202223870, 49998779, 100000610, 149996337, 200004272]
P: [0.0449983515, 0.1011105615, 0.1516672159, 0.2022238704, 0.049998779, 0.1000006103, 0.1499963377, 0.2000042726]
emaB: [[(4, 0.2225175085), (5, 0.2225175085), (6, 0.2225175085), (7, 0.2225175085)], [(4, 0.499993208), (5, 0.4999932083), (6, 0.4999932083), (7, 0.4999932083)], [(4, 0.7499966028), (5, 0.7499966032), (6, 0.7499966032), (7, 0.7499966032)], [(4, 1), (5, 1), (6, 1), (7, 1)], [], [], [], []] */
let bonds = get_bonds(&deps.storage, netuid);
assert_eq!(bonds[0][4], 14582);
assert_eq!(bonds[1][4], 32767);
assert_eq!(bonds[2][4], 49151);
assert_eq!(bonds[3][4], 65535);
// === Set self-weight only on val2
let uid = 1;
set_weights(
deps.as_mut(),
env.clone(),
Addr::unchecked((1000 + uid).to_string()).as_str(),
netuid,
vec![uid],
vec![u16::MAX],
0,
)
.unwrap();
step_block(deps.as_mut(), &mut env).unwrap();
if sparse {
epoch(
&mut deps.storage,
&deps.api,
netuid,
1_000_000_000,
env.block.height,
)
.unwrap();
} else {
epoch_dense(&mut deps.storage, netuid, 1_000_000_000, env.block.height);
}
/* current_block: 3
W: [[(0, 65535)], [(1, 65535)], [(4, 16383), (5, 32767), (6, 49149), (7, 65535)], [(4, 16383), (5, 32767), (6, 49149), (7, 65535)], [], [], [], []]
W (permit): [[(0, 65535)], [(1, 65535)], [(4, 16383), (5, 32767), (6, 49149), (7, 65535)], [(4, 16383), (5, 32767), (6, 49149), (7, 65535)], [], [], [], []]
W (permit+diag): [[], [], [(4, 16383), (5, 32767), (6, 49149), (7, 65535)], [(4, 16383), (5, 32767), (6, 49149), (7, 65535)], [], [], [], []]
W (permit+diag+outdate): [[], [], [(4, 16383), (5, 32767), (6, 49149), (7, 65535)], [(4, 16383), (5, 32767), (6, 49149), (7, 65535)], [], [], [], []]
W (mask+norm): [[], [], [(4, 0.0999975584), (5, 0.2000012207), (6, 0.2999926754), (7, 0.400008545)], [(4, 0.0999975584), (5, 0.2000012207), (6, 0.2999926754), (7, 0.400008545)], [], [], [], []]
R (before): [0, 0, 0, 0, 0.0699982906, 0.1400008542, 0.2099948723, 0.2800059812]
C: [0, 0, 0, 0, 0.0999975584, 0.2000012207, 0.2999926754, 0.400008545]
W: [[], [], [(4, 0.0999975584), (5, 0.2000012207), (6, 0.2999926754), (7, 0.400008545)], [(4, 0.0999975584), (5, 0.2000012207), (6, 0.2999926754), (7, 0.400008545)], [], [], [], []]
Tv: [0, 0, 0.9999999995, 0.9999999995, 0, 0, 0, 0]
R (after): [0, 0, 0, 0, 0.0699982906, 0.1400008542, 0.2099948723, 0.2800059812]
T: [0, 0, 0, 0, 1, 1, 1, 1]
I (=R): [0, 0, 0, 0, 0.0999975582, 0.2000012207, 0.2999926754, 0.4000085455]
B: [[(4, 14582), (5, 14582), (6, 14582), (7, 14582)], [(4, 32767), (5, 32767), (6, 32767), (7, 32767)], [(4, 49151), (5, 49151), (6, 49151), (7, 49151)], [(4, 65535), (5, 65535), (6, 65535), (7, 65535)], [], [], [], []]
B (outdatedmask): [[(4, 14582), (5, 14582), (6, 14582), (7, 14582)], [(4, 32767), (5, 32767), (6, 32767), (7, 32767)], [(4, 49151), (5, 49151), (6, 49151), (7, 49151)], [(4, 65535), (5, 65535), (6, 65535), (7, 65535)], [], [], [], []]
B (mask+norm): [[(4, 0.0899929027), (5, 0.0899929027), (6, 0.0899929027), (7, 0.0899929027)], [(4, 0.2022217421), (5, 0.2022217421), (6, 0.2022217421), (7, 0.2022217421)], [(4, 0.303335699), (5, 0.303335699), (6, 0.303335699), (7, 0.303335699)], [(4, 0.404449656), (5, 0.404449656), (6, 0.404449656), (7, 0.404449656)], [], [], [], []]
ฮB: [[], [], [(4, 0.0299992673), (5, 0.060000366), (6, 0.0899978024), (7, 0.1200025633)], [(4, 0.0399990233), (5, 0.080000488), (6, 0.11999707), (7, 0.1600034179)], [], [], [], []]
ฮB (norm): [[], [], [(4, 0.428571427), (5, 0.4285714284), (6, 0.4285714284), (7, 0.4285714284)], [(4, 0.5714285728), (5, 0.5714285714), (6, 0.5714285714), (7, 0.5714285714)], [], [], [], []]
emaB: [[(4, 0.0809936123), (5, 0.0809936123), (6, 0.0809936123), (7, 0.0809936123)], [(4, 0.181999568), (5, 0.181999568), (6, 0.181999568), (7, 0.181999568)], [(4, 0.3158592717), (5, 0.315859272), (6, 0.315859272), (7, 0.315859272)], [(4, 0.4211475477), (5, 0.4211475474), (6, 0.4211475474), (7, 0.4211475474)], [], [], [], []]
D: [0.0809936118, 0.1819995677, 0.3158592721, 0.421147548, 0, 0, 0, 0]
nE: [0.040496806, 0.0909997837, 0.157929636, 0.2105737738, 0.049998779, 0.1000006103, 0.1499963377, 0.2000042726]
E: [40496805, 90999783, 157929636, 210573773, 49998779, 100000610, 149996337, 200004272]
P: [0.040496806, 0.0909997837, 0.157929636, 0.2105737738, 0.049998779, 0.1000006103, 0.1499963377, 0.2000042726]
emaB: [[(4, 0.192316476), (5, 0.192316476), (6, 0.192316476), (7, 0.192316476)], [(4, 0.4321515555), (5, 0.4321515558), (6, 0.4321515558), (7, 0.4321515558)], [(4, 0.7499967015), (5, 0.7499967027), (6, 0.7499967027), (7, 0.7499967027)], [(4, 1), (5, 1), (6, 1), (7, 1)], [], [], [], []] */
let bonds = get_bonds(&deps.storage, netuid);
assert_eq!(bonds[0][4], 12603);
assert_eq!(bonds[1][4], 28321);
assert_eq!(bonds[2][4], 49151);
assert_eq!(bonds[3][4], 65535);
// === Set self-weight only on val3
let uid = 2;
set_weights(
deps.as_mut(),
env.clone(),
Addr::unchecked((1000 + uid).to_string()).as_str(),
netuid,
vec![uid],
vec![u16::MAX],
0,
)
.unwrap();
step_block(deps.as_mut(), &mut env).unwrap();
if sparse {
epoch(
&mut deps.storage,
&deps.api,
netuid,
1_000_000_000,
env.block.height,
)
.unwrap();
} else {
epoch_dense(&mut deps.storage, netuid, 1_000_000_000, env.block.height);
}
/* current_block: 4
W: [[(0, 65535)], [(1, 65535)], [(2, 65535)], [(4, 16383), (5, 32767), (6, 49149), (7, 65535)], [], [], [], []]
W (permit): [[(0, 65535)], [(1, 65535)], [(2, 65535)], [(4, 16383), (5, 32767), (6, 49149), (7, 65535)], [], [], [], []]
W (permit+diag): [[], [], [], [(4, 16383), (5, 32767), (6, 49149), (7, 65535)], [], [], [], []]
W (permit+diag+outdate): [[], [], [], [(4, 16383), (5, 32767), (6, 49149), (7, 65535)], [], [], [], []]
W (mask+norm): [[], [], [], [(4, 0.0999975584), (5, 0.2000012207), (6, 0.2999926754), (7, 0.400008545)], [], [], [], []]
R (before): [0, 0, 0, 0, 0.0399990233, 0.080000488, 0.11999707, 0.1600034179]
C: [0, 0, 0, 0, 0, 0, 0, 0]
W: [[], [], [], [], [], [], [], []]
Tv: [0, 0, 0, 0, 0, 0, 0, 0]
R (after): [0, 0, 0, 0, 0, 0, 0, 0]
T: [0, 0, 0, 0, 0, 0, 0, 0]
I (=R): [0, 0, 0, 0, 0, 0, 0, 0]
B: [[(4, 12603), (5, 12603), (6, 12603), (7, 12603)], [(4, 28321), (5, 28321), (6, 28321), (7, 28321)], [(4, 49151), (5, 49151), (6, 49151), (7, 49151)], [(4, 65535), (5, 65535), (6, 65535), (7, 65535)], [], [], [], []]
B (outdatedmask): [[(4, 12603), (5, 12603), (6, 12603), (7, 12603)], [(4, 28321), (5, 28321), (6, 28321), (7, 28321)], [(4, 49151), (5, 49151), (6, 49151), (7, 49151)], [(4, 65535), (5, 65535), (6, 65535), (7, 65535)], [], [], [], []]
B (mask+norm): [[(4, 0.0809909387), (5, 0.0809909387), (6, 0.0809909387), (7, 0.0809909387)], [(4, 0.1819998713), (5, 0.1819998713), (6, 0.1819998713), (7, 0.1819998713)], [(4, 0.3158601632), (5, 0.3158601632), (6, 0.3158601632), (7, 0.3158601632)], [(4, 0.4211490264), (5, 0.4211490264), (6, 0.4211490264), (7, 0.4211490264)], [], [], [], []]
ฮB: [[], [], [], [], [], [], [], []]
ฮB (norm): [[], [], [], [], [], [], [], []]
emaB: [[(4, 0.0809909385), (5, 0.0809909385), (6, 0.0809909385), (7, 0.0809909385)], [(4, 0.1819998713), (5, 0.1819998713), (6, 0.1819998713), (7, 0.1819998713)], [(4, 0.3158601632), (5, 0.3158601632), (6, 0.3158601632), (7, 0.3158601632)], [(4, 0.4211490266), (5, 0.4211490266), (6, 0.4211490266), (7, 0.4211490266)], [], [], [], []]
D: [0, 0, 0, 0, 0, 0, 0, 0]
nE: [0.0999999999, 0.2, 0.2999999998, 0.4, 0, 0, 0, 0]
E: [99999999, 199999999, 299999999, 399999999, 0, 0, 0, 0]
P: [0.0999999999, 0.2, 0.2999999998, 0.4, 0, 0, 0, 0]
emaB: [[(4, 0.1923094518), (5, 0.1923094518), (6, 0.1923094518), (7, 0.1923094518)], [(4, 0.4321507583), (5, 0.4321507583), (6, 0.4321507583), (7, 0.4321507583)], [(4, 0.7499961846), (5, 0.7499961846), (6, 0.7499961846), (7, 0.7499961846)], [(4, 1), (5, 1), (6, 1), (7, 1)], [], [], [], []] */
let bonds = get_bonds(&deps.storage, netuid);
assert_eq!(bonds[0][7], 12602);
assert_eq!(bonds[1][7], 28320);
assert_eq!(bonds[2][7], 49150);
assert_eq!(bonds[3][7], 65535);
// === Set val3->srv4: 1
set_weights(
deps.as_mut(),
env.clone(),
Addr::unchecked(1002.to_string()).as_str(),
netuid,
vec![7],
vec![u16::MAX],
0,
)
.unwrap();
step_block(deps.as_mut(), &mut env).unwrap();
if sparse {
epoch(
&mut deps.storage,
&deps.api,
netuid,
1_000_000_000,
env.block.height,
)
.unwrap();
} else {
epoch_dense(&mut deps.storage, netuid, 1_000_000_000, env.block.height);
}
/* current_block: 5
W: [[(0, 65535)], [(1, 65535)], [(7, 65535)], [(4, 16383), (5, 32767), (6, 49149), (7, 65535)], [], [], [], []]
W (permit): [[(0, 65535)], [(1, 65535)], [(7, 65535)], [(4, 16383), (5, 32767), (6, 49149), (7, 65535)], [], [], [], []]
W (permit+diag): [[], [], [(7, 65535)], [(4, 16383), (5, 32767), (6, 49149), (7, 65535)], [], [], [], []]
W (permit+diag+outdate): [[], [], [(7, 65535)], [(4, 16383), (5, 32767), (6, 49149), (7, 65535)], [], [], [], []]
W (mask+norm): [[], [], [(7, 1)], [(4, 0.0999975584), (5, 0.2000012207), (6, 0.2999926754), (7, 0.400008545)], [], [], [], []]
R (before): [0, 0, 0, 0, 0.0399990233, 0.080000488, 0.11999707, 0.4600034177]
C: [0, 0, 0, 0, 0, 0, 0, 0.400008545]
W: [[], [], [(7, 0.400008545)], [(7, 0.400008545)], [], [], [], []]
Tv: [0, 0, 0.400008545, 0.400008545, 0, 0, 0, 0]
R (after): [0, 0, 0, 0, 0, 0, 0, 0.2800059812]
T: [0, 0, 0, 0, 0, 0, 0, 0.6087041323]
I (=R): [0, 0, 0, 0, 0, 0, 0, 1]
B: [[(4, 12602), (5, 12602), (6, 12602), (7, 12602)], [(4, 28320), (5, 28320), (6, 28320), (7, 28320)], [(4, 49150), (5, 49150), (6, 49150), (7, 49150)], [(4, 65535), (5, 65535), (6, 65535), (7, 65535)], [], [], [], []]
B (outdatedmask): [[(4, 12602), (5, 12602), (6, 12602), (7, 12602)], [(4, 28320), (5, 28320), (6, 28320), (7, 28320)], [(4, 49150), (5, 49150), (6, 49150), (7, 49150)], [(4, 65535), (5, 65535), (6, 65535), (7, 65535)], [], [], [], []]
B (mask+norm): [[(4, 0.0809860737), (5, 0.0809860737), (6, 0.0809860737), (7, 0.0809860737)], [(4, 0.1819969537), (5, 0.1819969537), (6, 0.1819969537), (7, 0.1819969537)], [(4, 0.3158598263), (5, 0.3158598263), (6, 0.3158598263), (7, 0.3158598263)], [(4, 0.4211571459), (5, 0.4211571459), (6, 0.4211571459), (7, 0.4211571459)], [], [], [], []]
ฮB: [[], [], [(7, 0.1200025633)], [(7, 0.1600034179)], [], [], [], []]
ฮB (norm): [[], [], [(7, 0.4285714284)], [(7, 0.5714285714)], [], [], [], []]
emaB: [[(4, 0.0809860737), (5, 0.0809860737), (6, 0.0809860737), (7, 0.0728874663)], [(4, 0.1819969537), (5, 0.1819969537), (6, 0.1819969537), (7, 0.1637972582)], [(4, 0.3158598263), (5, 0.3158598263), (6, 0.3158598263), (7, 0.3271309866)], [(4, 0.421157146), (5, 0.421157146), (6, 0.421157146), (7, 0.4361842885)], [], [], [], []]
D: [0.0728874663, 0.1637972582, 0.3271309866, 0.4361842885, 0, 0, 0, 0]
nE: [0.0364437331, 0.081898629, 0.1635654932, 0.2180921442, 0, 0, 0, 0.5]
E: [36443733, 81898628, 163565493, 218092144, 0, 0, 0, 500000000]
P: [0.0364437331, 0.081898629, 0.1635654932, 0.2180921442, 0, 0, 0, 0.5]
emaB: [[(4, 0.1922941932), (5, 0.1922941932), (6, 0.1922941932), (7, 0.1671024568)], [(4, 0.4321354993), (5, 0.4321354993), (6, 0.4321354993), (7, 0.3755230587)], [(4, 0.7499809256), (5, 0.7499809256), (6, 0.7499809256), (7, 0.749983425)], [(4, 1), (5, 1), (6, 1), (7, 1)], [], [], [], []] */
let bonds = get_bonds(&deps.storage, netuid);
assert_eq!(bonds[0][7], 10951);
assert_eq!(bonds[1][7], 24609);
assert_eq!(bonds[2][7], 49150);
assert_eq!(bonds[3][7], 65535);
step_block(deps.as_mut(), &mut env).unwrap();
if sparse {
epoch(
&mut deps.storage,
&deps.api,
netuid,
1_000_000_000,
env.block.height,
)
.unwrap();
} else {
epoch_dense(&mut deps.storage, netuid, 1_000_000_000, env.block.height);
}
/* current_block: 6
B: [[(4, 12601), (5, 12601), (6, 12601), (7, 10951)], [(4, 28319), (5, 28319), (6, 28319), (7, 24609)], [(4, 49149), (5, 49149), (6, 49149), (7, 49150)], [(4, 65535), (5, 65535), (6, 65535), (7, 65535)], [], [], [], []]
B (outdatedmask): [[(4, 12601), (5, 12601), (6, 12601), (7, 10951)], [(4, 28319), (5, 28319), (6, 28319), (7, 24609)], [(4, 49149), (5, 49149), (6, 49149), (7, 49150)], [(4, 65535), (5, 65535), (6, 65535), (7, 65535)], [], [], [], []]
B (mask+norm): [[(4, 0.0809812085), (5, 0.0809812085), (6, 0.0809812085), (7, 0.0728876167)], [(4, 0.181994036), (5, 0.181994036), (6, 0.181994036), (7, 0.163792472)], [(4, 0.3158594894), (5, 0.3158594894), (6, 0.3158594894), (7, 0.3271323503)], [(4, 0.4211652656), (5, 0.4211652656), (6, 0.4211652656), (7, 0.4361875602)], [], [], [], []]
ฮB: [[], [], [(7, 0.1200025633)], [(7, 0.1600034179)], [], [], [], []]
ฮB (norm): [[], [], [(7, 0.4285714284)], [(7, 0.5714285714)], [], [], [], []]
emaB: [[(4, 0.0809812082), (5, 0.0809812082), (6, 0.0809812082), (7, 0.0655988548)], [(4, 0.181994036), (5, 0.181994036), (6, 0.181994036), (7, 0.1474132247)], [(4, 0.3158594896), (5, 0.3158594896), (6, 0.3158594896), (7, 0.3372762585)], [(4, 0.4211652658), (5, 0.4211652658), (6, 0.4211652658), (7, 0.4497116616)], [], [], [], []]
D: [0.0655988548, 0.1474132247, 0.3372762585, 0.4497116616, 0, 0, 0, 0]
nE: [0.0327994274, 0.0737066122, 0.1686381293, 0.2248558307, 0, 0, 0, 0.5]
E: [32799427, 73706612, 168638129, 224855830, 0, 0, 0, 500000000]
P: [0.0327994274, 0.0737066122, 0.1686381293, 0.2248558307, 0, 0, 0, 0.5]
emaB: [[(4, 0.1922789337), (5, 0.1922789337), (6, 0.1922789337), (7, 0.1458686984)], [(4, 0.4321202405), (5, 0.4321202405), (6, 0.4321202405), (7, 0.3277949789)], [(4, 0.749965667), (5, 0.749965667), (6, 0.749965667), (7, 0.74998335)], [(4, 1), (5, 1), (6, 1), (7, 1)], [], [], [], []] */
let bonds = get_bonds(&deps.storage, netuid);
assert_eq!(bonds[0][7], 9559);
assert_eq!(bonds[1][7], 21482);
assert_eq!(bonds[2][7], 49150);
assert_eq!(bonds[3][7], 65535);
step_block(deps.as_mut(), &mut env).unwrap();
if sparse {
epoch(
&mut deps.storage,
&deps.api,
netuid,
1_000_000_000,
env.block.height,
)
.unwrap();
} else {
epoch_dense(&mut deps.storage, netuid, 1_000_000_000, env.block.height);
}
/* current_block: 7
B: [[(4, 12600), (5, 12600), (6, 12600), (7, 9559)], [(4, 28318), (5, 28318), (6, 28318), (7, 21482)], [(4, 49148), (5, 49148), (6, 49148), (7, 49150)], [(4, 65535), (5, 65535), (6, 65535), (7, 65535)], [], [], [], []]
B (outdatedmask): [[(4, 12600), (5, 12600), (6, 12600), (7, 9559)], [(4, 28318), (5, 28318), (6, 28318), (7, 21482)], [(4, 49148), (5, 49148), (6, 49148), (7, 49150)], [(4, 65535), (5, 65535), (6, 65535), (7, 65535)], [], [], [], []]
B (mask+norm): [[(4, 0.0809763432), (5, 0.0809763432), (6, 0.0809763432), (7, 0.065595707)], [(4, 0.1819911182), (5, 0.1819911182), (6, 0.1819911182), (7, 0.1474136391)], [(4, 0.3158591525), (5, 0.3158591525), (6, 0.3158591525), (7, 0.337276807)], [(4, 0.4211733856), (5, 0.4211733856), (6, 0.4211733856), (7, 0.4497138464)], [], [], [], []]
ฮB: [[], [], [(7, 0.1200025633)], [(7, 0.1600034179)], [], [], [], []]
ฮB (norm): [[], [], [(7, 0.4285714284)], [(7, 0.5714285714)], [], [], [], []]
emaB: [[(4, 0.080976343), (5, 0.080976343), (6, 0.080976343), (7, 0.0590361361)], [(4, 0.181991118), (5, 0.181991118), (6, 0.181991118), (7, 0.1326722752)], [(4, 0.3158591525), (5, 0.3158591525), (6, 0.3158591525), (7, 0.3464062694)], [(4, 0.4211733858), (5, 0.4211733858), (6, 0.4211733858), (7, 0.4618853189)], [], [], [], []]
D: [0.0590361361, 0.1326722752, 0.3464062694, 0.4618853189, 0, 0, 0, 0]
nE: [0.029518068, 0.0663361375, 0.1732031347, 0.2309426593, 0, 0, 0, 0.5]
E: [29518068, 66336137, 173203134, 230942659, 0, 0, 0, 500000000]
P: [0.029518068, 0.0663361375, 0.1732031347, 0.2309426593, 0, 0, 0, 0.5]
emaB: [[(4, 0.192263675), (5, 0.192263675), (6, 0.192263675), (7, 0.1278155716)], [(4, 0.4321049813), (5, 0.4321049813), (6, 0.4321049813), (7, 0.2872407278)], [(4, 0.7499504078), (5, 0.7499504078), (6, 0.7499504078), (7, 0.7499832863)], [(4, 1), (5, 1), (6, 1), (7, 1)], [], [], [], []] */
let bonds = get_bonds(&deps.storage, netuid);
assert_eq!(bonds[0][7], 8376);
assert_eq!(bonds[1][7], 18824);
assert_eq!(bonds[2][7], 49150);
assert_eq!(bonds[3][7], 65535);
// run_to_block(8);
step_block(deps.as_mut(), &mut env).unwrap();
if sparse {
epoch(
&mut deps.storage,
&deps.api,
netuid,
1_000_000_000,
env.block.height,
)
.unwrap();
} else {
epoch_dense(&mut deps.storage, netuid, 1_000_000_000, env.block.height);
}
/* current_block: 8
B: [[(4, 12599), (5, 12599), (6, 12599), (7, 8376)], [(4, 28317), (5, 28317), (6, 28317), (7, 18824)], [(4, 49147), (5, 49147), (6, 49147), (7, 49150)], [(4, 65535), (5, 65535), (6, 65535), (7, 65535)], [], [], [], []]
B (outdatedmask): [[(4, 12599), (5, 12599), (6, 12599), (7, 8376)], [(4, 28317), (5, 28317), (6, 28317), (7, 18824)], [(4, 49147), (5, 49147), (6, 49147), (7, 49150)], [(4, 65535), (5, 65535), (6, 65535), (7, 65535)], [], [], [], []]
B (mask+norm): [[(4, 0.0809714776), (5, 0.0809714776), (6, 0.0809714776), (7, 0.0590337245)], [(4, 0.1819882002), (5, 0.1819882002), (6, 0.1819882002), (7, 0.1326708249)], [(4, 0.3158588156), (5, 0.3158588156), (6, 0.3158588156), (7, 0.3464073015)], [(4, 0.421181506), (5, 0.421181506), (6, 0.421181506), (7, 0.4618881487)], [], [], [], []]
ฮB: [[], [], [(7, 0.1200025633)], [(7, 0.1600034179)], [], [], [], []]
ฮB (norm): [[], [], [(7, 0.4285714284)], [(7, 0.5714285714)], [], [], [], []]
emaB: [[(4, 0.0809714776), (5, 0.0809714776), (6, 0.0809714776), (7, 0.053130352)], [(4, 0.1819882002), (5, 0.1819882002), (6, 0.1819882002), (7, 0.1194037423)], [(4, 0.3158588156), (5, 0.3158588156), (6, 0.3158588156), (7, 0.3546237142)], [(4, 0.4211815062), (5, 0.4211815062), (6, 0.4211815062), (7, 0.472842191)], [], [], [], []]
D: [0.053130352, 0.1194037423, 0.3546237142, 0.472842191, 0, 0, 0, 0]
nE: [0.026565176, 0.0597018711, 0.177311857, 0.2364210954, 0, 0, 0, 0.5]
E: [26565175, 59701871, 177311856, 236421095, 0, 0, 0, 500000000]
P: [0.026565176, 0.0597018711, 0.177311857, 0.2364210954, 0, 0, 0, 0.5]
emaB: [[(4, 0.1922484161), (5, 0.1922484161), (6, 0.1922484161), (7, 0.1123638137)], [(4, 0.4320897225), (5, 0.4320897225), (6, 0.4320897225), (7, 0.2525234516)], [(4, 0.7499351487), (5, 0.7499351487), (6, 0.7499351487), (7, 0.7499832308)], [(4, 1), (5, 1), (6, 1), (7, 1)], [], [], [], []] */
}
// // Test that epoch masks out inactive stake of validators with outdated weights beyond activity cutoff.
#[test]
fn test_active_stake() {
let (mut deps, mut env) = instantiate_contract();
let sparse: bool = true;
let n: u16 = 4;
let netuid: u16 = 2;
let tempo: u16 = u16::MAX - 1; // high tempo to skip automatic epochs in on_initialize, use manual epochs instead
let block_number: u64 = 0;
let stake: u64 = 1;
add_network(&mut deps.storage, netuid, tempo, 0);
set_max_allowed_uids(&mut deps.storage, netuid, n);
assert_eq!(get_max_allowed_uids(&deps.storage, netuid), n);
set_max_registrations_per_block(&mut deps.storage, netuid, n);
set_target_registrations_per_interval(&mut deps.storage, netuid, n);
set_min_allowed_weights(&mut deps.storage, netuid, 0);
set_max_weight_limit(&mut deps.storage, netuid, u16::MAX);
set_weights_set_rate_limit(&mut deps.storage, netuid, 0);
set_activity_cutoff(&mut deps.storage, netuid, 50);
set_min_difficulty(&mut deps.storage, netuid, 1_000);
set_difficulty(&mut deps.storage, netuid, 1_000);
// === Register [validator1, validator2, server1, server2]
for key in 0..n as u64 {
add_balance_to_coldkey_account(&Addr::unchecked((1000 + key).to_string()), stake);
let (nonce, work): (u64, Vec<u8>) = create_work_for_block_number(
&deps.storage,
netuid,
block_number,
key * 1_000_000,
Addr::unchecked((1000 + key).to_string()).as_str(),
);
pow_register_ok_neuron(
deps.as_mut(),
env.clone(),
netuid,
block_number,
nonce,
work,
Addr::unchecked((1000 + key).to_string()).as_str(),
Addr::unchecked((1000 + key).to_string()).as_str(),
)
.unwrap();
increase_stake_on_coldkey_hotkey_account(
&mut deps.storage,
&Addr::unchecked((1000 + key).to_string()),
&Addr::unchecked((1000 + key).to_string()),
stake,
);
}
assert_eq!(get_max_allowed_uids(&deps.storage, netuid), n);
assert_eq!(get_subnetwork_n(&deps.storage, netuid), n);
// === Issue validator permits
set_max_allowed_validators(&mut deps.storage, netuid, n);
assert_eq!(get_max_allowed_validators(&deps.storage, netuid), n);
epoch(
&mut deps.storage,
&deps.api,
netuid,
1_000_000_000,
env.block.height,
)
.unwrap(); // run first epoch to set allowed validators
step_block(deps.as_mut(), &mut env).unwrap(); // run to next block to ensure weights are set on nodes after their registration block
// === Set weights [val1->srv1: 0.5, val1->srv2: 0.5, val2->srv1: 0.5, val2->srv2: 0.5]
for uid in 0..(n / 2) as u64 {
set_weights(
deps.as_mut(),
env.clone(),
Addr::unchecked((1000 + uid).to_string()).as_str(),
netuid,
((n / 2)..n).collect(),
vec![u16::MAX / (n / 2); (n / 2) as usize],
0,
)
.unwrap();
}
if sparse {
epoch(
&mut deps.storage,
&deps.api,
netuid,
1_000_000_000,
env.block.height,
)
.unwrap();
} else {
epoch_dense(&mut deps.storage, netuid, 1_000_000_000, env.block.height);
}
let bonds = get_bonds(&deps.storage, netuid);
for uid in 0..n as u16 {
// log::info!("\n{uid}" );
// uid_stats(netuid, uid);
// log::info!("bonds: {:?}", bonds[uid as usize]);
if uid < n / 2 {
assert_eq!(get_dividends_for_uid(&deps.storage, netuid, uid), 32767);
// Note D = floor(0.5 * 65_535)
}
assert_eq!(get_emission_for_uid(&deps.storage, netuid, uid), 250000000);
// Note E = 0.5 / (n/2) * 1_000_000_000 = 250_000_000
}
for validator in 0..(n / 2) as usize {
for on_validator in 0..(n / 2) as usize {
assert_eq!(bonds[validator][on_validator], 0);
}
for server in ((n / 2) as usize)..n as usize {
assert_eq!(bonds[validator][server], I32F32::from_num(65_535)); // floor(0.5*(2^16-1))/(2^16-1), then max-upscale to 65_535
}
}
let activity_cutoff: u64 = get_activity_cutoff(&deps.storage, netuid) as u64;
// run_to_block(activity_cutoff + 2);
run_step_to_block(deps.as_mut(), &mut env, activity_cutoff + 3).unwrap(); // run to block where validator (uid 0, 1) weights become outdated
// === Update uid 0 weights
set_weights(
deps.as_mut(),
env.clone(),
Addr::unchecked(1000.to_string()).as_str(),
netuid,
((n / 2)..n).collect(),
vec![u16::MAX / (n / 2); (n / 2) as usize],
0,
)
.unwrap();
if sparse {
epoch(
&mut deps.storage,
&deps.api,
netuid,
1_000_000_000,
env.block.height,
)
.unwrap();
} else {
epoch_dense(&mut deps.storage, netuid, 1_000_000_000, env.block.height);
}
/* current_block: 5002; activity_cutoff: 5000
Last update: [5002, 1, 0, 0]; Inactive: [false, true, true, true]; Block at registration: [0, 0, 0, 0]
S: [0.25, 0.25, 0.25, 0.25]; S (mask): [0.25, 0, 0, 0]; S (mask+norm): [1, 0, 0, 0]
validator_permits: [true, true, true, true]; max_allowed_validators: 4; new_validator_permits: [true, true, true, true]
W: [[(2, 0.4999923704), (3, 0.4999923704)], [(2, 0.4999923704), (3, 0.4999923704)], [], []]
W (permit): [[(2, 0.4999923704), (3, 0.4999923704)], [(2, 0.4999923704), (3, 0.4999923704)], [], []]
W (permit+diag): [[(2, 0.4999923704), (3, 0.4999923704)], [(2, 0.4999923704), (3, 0.4999923704)], [], []]
W (permit+diag+outdate): [[(2, 0.4999923704), (3, 0.4999923704)], [(2, 0.4999923704), (3, 0.4999923704)], [], []]
W (mask+norm): [[(2, 0.5), (3, 0.5)], [(2, 0.5), (3, 0.5)], [], []]
R: [0, 0, 0.5, 0.5]
W (threshold): [[(2, 1), (3, 1)], [(2, 1), (3, 1)], [], []]
T: [0, 0, 1, 1]
C: [0.006693358, 0.006693358, 0.9933076561, 0.9933076561]
I: [0, 0, 0.5, 0.5]
B: [[(2, 0.4999923704), (3, 0.4999923704)], [(2, 0.4999923704), (3, 0.4999923704)], [], []]
B (outdatedmask): [[(2, 0.4999923704), (3, 0.4999923704)], [(2, 0.4999923704), (3, 0.4999923704)], [], []]
B (mask+norm): [[(2, 0.5), (3, 0.5)], [(2, 0.5), (3, 0.5)], [], []]
ฮB: [[(2, 0.5), (3, 0.5)], [(2, 0), (3, 0)], [], []]
ฮB (norm): [[(2, 1), (3, 1)], [(2, 0), (3, 0)], [], []]
emaB: [[(2, 0.55), (3, 0.55)], [(2, 0.45), (3, 0.45)], [], []]
emaB (max-upscale): [[(2, 1), (3, 1)], [(2, 1), (3, 1)], [], []]
D: [0.55, 0.4499999997, 0, 0]
nE: [0.275, 0.2249999999, 0.25, 0.25]
E: [274999999, 224999999, 250000000, 250000000]
P: [0.275, 0.2249999999, 0.25, 0.25]
P (u16): [65535, 53619, 59577, 59577] */
let bonds = get_bonds(&deps.storage, netuid);
assert_eq!(get_dividends_for_uid(&deps.storage, netuid, 0), 36044); // Note D = floor((0.5 * 0.9 + 0.1) * 65_535)
assert_eq!(get_emission_for_uid(&deps.storage, netuid, 0), 274999999); // Note E = 0.5 * 0.55 * 1_000_000_000 = 275_000_000 (discrepancy)
for server in ((n / 2) as usize)..n as usize {
assert_eq!(bonds[0][server], I32F32::from_num(65_535)); // floor(0.55*(2^16-1))/(2^16-1), then max-upscale
}
for validator in 1..(n / 2) as u16 {
assert_eq!(
get_dividends_for_uid(&deps.storage, netuid, validator),
29490
); // Note D = floor((0.5 * 0.9) * 65_535)
assert_eq!(
get_emission_for_uid(&deps.storage, netuid, validator),
224999999
); // Note E = 0.5 * 0.45 * 1_000_000_000 = 225_000_000 (discrepancy)
for server in ((n / 2) as usize)..n as usize {
assert_eq!(bonds[validator as usize][server], I32F32::from_num(53619));
// floor(0.45*(2^16-1))/(2^16-1), then max-upscale
}
}
// === Update uid 1 weights as well
set_weights(
deps.as_mut(),
env.clone(),
Addr::unchecked(1001.to_string()).as_str(),
netuid,
((n / 2)..n).collect(),
vec![u16::MAX / (n / 2); (n / 2) as usize],
0,
)
.unwrap();
// run_to_block(activity_cutoff + 3); // run to block where validator (uid 0, 1) weights become outdated
// run_step_to_block(deps.as_mut(), &mut env, activity_cutoff + 4).unwrap();
step_block(deps.as_mut(), &mut env).unwrap();
if sparse {
epoch(
&mut deps.storage,
&deps.api,
netuid,
1_000_000_000,
env.block.height,
)
.unwrap();
} else {
epoch_dense(&mut deps.storage, netuid, 1_000_000_000, env.block.height);
}
/* current_block: 5003; activity_cutoff: 5000
Last update: [5002, 5002, 0, 0]; Inactive: [false, false, true, true]; Block at registration: [0, 0, 0, 0]
S: [0.25, 0.25, 0.25, 0.25]; S (mask): [0.25, 0.25, 0, 0]; S (mask+norm): [0.5, 0.5, 0, 0]
validator_permits: [true, true, true, true]; max_allowed_validators: 4; new_validator_permits: [true, true, true, true]
W: [[(2, 0.4999923704), (3, 0.4999923704)], [(2, 0.4999923704), (3, 0.4999923704)], [], []]
W (permit): [[(2, 0.4999923704), (3, 0.4999923704)], [(2, 0.4999923704), (3, 0.4999923704)], [], []]
W (permit+diag): [[(2, 0.4999923704), (3, 0.4999923704)], [(2, 0.4999923704), (3, 0.4999923704)], [], []]
W (permit+diag+outdate): [[(2, 0.4999923704), (3, 0.4999923704)], [(2, 0.4999923704), (3, 0.4999923704)], [], []]
W (mask+norm): [[(2, 0.5), (3, 0.5)], [(2, 0.5), (3, 0.5)], [], []]
R: [0, 0, 0.5, 0.5]
W (threshold): [[(2, 1), (3, 1)], [(2, 1), (3, 1)], [], []]
T: [0, 0, 1, 1]
C: [0.006693358, 0.006693358, 0.9933076561, 0.9933076561]
I: [0, 0, 0.5, 0.5]
B: [[(2, 65535), (3, 65535)], [(2, 53619), (3, 53619)], [], []]
B (outdatedmask): [[(2, 65535), (3, 65535)], [(2, 53619), (3, 53619)], [], []]
B (mask+norm): [[(2, 0.5500025176), (3, 0.5500025176)], [(2, 0.4499974821), (3, 0.4499974821)], [], []]
ฮB: [[(2, 0.25), (3, 0.25)], [(2, 0.25), (3, 0.25)], [], []]
ฮB (norm): [[(2, 0.5), (3, 0.5)], [(2, 0.5), (3, 0.5)], [], []]
emaB: [[(2, 0.545002266), (3, 0.545002266)], [(2, 0.4549977337), (3, 0.4549977337)], [], []]
emaB (max-upscale): [[(2, 1), (3, 1)], [(2, 0.8348547556), (3, 0.8348547556)], [], []]
D: [0.545002266, 0.4549977337, 0, 0]
nE: [0.272501133, 0.2274988669, 0.25, 0.25]
E: [272501132, 227498866, 250000000, 250000000]
P: [0.272501133, 0.2274988669, 0.25, 0.25]
P (u16): [65535, 54711, 60123, 60123] */
let bonds = get_bonds(&deps.storage, netuid);
assert_eq!(get_dividends_for_uid(&deps.storage, netuid, 0), 35716); // Note D = floor((0.55 * 0.9 + 0.5 * 0.1) * 65_535)
assert_eq!(get_emission_for_uid(&deps.storage, netuid, 0), 272501132); // Note E = 0.5 * (0.55 * 0.9 + 0.5 * 0.1) * 1_000_000_000 = 272_500_000 (discrepancy)
for server in ((n / 2) as usize)..n as usize {
assert_eq!(bonds[0][server], I32F32::from_num(65_535)); // floor((0.55 * 0.9 + 0.5 * 0.1)*(2^16-1))/(2^16-1), then max-upscale
}
assert_eq!(get_dividends_for_uid(&deps.storage, netuid, 1), 29818); // Note D = floor((0.45 * 0.9 + 0.5 * 0.1) * 65_535)
assert_eq!(get_emission_for_uid(&deps.storage, netuid, 1), 227498866); // Note E = 0.5 * (0.45 * 0.9 + 0.5 * 0.1) * 1_000_000_000 = 227_500_000 (discrepancy)
for server in ((n / 2) as usize)..n as usize {
assert_eq!(bonds[1][server], I32F32::from_num(54712)); // floor((0.45 * 0.9 + 0.5 * 0.1)/(0.55 * 0.9 + 0.5 * 0.1)*(2^16-1))
}
}
// // Test that epoch masks out outdated weights and bonds of validators on deregistered servers.
#[test]
fn test_outdated_weights() {
let (mut deps, mut env) = instantiate_contract();
let sparse: bool = true;
let n: u16 = 4;
let netuid: u16 = 2;
let tempo: u16 = u16::MAX - 1; // high tempo to skip automatic epochs in on_initialize, use manual epochs instead
// let mut block_number: u64 = 0;
let stake: u64 = 1;
add_network(&mut deps.storage, netuid, tempo, 0);
set_max_allowed_uids(&mut deps.storage, netuid, n);
set_weights_set_rate_limit(&mut deps.storage, netuid, 0);
set_max_registrations_per_block(&mut deps.storage, netuid, n);
set_target_registrations_per_interval(&mut deps.storage, netuid, n);
set_min_allowed_weights(&mut deps.storage, netuid, 0);
set_max_weight_limit(&mut deps.storage, netuid, u16::MAX);
set_min_difficulty(&mut deps.storage, netuid, 1_000);
set_difficulty(&mut deps.storage, netuid, 1_000);
// === Register [validator1, validator2, server1, server2]
for key in 0..n as u64 {
add_balance_to_coldkey_account(&Addr::unchecked((1000 + key).to_string()), stake);
let (nonce, work): (u64, Vec<u8>) = create_work_for_block_number(
&deps.storage,
netuid,
env.block.height,
key * 1_000_000,
Addr::unchecked((1000 + key).to_string()).as_str(),
);
pow_register_ok_neuron(
deps.as_mut(),
env.clone(),
netuid,
env.block.height,
nonce,
work,
Addr::unchecked((1000 + key).to_string()).as_str(),
Addr::unchecked((1000 + key).to_string()).as_str(),
)
.unwrap();
increase_stake_on_coldkey_hotkey_account(
&mut deps.storage,
&Addr::unchecked((1000 + key).to_string()),
&Addr::unchecked((1000 + key).to_string()),
stake,
);
}
assert_eq!(get_subnetwork_n(&deps.storage, netuid), n);
// === Issue validator permits
set_max_allowed_validators(&mut deps.storage, netuid, n);
assert_eq!(get_max_allowed_validators(&deps.storage, netuid), n);
epoch(
&mut deps.storage,
&deps.api,
netuid,
1_000_000_000,
env.block.height,
)
.unwrap(); // run first epoch to set allowed validators
step_block(deps.as_mut(), &mut env).unwrap(); // run to next block to ensure weights are set on nodes after their registration block
// === Set weights [val1->srv1: 2/3, val1->srv2: 1/3, val2->srv1: 2/3, val2->srv2: 1/3, srv1->srv1: 1, srv2->srv2: 1]
for uid in 0..(n / 2) as u64 {
set_weights(
deps.as_mut(),
env.clone(),
Addr::unchecked((1000 + uid).to_string()).as_str(),
netuid,
((n / 2)..n).collect(),
vec![2 * (u16::MAX / 3), u16::MAX / 3],
0,
)
.unwrap();
}
for uid in ((n / 2) as u64)..n as u64 {
set_weights(
deps.as_mut(),
env.clone(),
Addr::unchecked((1000 + uid).to_string()).as_str(),
netuid,
vec![uid as u16],
vec![u16::MAX],
0,
)
.unwrap();
}
if sparse {
epoch(
&mut deps.storage,
&deps.api,
netuid,
1_000_000_000,
env.block.height,
)
.unwrap();
} else {
epoch_dense(&mut deps.storage, netuid, 1_000_000_000, env.block.height);
}
/* current_block: 1; activity_cutoff: 5000
Last update: [1, 1, 1, 1]; Inactive: [false, false, false, false]; Block at registration: [0, 0, 0, 0]
S: [0.25, 0.25, 0.25, 0.25]; S (mask): [0.25, 0.25, 0.25, 0.25]; S (mask+norm): [0.25, 0.25, 0.25, 0.25]
validator_permits: [true, true, true, true]; max_allowed_validators: 4; new_validator_permits: [true, true, true, true]
W: [[(2, 65535), (3, 32768)], [(2, 65535), (3, 32768)], [(2, 65535)], [(3, 65535)]]
W (permit): [[(2, 65535), (3, 32768)], [(2, 65535), (3, 32768)], [(2, 65535)], [(3, 65535)]]
W (permit+diag): [[(2, 65535), (3, 32768)], [(2, 65535), (3, 32768)], [], []]
W (permit+diag+outdate): [[(2, 65535), (3, 32768)], [(2, 65535), (3, 32768)], [], []]
W (mask+norm): [[(2, 0.6666632756), (3, 0.3333367242)], [(2, 0.6666632756), (3, 0.3333367242)], [], []]
R (before): [0, 0, 0.3333316376, 0.166668362]
C: [0, 0, 0.6666632756, 0.3333367242]
W: [[(2, 0.6666632756), (3, 0.3333367242)], [(2, 0.6666632756), (3, 0.3333367242)], [], []]
Tv: [0.9999999998, 0.9999999998, 0, 0]
R (after): [0, 0, 0.3333316376, 0.166668362]
T: [0, 0, 1, 1]
I (=R): [0, 0, 0.6666632756, 0.3333367242]
B: [[], [], [], []]
B (outdatedmask): [[], [], [], []]
B (mask+norm): [[], [], [], []]
ฮB: [[(2, 0.1666658188), (3, 0.083334181)], [(2, 0.1666658188), (3, 0.083334181)], [], []]
ฮB (norm): [[(2, 0.5), (3, 0.5)], [(2, 0.5), (3, 0.5)], [], []]
emaB: [[(2, 0.5), (3, 0.5)], [(2, 0.5), (3, 0.5)], [], []]
D: [0.5, 0.5, 0, 0]
nE: [0.25, 0.25, 0.3333316378, 0.166668362]
E: [250000000, 250000000, 333331637, 166668361]
P: [0.25, 0.25, 0.3333316378, 0.166668362]
P (u16): [49151, 49151, 65535, 32767] */
// === Dereg server2 at uid3 (least emission) + register new key over uid3
let new_key: u64 = n as u64; // register a new key while at max capacity, which means the least incentive uid will be deregistered
let (nonce, work): (u64, Vec<u8>) = create_work_for_block_number(
&deps.storage,
netuid,
env.block.height,
0,
Addr::unchecked((1000 + new_key).to_string()).as_str(),
);
pow_register_ok_neuron(
deps.as_mut(),
env.clone(),
netuid,
env.block.height,
nonce,
work,
Addr::unchecked((1000 + new_key).to_string()).as_str(),
Addr::unchecked((1000 + new_key).to_string()).as_str(),
)
.unwrap();
let deregistered_uid: u16 = n - 1; // since uid=n-1 only recieved 1/3 of weight, it will get pruned first
assert_eq!(
Addr::unchecked((1000 + new_key).to_string()),
get_hotkey_for_net_and_uid(&deps.storage, netuid, deregistered_uid)
.expect("Not registered")
);
step_block(deps.as_mut(), &mut env).unwrap(); // run to next block to outdate weights and bonds set on deregistered uid
// === Update weights from only uid=0
set_weights(
deps.as_mut(),
env.clone(),
Addr::unchecked(1000.to_string()).as_str(),
netuid,
((n / 2)..n).collect(),
vec![2 * (u16::MAX / 3), u16::MAX / 3],
0,
)
.unwrap();
if sparse {
epoch(
&mut deps.storage,
&deps.api,
netuid,
1_000_000_000,
env.block.height,
)
.unwrap();
} else {
epoch_dense(&mut deps.storage, netuid, 1_000_000_000, env.block.height);
}
/* current_block: 2; activity_cutoff: 5000
Last update: [2, 1, 1, 1]; Inactive: [false, false, false, false]; Block at registration: [0, 0, 0, 1]
S: [0.3333333333, 0.3333333333, 0.3333333333, 0]
S (mask): [0.3333333333, 0.3333333333, 0.3333333333, 0]
S (mask+norm): [0.3333333333, 0.3333333333, 0.3333333333, 0]
validator_permits: [true, true, true, false]; max_allowed_validators: 4; new_validator_permits: [true, true, true, true]
W: [[(2, 65535), (3, 32768)], [(2, 65535), (3, 32768)], [(2, 65535)], [(3, 65535)]]
W (permit): [[(2, 65535), (3, 32768)], [(2, 65535), (3, 32768)], [(2, 65535)], [(3, 65535)]]
W (permit+diag): [[(2, 65535), (3, 32768)], [(2, 65535), (3, 32768)], [], []]
W (permit+diag+outdate): [[(2, 65535), (3, 32768)], [(2, 65535)], [], []]
W (mask+norm): [[(2, 0.6666632756), (3, 0.3333367242)], [(2, 1)], [], []]
R (before): [0, 0, 0.5555544249, 0.1111122412]
C: [0, 0, 0.6666632756, 0]
W: [[(2, 0.6666632756)], [(2, 0.6666632756)], [], []]
Tv: [0.6666632756, 0.6666632756, 0, 0]
R (after): [0, 0, 0.4444421832, 0]
T: [0, 0, 0.799997558, 0]
I (=R): [0, 0, 1, 0]
B: [[(2, 65535), (3, 65535)], [(2, 65535), (3, 65535)], [], []]
B (outdatedmask): [[(2, 65535), (3, 65535)], [(2, 65535)], [], []]
B (mask+norm): [[(2, 0.5), (3, 1)], [(2, 0.5)], [], []]
ฮB: [[(2, 0.2222210916)], [(2, 0.2222210916)], [], []]
ฮB (norm): [[(2, 0.5)], [(2, 0.5)], [], []]
emaB: [[(2, 0.5), (3, 1)], [(2, 0.5)], [], []]
emaB (max-upscale): [[(2, 1), (3, 1)], [(2, 1)], [], []]
D: [0.5, 0.5, 0, 0]
nE: [0.25, 0.25, 0.5, 0]
E: [250000000, 250000000, 500000000, 0]
P: [0.25, 0.25, 0.5, 0]
P (u16): [32767, 32767, 65535, 0] */
let bonds = get_bonds(&mut deps.storage, netuid);
assert_eq!(get_dividends_for_uid(&mut deps.storage, netuid, 0), 32767); // Note D = floor(0.5 * 65_535)
assert_eq!(
get_emission_for_uid(&mut deps.storage, netuid, 0),
250000000
); // Note E = 0.5 * 0.5 * 1_000_000_000 = 249311245
assert_eq!(bonds[0][2], I32F32::from_num(65_535)); // floor(0.5*(2^16-1))/(2^16-1), then max-upscale
assert_eq!(bonds[0][3], I32F32::from_num(65_535)); // only uid0 has updated weights for new reg
}
// Test the zero emission handling and fallback under zero effective weight conditions, to ensure non-zero effective emission.
#[test]
fn test_zero_weights() {
let (mut deps, mut env) = instantiate_contract();
let sparse: bool = true;
let n: u16 = 2;
let netuid: u16 = 2;
let tempo: u16 = u16::MAX - 1; // high tempo to skip automatic epochs in on_initialize, use manual epochs instead
// let mut block_number: u64 = en;
let stake: u64 = 1;
add_network(&mut deps.storage, netuid, tempo, 0);
set_max_allowed_uids(&mut deps.storage, netuid, n);
set_weights_set_rate_limit(&mut deps.storage, netuid, 0);
set_max_registrations_per_block(&mut deps.storage, netuid, n);
set_target_registrations_per_interval(&mut deps.storage, netuid, n);
set_min_allowed_weights(&mut deps.storage, netuid, 0);
set_max_weight_limit(&mut deps.storage, netuid, u16::MAX);
set_min_difficulty(&mut deps.storage, netuid, 1_000);
set_difficulty(&mut deps.storage, netuid, 1_000);
// === Register [validator, server]
for key in 0..n as u64 {
let (nonce, work): (u64, Vec<u8>) = create_work_for_block_number(
&mut deps.storage,
netuid,
env.block.height,
key * 1_000_000,
Addr::unchecked((1000 + key).to_string()).as_str(),
);
pow_register_ok_neuron(
deps.as_mut(),
env.clone(),
netuid,
env.block.height,
nonce,
work,
Addr::unchecked((1000 + key).to_string()).as_str(),
Addr::unchecked((1000 + key).to_string()).as_str(),
)
.unwrap();
}
for validator in 0..(n / 2) as u64 {
add_balance_to_coldkey_account(&Addr::unchecked((1000 + validator).to_string()), stake);
increase_stake_on_coldkey_hotkey_account(
&mut deps.storage,
&Addr::unchecked((1000 + validator).to_string()),
&Addr::unchecked((1000 + validator).to_string()),
stake,
);
}
assert_eq!(get_subnetwork_n(&deps.storage, netuid), n);
// === No weights
if sparse {
epoch(
&mut deps.storage,
&deps.api,
netuid,
1_000_000_000,
env.block.height,
)
.unwrap();
} else {
epoch_dense(&mut deps.storage, netuid, 1_000_000_000, env.block.height);
}
/* current_block: 0; activity_cutoff: 5000; Last update: [0, 0]; Inactive: [false, false]
S: [1, 0]; S (mask): [1, 0]; S (mask+norm): [1, 0]; Block at registration: [0, 0]
W: [[], []]; W (diagmask): [[], []]; W (diag+outdatemask): [[], []]; W (mask+norm): [[], []]
R: [0, 0]; W (threshold): [[], []]; T: [0, 0]; C: [0.006693358, 0.006693358]; I: [0, 0]
B: [[], []]; B (outdatedmask): [[], []]; B (mask+norm): [[], []];
ฮB: [[], []]; ฮB (norm): [[], []]; emaB: [[], []]; D: [0, 0]
E: [1000000000, 0]; P: [1, 0] */
for validator in 0..(n / 2) as u16 {
assert_eq!(
get_emission_for_uid(&deps.storage, netuid, validator),
1000000000
); // Note E = 1 * 1_000_000_000
}
for server in (n / 2)..n as u16 {
assert_eq!(get_emission_for_uid(&deps.storage, netuid, server), 0);
// no stake
}
step_block(deps.as_mut(), &mut env).unwrap(); // run to next block to ensure weights are set on nodes after their registration block
// === Self-weights only: set weights [srv->srv: 1]
for uid in ((n / 2) as u64)..n as u64 {
set_weights(
deps.as_mut(),
env.clone(),
Addr::unchecked((1000 + uid).to_string()).as_str(),
netuid,
vec![uid as u16],
vec![u16::MAX],
0,
)
.unwrap();
}
if sparse {
epoch(
&mut deps.storage,
&deps.api,
netuid,
1_000_000_000,
env.block.height,
)
.unwrap();
} else {
epoch_dense(&mut deps.storage, netuid, 1_000_000_000, env.block.height);
}
/* current_block: 1; activity_cutoff: 5000; Last update: [0, 1]; Inactive: [false, false]
S: [1, 0]; S (mask): [1, 0]; S (mask+norm): [1, 0]; Block at registration: [0, 0]
W: [[], [(1, 1)]]
W (diagmask): [[], []]; W (diag+outdatemask): [[], []]; W (mask+norm): [[], []]
R: [0, 0]; W (threshold): [[], []]; T: [0, 0]; C: [0.006693358, 0.006693358]; I: [0, 0]
B: [[], []]: B (outdatedmask): [[], []]; B (mask+norm): [[], []]
ฮB: [[], []]; ฮB (norm): [[], []]; emaB: [[], []]; D: [0, 0]
E: [1000000000, 0]; P: [1, 0] */
for validator in 0..(n / 2) as u16 {
assert_eq!(
get_emission_for_uid(&deps.storage, netuid, validator),
1000000000
); // Note E = 1 * 1_000_000_000
}
for server in (n / 2)..n as u16 {
assert_eq!(get_emission_for_uid(&deps.storage, netuid, server), 0);
// no stake
}
step_block(deps.as_mut(), &mut env).unwrap();
// === Set weights [val->srv: 1/(n/2)]
for uid in 0..(n / 2) as u64 {
set_weights(
deps.as_mut(),
env.clone(),
Addr::unchecked((1000 + uid).to_string()).as_str(),
netuid,
((n / 2)..n).collect(),
vec![u16::MAX / (n / 2); (n / 2) as usize],
0,
)
.unwrap();
}
// === Outdate weights by reregistering servers
for new_key in n..n + (n / 2) {
// register a new key while at max capacity, which means the least emission uid will be deregistered
let (nonce, work): (u64, Vec<u8>) = create_work_for_block_number(
&mut deps.storage,
netuid,
env.block.height,
new_key as u64 * 1_000_000,
Addr::unchecked((1000 + new_key).to_string()).as_str(),
);
pow_register_ok_neuron(
deps.as_mut(),
env.clone(),
netuid,
env.block.height,
nonce,
work,
Addr::unchecked((1000 + new_key).to_string()).as_str(),
Addr::unchecked((1000 + new_key).to_string()).as_str(),
)
.unwrap();
}
if sparse {
epoch(
&mut deps.storage,
&deps.api,
netuid,
1_000_000_000,
env.block.height,
)
.unwrap();
} else {
epoch_dense(&mut deps.storage, netuid, 1_000_000_000, env.block.height);
}
/* current_block: 2; activity_cutoff: 5000; Last update: [2, 1]; Inactive: [false, false];
S: [1, 0]; S (mask): [1, 0]; S (mask+norm): [1, 0]; Block at registration: [0, 2];
W: [[(1, 1)], []]; W (diagmask): [[(1, 1)], []]; W (diag+outdatemask): [[], []]; W (mask+norm): [[], []];
R: [0, 0]; W (threshold): [[], []]; T: [0, 0]; C: [0.006693358, 0.006693358]; I: [0, 0];
B: [[], []]; B (outdatedmask): [[], []]; B (mask+norm): [[], []];
ฮB: [[], []]; ฮB (norm): [[], []]; emaB: [[], []]; D: [0, 0];
E: [1000000000, 0]; P: [1, 0] */
for validator in 0..(n / 2) as u16 {
assert_eq!(
get_emission_for_uid(&deps.storage, netuid, validator),
1000000000
); // Note E = 1 * 1_000_000_000
}
for server in (n / 2)..n as u16 {
assert_eq!(get_emission_for_uid(&deps.storage, netuid, server), 0);
// no stake
}
step_block(deps.as_mut(), &mut env).unwrap();
// === Set new weights [val->srv: 1/(n/2)] to check that updated weights would produce non-zero incentive
for uid in 0..(n / 2) as u64 {
set_weights(
deps.as_mut(),
env.clone(),
Addr::unchecked((1000 + uid).to_string()).as_str(),
netuid,
((n / 2)..n).collect(),
vec![u16::MAX / (n / 2); (n / 2) as usize],
0,
)
.unwrap();
}
if sparse {
epoch(
&mut deps.storage,
&deps.api,
netuid,
1_000_000_000,
env.block.height,
)
.unwrap();
} else {
epoch_dense(&mut deps.storage, netuid, 1_000_000_000, env.block.height);
}
/* current_block: 3; activity_cutoff: 5000; Last update: [3, 1]; Inactive: [false, false];
S: [1, 0]; S (mask): [1, 0]; S (mask+norm): [1, 0]; Block at registration: [0, 2];
W: [[(1, 1)], []]; W (diagmask): [[(1, 1)], []]; W (diag+outdatemask): [[(1, 1)], []]; W (mask+norm): [[(1, 1)], []];
R: [0, 1]; W (threshold): [[(1, 1)], []]; T: [0, 1]; C: [0.006693358, 0.9933076561]; I: [0, 1];
B: [[], []]; B (outdatedmask): [[], []]; B (mask+norm): [[], []];
ฮB: [[(1, 1)], []]; ฮB (norm): [[(1, 1)], []]; emaB: [[(1, 1)], []]; D: [1, 0]; emaB (max-upscale): [[(1, 1)], []]
E: [500000000, 500000000]; P: [0.5, 0.5] */
for validator in 0..n as u16 {
assert_eq!(
get_emission_for_uid(&deps.storage, netuid, validator),
1000000000 / (n as u64)
); // Note E = 1/2 * 1_000_000_000
}
}
// Test that epoch assigns validator permits to highest stake uids, varies uid interleaving and stake values.
#[test]
#[cfg(not(tarpaulin))]
fn test_validator_permits() {
let netuid: u16 = 2;
let tempo: u16 = u16::MAX - 1; // high tempo to skip automatic epochs in on_initialize, use manual epochs instead
for interleave in 0..3 {
for (network_n, validators_n) in vec![(2, 1), (4, 2), (8, 4)] {
for assignment in 0..=1 {
let (validators, servers) = distribute_nodes(
validators_n as usize,
network_n as usize,
interleave as usize,
);
let correct: bool = true;
let mut stake: Vec<u64> = vec![0; network_n];
for validator in &validators {
stake[*validator as usize] = match assignment {
1 => *validator as u64 + network_n as u64,
_ => 1,
};
}
for server in &servers {
stake[*server as usize] = match assignment {
1 => *server as u64,
_ => 0,
};
}
let (mut deps, mut env) = instantiate_contract();
let block_number: u64 = env.block.height;
add_network(&mut deps.storage, netuid, tempo, 0);
set_min_difficulty(&mut deps.storage, netuid, 1_000);
set_difficulty(&mut deps.storage, netuid, 1_000);
set_max_allowed_uids(&mut deps.storage, netuid, network_n as u16);
assert_eq!(
get_max_allowed_uids(&deps.storage, netuid),
network_n as u16
);
set_max_registrations_per_block(&mut deps.storage, netuid, network_n as u16);
set_target_registrations_per_interval(&mut deps.storage, netuid, network_n as u16);
// === Register [validator1, validator2, server1, server2]
for key in 0..network_n as u64 {
add_balance_to_coldkey_account(
&Addr::unchecked((1000 + key).to_string()),
stake[key as usize],
);
let (nonce, work): (u64, Vec<u8>) = create_work_for_block_number(
&mut deps.storage,
netuid,
block_number,
key * 1_000_000,
&Addr::unchecked((1000 + key).to_string()).as_str(),
);
pow_register_ok_neuron(
deps.as_mut(),
env.clone(),
netuid,
block_number,
nonce,
work,
Addr::unchecked((1000 + key).to_string()).as_str(),
Addr::unchecked((1000 + key).to_string()).as_str(),
)
.unwrap();
increase_stake_on_coldkey_hotkey_account(
&mut deps.storage,
&Addr::unchecked((1000 + key).to_string()),
&Addr::unchecked((1000 + key).to_string()),
stake[key as usize],
);
}
assert_eq!(get_subnetwork_n(&deps.storage, netuid), network_n as u16);
// === Issue validator permits
set_max_allowed_validators(&mut deps.storage, netuid, validators_n as u16);
assert_eq!(
get_max_allowed_validators(&deps.storage, netuid),
validators_n as u16
);
epoch(
&mut deps.storage,
&deps.api,
netuid,
1_000_000_000,
env.block.height,
)
.unwrap(); // run first epoch to set allowed validators
for validator in &validators {
assert_eq!(
correct,
get_validator_permit_for_uid(&deps.storage, netuid, *validator)
);
}
for server in &servers {
assert_eq!(
!correct,
get_validator_permit_for_uid(&deps.storage, netuid, *server)
);
}
// === Increase server stake above validators
for server in &servers {
add_balance_to_coldkey_account(
&Addr::unchecked((1000 + (*server as u64)).to_string()),
2 * network_n as u64,
);
increase_stake_on_coldkey_hotkey_account(
&mut deps.storage,
&Addr::unchecked((1000 + (*server as u64)).to_string()),
&Addr::unchecked((1000 + (*server as u64)).to_string()),
2 * network_n as u64,
);
}
// === Update validator permits
step_block(deps.as_mut(), &mut env).unwrap();
epoch(
&mut deps.storage,
&deps.api,
netuid,
1_000_000_000,
env.block.height,
)
.unwrap();
// === Check that servers now own permits instead of the validator uids
for validator in &validators {
assert_eq!(
!correct,
get_validator_permit_for_uid(&deps.storage, netuid, *validator)
);
}
for server in &servers {
assert_eq!(
correct,
get_validator_permit_for_uid(&deps.storage, netuid, *server)
);
}
drop(deps);
drop(env);
}
}
}
}
#[test]
fn test_graph_with_gas_sim() {
let netuid: u16 = 2;
let network_n: u16 = 512;
let validators_n: u16 = 64;
// let max_stake_per_validator: u64 = 328_125_000_000_000; // 21_000_000_000_000_000 / 64
let epochs: u16 = 3;
log::info!("test_{network_n:?}_graph ({validators_n:?} validators)");
for interleave in 0..3 {
for server_self in vec![false, true] {
// server-self weight off/on
let (validators1, servers1) = distribute_nodes(
validators_n as usize,
network_n as usize,
interleave as usize,
);
let validators = &validators1;
let servers = &servers1;
let server: usize = servers[0] as usize;
let validator: usize = validators[0] as usize;
let (mut deps, mut env) = instantiate_contract();
let gas = deps.storage.gas_used.borrow();
println!(
"before total {:?} gas {:?} write {:?} read {:?}",
gas.total, gas.last, gas.write_cnt, gas.read_cnt
);
drop(gas);
// fn init_run_epochs(
// mut deps: DepsMut,
// mut env: &mut Env,
// netuid: u16,
// n: u16,
// validators: &Vec<u16>,
// servers: &Vec<u16>,
// epochs: u16,
// stake_per_validator: u64,
// server_self: bool,
// input_stake: &Vec<u64>,
// use_input_stake: bool,
// input_weights: &Vec<Vec<(u16, u16)>>,
// use_input_weights: bool,
// random_weights: bool,
// random_seed: u64,
// sparse: bool,
// ) {
// init_run_epochs(
// deps.as_mut(),
// &mut env,
// netuid,
// network_n,
// &validators,
// &servers,
// epochs,
// 1,
// server_self,
// &vec![],
// false,
// &vec![],
// false,
// true,
// interleave as u64,
// false,
// );
// netuid: u16,
let n = network_n;
// validators: &Vec<u16>,
// servers: &Vec<u16>,
// epochs: u16,
let stake_per_validator = 1;
// server_self: bool,
let input_stake: &Vec<u64> = &vec![];
let use_input_stake = false;
let input_weights: &Vec<Vec<(u16, u16)>> = &vec![];
let use_input_weights = false;
let random_weights = true;
let random_seed = interleave;
let sparse = false;
// let (mut deps, mut env) = instantiate_contract();
// === Create the network
add_network(&mut deps.storage, netuid, u16::MAX - 1, 0); // set higher tempo to avoid built-in epoch, then manual epoch instead
// === Register uids
set_max_allowed_uids(&mut deps.storage, netuid, n);
for key in 0..n {
let stake: u64;
if use_input_stake {
stake = input_stake[key as usize];
} else {
stake = if validators.contains(&key) {
stake_per_validator
} else {
0
}; // only validators receive stake
}
// let stake: u64 = 1; // alternative test: all nodes receive stake, should be same outcome, except stake
add_balance_to_coldkey_account(&Addr::unchecked((1000 + key).to_string()), stake);
append_neuron(
&mut deps.storage,
&mut deps.api,
netuid,
&(Addr::unchecked((1000 + key).to_string())),
0,
)
.unwrap();
increase_stake_on_coldkey_hotkey_account(
&mut deps.storage,
&Addr::unchecked((1000 + key).to_string()),
&Addr::unchecked((1000 + key).to_string()),
stake as u64,
);
}
assert_eq!(get_subnetwork_n(&mut deps.storage, netuid), n);
// === Issue validator permits
set_max_allowed_validators(&mut deps.storage, netuid, validators.len() as u16);
assert_eq!(
get_max_allowed_validators(&mut deps.storage, netuid),
validators.len() as u16
);
epoch(
&mut deps.storage,
&mut deps.api,
netuid,
1_000_000_000,
env.block.height,
)
.unwrap(); // run first epoch to set allowed validators
step_block(deps.as_mut(), &mut env).unwrap(); // run to next block to ensure weights are set on nodes after their registration block
// === Set weights
let mut rng = StdRng::seed_from_u64(random_seed); // constant seed so weights over multiple runs are equal
let range = Uniform::new(0, u16::MAX);
let mut weights: Vec<u16> = vec![u16::MAX / n; servers.len() as usize];
for uid in validators {
if random_weights {
weights = (0..servers.len()).map(|_| rng.sample(&range)).collect();
weights = normalize_weights(weights);
// assert_eq!(weights.iter().map(|x| *x as u64).sum::<u64>(), u16::MAX as u64); // normalized weight sum not always u16::MAX
}
if use_input_weights {
let sparse_weights = input_weights[*uid as usize].clone();
weights = sparse_weights.iter().map(|(_, w)| *w).collect();
let srvs: Vec<u16> = sparse_weights.iter().map(|(s, _)| *s).collect();
let result = set_weights(
deps.as_mut(),
env.clone(),
(1000 + uid).to_string().as_str(),
netuid,
srvs.clone(),
weights.clone(),
0,
);
assert_eq!(result.is_ok(), true);
// let msg = ExecuteMsg::SetWeights {
// netuid,
// dests: srvs.clone(),
// weights: weights.clone(),
// version_key: 0,
// };
// let info = mock_info((1000 + uid).to_string().as_str(), &[]);
// let res = execute(deps.branch(), env.clone(), info, msg);
// assert_eq!(res.is_ok(), true);
} else {
let result = set_weights(
deps.as_mut(),
env.clone(),
(1000 + uid).to_string().as_str(),
netuid,
servers.clone(),
weights.clone(),
0,
);
assert_eq!(result.is_ok(), true);
// let msg = ExecuteMsg::SetWeights {
// netuid,
// dests: servers.clone(),
// weights: weights.clone(),
// version_key: 0,
// };
// let info = mock_info((1000 + uid).to_string().as_str(), &[]);
// let res = execute(deps.branch(), env.clone(), info, msg);
// assert_eq!(res.is_ok(), true);
}
}
for uid in servers {
if server_self {
let result = set_weights(
deps.as_mut(),
env.clone(),
(1000 + uid).to_string().as_str(),
netuid,
vec![*uid as u16],
vec![u16::MAX],
0,
);
assert_eq!(result.is_ok(), true);
// let msg = ExecuteMsg::SetWeights {
// netuid,
// dests: vec![*uid as u16],
// weights: vec![u16::MAX],
// version_key: 0,
// }; // server self-weight
// let info = mock_info((1000 + uid).to_string().as_str(), &[]);
// let res = execute(deps.branch(), env.clone(), info, msg);
// assert_eq!(res.is_ok(), true);
}
}
// === Run the epochs.
for n in 0..epochs {
println!("Start {n} epoch");
let start = Instant::now();
let gas = deps.storage.gas_used.borrow();
let gas_total = gas.total;
let gas_last = gas.last;
let gas_write_cnt = gas.write_cnt;
let gas_read_cnt = gas.read_cnt;
println!(
"before epoch {:?} total {:?} gas {:?} write {:?} read {:?}",
n, gas.total, gas.last, gas.write_cnt, gas.read_cnt
);
drop(gas);
if sparse {
epoch(
&mut deps.storage,
&mut deps.api,
netuid,
1_000_000_000,
env.block.height,
)
.unwrap();
} else {
epoch_dense(&mut deps.storage, netuid, 1_000_000_000, env.block.height);
}
let gas = deps.storage.gas_used.borrow();
println!(
"after epoch {:?} total {:?} gas {:?} write {:?} read {:?}",
n, gas.total, gas.last, gas.write_cnt, gas.read_cnt
);
println!(
"after epoch {:?} total {:?} gas {:?} write {:?} read {:?}",
n, gas_total, gas_last, gas_write_cnt, gas_read_cnt
);
println!(
"diff epoch {:?} gas {:?} write {:?} read {:?}",
n,
gas.total - gas_total,
gas.write_cnt - gas_write_cnt,
gas.read_cnt - gas_read_cnt
);
drop(gas);
let duration = start.elapsed();
println!(
"Time elapsed in (sparse={sparse}) epoch() is: {:?}",
duration
);
}
// let bonds = get_bonds(&deps.storage, netuid );
// for (uid, node) in vec![ (validators[0], "validator"), (servers[0], "server") ] {
// log::info!("\n{node}" );
// uid_stats(netuid, uid);
// log::info!("bonds: {:?} (on validator), {:?} (on server)", bonds[uid as usize][0], bonds[uid as usize][servers[0] as usize]);
// }
let gas = deps.storage.gas_used.borrow();
println!(
"after total {:?} gas {:?} write {:?} read {:?}",
gas.total, gas.last, gas.write_cnt, gas.read_cnt
);
drop(gas);
let bonds = get_bonds(&deps.storage, netuid);
for uid in validators {
// assert_eq!(
// get_total_stake_for_hotkey(
// &deps.storage,
// &Addr::unchecked((1000 + uid).to_string()),
// ),
// max_stake_per_validator
// );
assert_eq!(get_rank_for_uid(&deps.storage, netuid, *uid), 0);
assert_eq!(get_trust_for_uid(&deps.storage, netuid, *uid), 0);
assert_eq!(get_consensus_for_uid(&deps.storage, netuid, *uid), 0);
assert_eq!(get_incentive_for_uid(&deps.storage, netuid, *uid), 0);
// assert_eq!(get_dividends_for_uid(&deps.storage, netuid, *uid), 1023); // Note D = floor(1 / 64 * 65_535) = 1023
// assert_eq!(get_emission_for_uid(&deps.storage, netuid, *uid), 7812500); // Note E = 0.5 / 200 * 1_000_000_000 = 7_812_500
assert_eq!(bonds[*uid as usize][validator], 0.0);
// assert_eq!(bonds[*uid as usize][server], I32F32::from_num(65_535));
// Note B_ij = floor(1 / 64 * 65_535) / 65_535 = 1023 / 65_535, then max-upscaled to 65_535
}
for uid in servers {
assert_eq!(
get_total_stake_for_hotkey(
&deps.storage,
&Addr::unchecked((1000 + uid).to_string()),
),
0
);
// assert_eq!(get_rank_for_uid(&deps.storage, netuid, *uid), 146); // Note R = floor(1 / (512 - 64) * 65_535) = 146
// assert_eq!(get_trust_for_uid(&deps.storage, netuid, *uid), 65535);
// assert_eq!(get_consensus_for_uid(&deps.storage, netuid, *uid), 146); // Note C = floor(1 / (512 - 64) * 65_535) = 146
// assert_eq!(get_incentive_for_uid(&deps.storage, netuid, *uid), 146); // Note I = floor(1 / (512 - 64) * 65_535) = 146
assert_eq!(get_dividends_for_uid(&deps.storage, netuid, *uid), 0);
// assert_eq!(get_emission_for_uid(&deps.storage, netuid, *uid), 1116071); // Note E = floor(0.5 / (512 - 64) * 1_000_000_000) = 1_116_071
assert_eq!(bonds[*uid as usize][validator], 0.0);
assert_eq!(bonds[*uid as usize][server], 0.0);
}
drop(deps);
drop(env);
}
}
}
// // Map the retention graph for consensus guarantees with an single epoch on a graph with 512 nodes, of which the first 64 are validators, the graph is split into a major and minor set, each setting specific weight on itself and the complement on the other.
// //
// // ```import torch
// // import matplotlib.pyplot as plt
// // from matplotlib.pyplot import cm
// // %matplotlib inline
// //
// // with open('finney_consensus_0.4.txt') as f: # test output saved to finney_consensus.txt
// // retention_map = eval(f.read())
// //
// // major_ratios = {}
// // avg_weight_devs = {}
// // for major_stake, major_weight, minor_weight, avg_weight_dev, major_ratio in retention_map:
// // major_stake = f'{major_stake:.2f}'
// // maj, min = int(round(50 * major_weight)), int(round(50 * minor_weight))
// // avg_weight_devs.setdefault(major_stake, torch.zeros((51, 51)))
// // avg_weight_devs[major_stake][maj][min] = avg_weight_dev
// // major_ratios.setdefault(major_stake, torch.zeros((51, 51)))
// // major_ratios[major_stake][maj][min] = major_ratio
// //
// // _x = torch.linspace(0, 1, 51); _y = torch.linspace(0, 1, 51)
// // x, y = torch.meshgrid(_x, _y, indexing='ij')
// //
// // fig = plt.figure(figsize=(6, 6), dpi=70); ax = fig.gca()
// // ax.set_xticks(torch.arange(0, 1, 0.05)); ax.set_yticks(torch.arange(0, 1., 0.05))
// // ax.set_xticklabels([f'{_:.2f}'[1:] for _ in torch.arange(0, 1., 0.05)])
// // plt.grid(); plt.rc('grid', linestyle="dotted", color=[0.85, 0.85, 0.85])
// //
// // isolate = ['0.60']; stakes = [0.51, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 0.99]
// // colors = cm.viridis(torch.linspace(0, 1, len(stakes) + 1))
// // for i, stake in enumerate(stakes):
// // contours = plt.contour(x, y, major_ratios[f'{stake:.2f}'], levels=[0., stake], colors=[colors[i + 1]])
// // if f'{stake:.2f}' in isolate:
// // contours.collections[1].set_linewidth(3)
// // plt.clabel(contours, inline=True, fontsize=10)
// //
// // plt.title(f'Major emission [$stake_{{maj}}=emission_{{maj}}$ retention lines]')
// // plt.ylabel('Minor self-weight'); plt.xlabel('Major self-weight'); plt.show()
// // ```
// // #[test]
// fn _map_consensus_guarantees() {
// let netuid: u16 = 1;
// let network_n: u16 = 512;
// let validators_n: u16 = 64;
// let epochs: u16 = 1;
// let interleave = 0;
// let weight_stddev: I32F32 = fixed(0.4);
// println!("[");
// for _major_stake in vec![0.51, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 0.99] {
// let major_stake: I32F32 = I32F32::from_num(_major_stake);
// for _major_weight in 0..51 {
// let major_weight: I32F32 = I32F32::from_num(50 - _major_weight) / I32F32::from_num(50);
// for _minor_weight in 0..51 {
// let minor_weight: I32F32 =
// I32F32::from_num(50 - _minor_weight) / I32F32::from_num(50);
// let (
// validators,
// servers,
// major_validators,
// minor_validators,
// major_servers,
// minor_servers,
// stake,
// weights,
// avg_weight_dev,
// ) = split_graph(
// major_stake,
// major_weight,
// minor_weight,
// weight_stddev,
// validators_n as usize,
// network_n as usize,
// interleave as usize,
// );
// new_test_ext().execute_with(|| {
// init_run_epochs(netuid, network_n, &validators, &servers, epochs, 1, true, &stake, true, &weights, true, false, 0, true);
// let mut major_emission: I64F64 = I64F64::from_num(0);
// let mut minor_emission: I64F64 = I64F64::from_num(0);
// for set in vec![major_validators, major_servers] {
// for uid in set {
// major_emission += I64F64::from_num(SubtensorModule::get_emission_for_uid( netuid, uid ));
// }
// }
// for set in vec![minor_validators, minor_servers] {
// for uid in set {
// minor_emission += I64F64::from_num(SubtensorModule::get_emission_for_uid( netuid, uid ));
// }
// }
// let major_ratio: I32F32 = I32F32::from_num(major_emission / (major_emission + minor_emission));
// println!("[{major_stake}, {major_weight:.2}, {minor_weight:.2}, {avg_weight_dev:.3}, {major_ratio:.3}], ");
// });
// }
// }
// }
// println!("]");
// }
cw-cyber/contracts/cybernet/src/tests/epoch.rs
ฯ 0.0%
use Instant;
use mock_info;
use ;
use ;
use ;
use ;
use crate;
use crate;
use crateExecuteMsg;
use cratecreate_work_for_block_number;
use crate;
use crate;
use crate;
use crateepoch_dense;
use crate;
use crate;
// Normalizes (sum to 1 except 0) the input vector directly in-place.
// Inplace normalize the passed positive integer weights so that they sum to u16 max value.
// Return as usize an I32F32 ratio of a usize input, avoiding the 0% and 100% extremes.
// Box-Muller Transform converting two uniform random samples to a normal random sample.
// Returns validators and servers uids with either blockwise, regular, or random interleaving.
// Generate a random graph that is split into a major and minor set, each setting specific weight on itself and the complement on the other.
// Test consensus guarantees with an epoch on a graph with 4096 nodes, of which the first 128 are validators, the graph is split into a major and minor set, each setting specific weight on itself and the complement on the other. Asserts that the major emission ratio >= major stake ratio.
// #[test]
// fn test_consensus_guarantees() {
// let netuid: u16 = 0;
// let network_n: u16 = 512;
// let validators_n: u16 = 64;
// let epochs: u16 = 1;
// let interleave = 2;
// log::info!("test_consensus_guarantees ({network_n:?}, {validators_n:?} validators)");
// for (major_stake, major_weight, minor_weight, weight_stddev) in vec![
// (0.51, 1., 1., 0.001),
// (0.51, 0.03, 0., 0.001),
// (0.51, 0.51, 0.49, 0.001),
// (0.51, 0.51, 1., 0.001),
// (0.51, 0.61, 0.8, 0.1),
// (0.6, 0.67, 0.65, 0.2),
// (0.6, 0.74, 0.77, 0.4),
// (0.6, 0.76, 0.8, 0.4),
// (0.6, 0.76, 1., 0.4),
// (0.6, 0.92, 1., 0.4),
// (0.6, 0.94, 1., 0.4),
// (0.65, 0.78, 0.85, 0.6),
// (0.7, 0.81, 0.85, 0.8),
// (0.7, 0.83, 0.85, 1.),
// ] {
// let (
// validators,
// servers,
// major_validators,
// minor_validators,
// major_servers,
// minor_servers,
// stake,
// weights,
// _avg_weight_dev,
// ) = split_graph(
// fixed(major_stake),
// fixed(major_weight),
// fixed(minor_weight),
// fixed(weight_stddev),
// validators_n as usize,
// network_n as usize,
// interleave as usize,
// );
// new_test_ext().execute_with(|| {
// init_run_epochs(
// netuid,
// network_n,
// &validators,
// &servers,
// epochs,
// 1,
// true,
// &stake,
// true,
// &weights,
// true,
// false,
// 0,
// false,
// );
// let mut major_emission: I64F64 = I64F64::from_num(0);
// let mut minor_emission: I64F64 = I64F64::from_num(0);
// for set in vec![major_validators, major_servers] {
// for uid in set {
// major_emission +=
// I64F64::from_num(get_emission_for_uid(netuid, uid));
// }
// }
// for set in vec![minor_validators, minor_servers] {
// for uid in set {
// minor_emission +=
// I64F64::from_num(get_emission_for_uid(netuid, uid));
// }
// }
// let major_ratio: I32F32 =
// I32F32::from_num(major_emission / (major_emission + minor_emission));
// assert!(major_stake <= major_ratio);
// });
// }
// }
// Test an epoch on an empty graph.
// #[test]
// fn test_overflow() {
// new_test_ext().execute_with(|| {
// log::info!("test_overflow:");
// let netuid: u16 = 1;
// add_network(netuid, 0, 0);
// set_max_allowed_uids(netuid, 3);
// increase_stake_on_coldkey_hotkey_account(
// Addr::unchecked(1000.to_string()),
// Addr::unchecked(1000.to_string()),
// 10,
// );
// increase_stake_on_coldkey_hotkey_account(
// Addr::unchecked(1001.to_string()),
// Addr::unchecked(1001.to_string()),
// 10,
// );
// increase_stake_on_coldkey_hotkey_account(
// Addr::unchecked(1002.to_string()),
// Addr::unchecked(1002.to_string()),
// 10,
// );
// append_neuron(netuid, Addr::unchecked(1000.to_string()), 0);
// append_neuron(netuid, Addr::unchecked(1001.to_string()), 0);
// append_neuron(netuid, Addr::unchecked(1002.to_string()), 0);
// set_validator_permit_for_uid(0, 0, true);
// set_validator_permit_for_uid(0, 1, true);
// set_validator_permit_for_uid(0, 2, true);
// assert_ok!(set_weights(
// Addr::unchecked(1000.to_string())),
// netuid,
// vec![0, 1, 2],
// vec![u16::MAX / 3, u16::MAX / 3, u16::MAX],
// 0
// ));
// assert_ok!(set_weights(
// Addr::unchecked(1001.to_string())),
// netuid,
// vec![1, 2],
// vec![u16::MAX / 2, u16::MAX / 2],
// 0
// ));
// assert_ok!(set_weights(
// Addr::unchecked(1002.to_string())),
// netuid,
// vec![2],
// vec![u16::MAX],
// 0
// ));
// epoch(0, u64::MAX);
// });
// }
// Test an epoch on an empty graph.
// #[test]
// fn test_nill_epoch_subtensor() {
// new_test_ext().execute_with(|| {
// log::info!("test_nill_epoch:");
// epoch(0, 0);
// });
// }
// Test an epoch on a graph with a single item.
// Test an epoch on a graph with two items.
// Test an epoch on a graph with 512 nodes, of which the first 64 are validators setting non-self weights, and the rest servers setting only self-weights.
// Test an epoch on a graph with 512 nodes, of which the first 64 are validators setting random non-self weights, and the rest servers setting only self-weights.
// // Test an epoch on a graph with 4096 nodes, of which the first 256 are validators setting non-self weights, and the rest servers setting only self-weights.
// TODO revisit because commented also in subtensor repo and tests fails
// #[test]
// // Test an epoch_sparse on a graph with 16384 nodes, of which the first 512 are validators setting non-self weights, and the rest servers setting only self-weights.
// TODO revisit because commented also in subtensor repo and tests fails
// #[test]
// // Test bonds exponential moving average over a sequence of epochs.
// // Test that epoch masks out inactive stake of validators with outdated weights beyond activity cutoff.
// // Test that epoch masks out outdated weights and bonds of validators on deregistered servers.
// Test the zero emission handling and fallback under zero effective weight conditions, to ensure non-zero effective emission.
// Test that epoch assigns validator permits to highest stake uids, varies uid interleaving and stake values.
// // Map the retention graph for consensus guarantees with an single epoch on a graph with 512 nodes, of which the first 64 are validators, the graph is split into a major and minor set, each setting specific weight on itself and the complement on the other.
// //
// // ```import torch
// // import matplotlib.pyplot as plt
// // from matplotlib.pyplot import cm
// // %matplotlib inline
// //
// // with open('finney_consensus_0.4.txt') as f: # test output saved to finney_consensus.txt
// // retention_map = eval(f.read())
// //
// // major_ratios = {}
// // avg_weight_devs = {}
// // for major_stake, major_weight, minor_weight, avg_weight_dev, major_ratio in retention_map:
// // major_stake = f'{major_stake:.2f}'
// // maj, min = int(round(50 * major_weight)), int(round(50 * minor_weight))
// // avg_weight_devs.setdefault(major_stake, torch.zeros((51, 51)))
// // avg_weight_devs[major_stake][maj][min] = avg_weight_dev
// // major_ratios.setdefault(major_stake, torch.zeros((51, 51)))
// // major_ratios[major_stake][maj][min] = major_ratio
// //
// // _x = torch.linspace(0, 1, 51); _y = torch.linspace(0, 1, 51)
// // x, y = torch.meshgrid(_x, _y, indexing='ij')
// //
// // fig = plt.figure(figsize=(6, 6), dpi=70); ax = fig.gca()
// // ax.set_xticks(torch.arange(0, 1, 0.05)); ax.set_yticks(torch.arange(0, 1., 0.05))
// // ax.set_xticklabels([f'{_:.2f}'[1:] for _ in torch.arange(0, 1., 0.05)])
// // plt.grid(); plt.rc('grid', linestyle="dotted", color=[0.85, 0.85, 0.85])
// //
// // isolate = ['0.60']; stakes = [0.51, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 0.99]
// // colors = cm.viridis(torch.linspace(0, 1, len(stakes) + 1))
// // for i, stake in enumerate(stakes):
// // contours = plt.contour(x, y, major_ratios[f'{stake:.2f}'], levels=[0., stake], colors=[colors[i + 1]])
// // if f'{stake:.2f}' in isolate:
// // contours.collections[1].set_linewidth(3)
// // plt.clabel(contours, inline=True, fontsize=10)
// //
// // plt.title(f'Major emission [$stake_{{maj}}=emission_{{maj}}$ retention lines]')
// // plt.ylabel('Minor self-weight'); plt.xlabel('Major self-weight'); plt.show()
// // ```
// // #[test]
// fn _map_consensus_guarantees() {
// let netuid: u16 = 1;
// let network_n: u16 = 512;
// let validators_n: u16 = 64;
// let epochs: u16 = 1;
// let interleave = 0;
// let weight_stddev: I32F32 = fixed(0.4);
// println!("[");
// for _major_stake in vec![0.51, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95, 0.99] {
// let major_stake: I32F32 = I32F32::from_num(_major_stake);
// for _major_weight in 0..51 {
// let major_weight: I32F32 = I32F32::from_num(50 - _major_weight) / I32F32::from_num(50);
// for _minor_weight in 0..51 {
// let minor_weight: I32F32 =
// I32F32::from_num(50 - _minor_weight) / I32F32::from_num(50);
// let (
// validators,
// servers,
// major_validators,
// minor_validators,
// major_servers,
// minor_servers,
// stake,
// weights,
// avg_weight_dev,
// ) = split_graph(
// major_stake,
// major_weight,
// minor_weight,
// weight_stddev,
// validators_n as usize,
// network_n as usize,
// interleave as usize,
// );
// new_test_ext().execute_with(|| {
// init_run_epochs(netuid, network_n, &validators, &servers, epochs, 1, true, &stake, true, &weights, true, false, 0, true);
// let mut major_emission: I64F64 = I64F64::from_num(0);
// let mut minor_emission: I64F64 = I64F64::from_num(0);
// for set in vec![major_validators, major_servers] {
// for uid in set {
// major_emission += I64F64::from_num(SubtensorModule::get_emission_for_uid( netuid, uid ));
// }
// }
// for set in vec![minor_validators, minor_servers] {
// for uid in set {
// minor_emission += I64F64::from_num(SubtensorModule::get_emission_for_uid( netuid, uid ));
// }
// }
// let major_ratio: I32F32 = I32F32::from_num(major_emission / (major_emission + minor_emission));
// println!("[{major_stake}, {major_weight:.2}, {minor_weight:.2}, {avg_weight_dev:.3}, {major_ratio:.3}], ");
// });
// }
// }
// }
// println!("]");
// }