diff --git a/pallets/subtensor/src/math.rs b/pallets/subtensor/src/math.rs index 294dea1a02..437f5a7653 100644 --- a/pallets/subtensor/src/math.rs +++ b/pallets/subtensor/src/math.rs @@ -59,6 +59,30 @@ pub fn vec_max_upscale_to_u16( vec: &Vec ) -> Vec { } } +#[allow(dead_code)] +// Max-upscale u16 vector and convert to u16 so max_value = u16::MAX. Assumes u16 vector input. +pub fn vec_u16_max_upscale_to_u16( vec: &Vec ) -> Vec { + let vec_fixed: Vec = vec.iter().map(|e: &u16| I32F32::from_num( *e ) ).collect(); + vec_max_upscale_to_u16( &vec_fixed ) +} + +#[allow(dead_code)] +// Checks if u16 vector, when normalized, has a max value not greater than a u16 ratio max_limit. +pub fn check_vec_max_limited( vec: &Vec, max_limit: u16 ) -> bool { + let max_limit_fixed: I32F32 = I32F32::from_num( max_limit ) / I32F32::from_num( u16::MAX ); + let mut vec_fixed: Vec = vec.iter().map(|e: &u16| I32F32::from_num( *e ) ).collect(); + inplace_normalize( &mut vec_fixed ); + let max_value: Option<&I32F32> = vec_fixed.iter().max(); + match max_value { + Some(val) => { + return *val <= max_limit_fixed; + }, + None => { + return true; + } + } +} + #[allow(dead_code)] pub fn sum( x: &Vec ) -> I32F32 { x.iter().sum() } @@ -804,7 +828,7 @@ mod tests { assert_float_compare_64(va[i], vb[i], epsilon); } } - + fn assert_vec_compare_u16(va: &Vec, vb: &Vec) { assert!(va.len() == vb.len()); for i in 0..va.len(){ @@ -909,6 +933,83 @@ mod tests { assert_vec_compare_u16(&result, &target); } + #[test] + fn test_vec_u16_max_upscale_to_u16() { + let vector: Vec = vec![]; + let result: Vec = vec_u16_max_upscale_to_u16( &vector ); + assert_vec_compare_u16(&result, &vector); + let vector: Vec = vec![ 0 ]; + let result: Vec = vec_u16_max_upscale_to_u16( &vector ); + assert_vec_compare_u16(&result, &vector); + let vector: Vec = vec![ 0, 0 ]; + let result: Vec = vec_u16_max_upscale_to_u16( &vector ); + assert_vec_compare_u16(&result, &vector); + let vector: Vec = vec![ 1 ]; + let target: Vec = vec![ 65535 ]; + let result: Vec = vec_u16_max_upscale_to_u16( &vector ); + assert_vec_compare_u16(&result, &target); + let vector: Vec = vec![ 0, 1 ]; + let target: Vec = vec![ 0, 65535 ]; + let result: Vec = vec_u16_max_upscale_to_u16( &vector ); + assert_vec_compare_u16(&result, &target); + let vector: Vec = vec![ 65534 ]; + let target: Vec = vec![ 65535 ]; + let result: Vec = vec_u16_max_upscale_to_u16( &vector ); + assert_vec_compare_u16(&result, &target); + let vector: Vec = vec![ 65535 ]; + let target: Vec = vec![ 65535 ]; + let result: Vec = vec_u16_max_upscale_to_u16( &vector ); + assert_vec_compare_u16(&result, &target); + let vector: Vec = vec![ 65535, 65535 ]; + let target: Vec = vec![ 65535, 65535 ]; + let result: Vec = vec_u16_max_upscale_to_u16( &vector ); + assert_vec_compare_u16(&result, &target); + let vector: Vec = vec![ 0, 1, 65534 ]; + let target: Vec = vec![ 0, 1, 65535 ]; + let result: Vec = vec_u16_max_upscale_to_u16( &vector ); + assert_vec_compare_u16(&result, &target); + let vector: Vec = vec![ 0, 1, 2, 3, 4, 65533, 65535 ]; + let result: Vec = vec_u16_max_upscale_to_u16( &vector ); + assert_vec_compare_u16(&result, &vector); + } + + #[test] + fn test_check_vec_max_limited() { + let vector: Vec = vec![]; + let max_limit: u16 = 0; + assert!( check_vec_max_limited( &vector, max_limit ) ); + let vector: Vec = vec![]; + let max_limit: u16 = u16::MAX; + assert!( check_vec_max_limited( &vector, max_limit ) ); + let vector: Vec = vec![ u16::MAX ]; + let max_limit: u16 = u16::MAX; + assert!( check_vec_max_limited( &vector, max_limit ) ); + let vector: Vec = vec![ u16::MAX ]; + let max_limit: u16 = u16::MAX - 1; + assert!( !check_vec_max_limited( &vector, max_limit ) ); + let vector: Vec = vec![ u16::MAX ]; + let max_limit: u16 = 0; + assert!( !check_vec_max_limited( &vector, max_limit ) ); + let vector: Vec = vec![ 0 ]; + let max_limit: u16 = u16::MAX; + assert!( check_vec_max_limited( &vector, max_limit ) ); + let vector: Vec = vec![ 0, u16::MAX ]; + let max_limit: u16 = u16::MAX; + assert!( check_vec_max_limited( &vector, max_limit ) ); + let vector: Vec = vec![ 0, u16::MAX, u16::MAX ]; + let max_limit: u16 = u16::MAX / 2; + assert!( !check_vec_max_limited( &vector, max_limit ) ); + let vector: Vec = vec![ 0, u16::MAX, u16::MAX ]; + let max_limit: u16 = u16::MAX / 2 + 1; + assert!( check_vec_max_limited( &vector, max_limit ) ); + let vector: Vec = vec![ 0, u16::MAX, u16::MAX, u16::MAX ]; + let max_limit: u16 = u16::MAX / 3 - 1; + assert!( !check_vec_max_limited( &vector, max_limit ) ); + let vector: Vec = vec![ 0, u16::MAX, u16::MAX, u16::MAX ]; + let max_limit: u16 = u16::MAX / 3; + assert!( check_vec_max_limited( &vector, max_limit ) ); + } + #[test] fn test_math_fixed_overflow() { let max_32: I32F32 = I32F32::max_value(); diff --git a/pallets/subtensor/src/weights.rs b/pallets/subtensor/src/weights.rs index 143cf3a7d6..8df9f6ef45 100644 --- a/pallets/subtensor/src/weights.rs +++ b/pallets/subtensor/src/weights.rs @@ -1,4 +1,5 @@ use super::*; +use crate::math::*; use frame_support::sp_std::vec; use sp_std::vec::Vec; @@ -102,15 +103,15 @@ impl Pallet { // --- 12. Ensure that the weights have the required length. ensure!( Self::check_length( netuid, neuron_uid, &uids, &values ), Error::::NotSettingEnoughWeights ); - // --- 13. Normalize the weights. - let normalized_values = Self::normalize_weights( values ); + // --- 13. Max-upscale the weights. + let max_upscaled_weights: Vec = vec_u16_max_upscale_to_u16( &values ); // --- 14. Ensure the weights are max weight limited - ensure!( Self::max_weight_limited( netuid, neuron_uid, &uids, &normalized_values ), Error::::MaxWeightExceeded ); + ensure!( Self::max_weight_limited( netuid, neuron_uid, &uids, &max_upscaled_weights ), Error::::MaxWeightExceeded ); // --- 15. Zip weights for sinking to storage map. let mut zipped_weights: Vec<( u16, u16 )> = vec![]; - for ( uid, val ) in uids.iter().zip(normalized_values.iter()) { zipped_weights.push((*uid, *val)) } + for ( uid, val ) in uids.iter().zip(max_upscaled_weights.iter()) { zipped_weights.push((*uid, *val)) } // --- 16. Set weights under netuid, uid double map entry. Weights::::insert( netuid, neuron_uid, zipped_weights ); @@ -221,11 +222,7 @@ impl Pallet { if max_weight_limit == u16::MAX { return true; } // Check if the weights max value is less than or equal to the limit. - let max: u16 = *weights.iter().max().unwrap(); - if max <= max_weight_limit { return true; } - - // The check has failed. - return false; + check_vec_max_limited( weights, max_weight_limit) } // Returns true if the uids and weights correspond to a self weight on the uid. diff --git a/pallets/subtensor/tests/epoch.rs b/pallets/subtensor/tests/epoch.rs index 55f3d6f725..3bce819002 100644 --- a/pallets/subtensor/tests/epoch.rs +++ b/pallets/subtensor/tests/epoch.rs @@ -110,7 +110,7 @@ fn init_run_epochs(netuid: u16, n: u16, validators: &Vec, servers: &Vec> = SubtensorModule::get_weights(netuid); - let weights_set: Vec = all_weights[neuron_uid as usize].iter().map(|x| x.to_bits() as u16).collect(); - - // Should sum less than u16 max. - assert!(weights_set.iter().map(|x| *x as u64).sum::() <= (u16::MAX as u64) ); - - // Should be normalized to 50% each. - assert_eq!(weights_set, vec![u16::MAX/2, u16::MAX/2]); + let weights_set: &Vec = &all_weights[neuron_uid as usize]; + assert_eq!( weights_set[0], I32F32::from_num(1) ); + assert_eq!( weights_set[1], I32F32::from_num(1) ); }); } \ No newline at end of file diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index c752be7504..6bc502d057 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -111,7 +111,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // `spec_version`, and `authoring_version` are the same between Wasm and native. // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use // the compatible custom types. - spec_version: 116, + spec_version: 117, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1,