From 7233ee60ac858c20b3f899e7df7088ae759d441a Mon Sep 17 00:00:00 2001 From: johnreedv <johnreedv@pm.me> Date: Wed, 4 Dec 2024 11:37:14 -0800 Subject: [PATCH 1/3] fix early exit bug & increase max commit bytes --- .../subtensor/src/coinbase/run_coinbase.rs | 3 - pallets/subtensor/src/lib.rs | 2 +- pallets/subtensor/src/tests/weights.rs | 374 ++++++++++++++++++ 3 files changed, 375 insertions(+), 4 deletions(-) diff --git a/pallets/subtensor/src/coinbase/run_coinbase.rs b/pallets/subtensor/src/coinbase/run_coinbase.rs index 6469ca3ac..51abc08c8 100644 --- a/pallets/subtensor/src/coinbase/run_coinbase.rs +++ b/pallets/subtensor/src/coinbase/run_coinbase.rs @@ -327,9 +327,6 @@ impl<T: Config> Pallet<T> { ); continue; }; - - // If we reached here, we sucessfully set weights! - return Ok(()); } Ok(()) diff --git a/pallets/subtensor/src/lib.rs b/pallets/subtensor/src/lib.rs index 8f9703d36..9b0bf67c0 100644 --- a/pallets/subtensor/src/lib.rs +++ b/pallets/subtensor/src/lib.rs @@ -54,7 +54,7 @@ mod tests; // apparently this is stabilized since rust 1.36 extern crate alloc; -pub const MAX_CRV3_COMMIT_SIZE_BYTES: u32 = 2048; +pub const MAX_CRV3_COMMIT_SIZE_BYTES: u32 = 5000; #[deny(missing_docs)] #[import_section(errors::errors)] diff --git a/pallets/subtensor/src/tests/weights.rs b/pallets/subtensor/src/tests/weights.rs index 63bf509a4..f8dc8ea23 100644 --- a/pallets/subtensor/src/tests/weights.rs +++ b/pallets/subtensor/src/tests/weights.rs @@ -5384,3 +5384,377 @@ fn test_reveal_crv3_commits_removes_past_epoch_commits() { ); }); } + +#[test] +fn test_reveal_crv3_commits_multiple_valid_commits_all_processed() { + new_test_ext(100).execute_with(|| { + use ark_serialize::CanonicalSerialize; + + let netuid: u16 = 1; + let reveal_round: u64 = 1000; + + // Initialize the network + add_network(netuid, 5, 0); + SubtensorModule::set_commit_reveal_weights_enabled(netuid, true); + SubtensorModule::set_reveal_period(netuid, 1); + SubtensorModule::set_weights_set_rate_limit(netuid, 0); + SubtensorModule::set_max_registrations_per_block(netuid, 100); + SubtensorModule::set_target_registrations_per_interval(netuid, 100); + + // Register multiple neurons (e.g., 5 neurons) + let num_neurons = 5; + let mut hotkeys = Vec::new(); + let mut neuron_uids = Vec::new(); + for i in 0..num_neurons { + let hotkey: AccountId = U256::from(i + 1); + register_ok_neuron(netuid, hotkey, U256::from(i + 100), 100_000); + SubtensorModule::set_validator_permit_for_uid(netuid, i as u16, true); + hotkeys.push(hotkey); + neuron_uids.push( + SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkey) + .expect("Failed to get neuron UID"), + ); + } + + let version_key = SubtensorModule::get_weights_version_key(netuid); + + // Prepare payloads and commits for each hotkey + let esk = [2; 32]; + let pk_bytes = hex::decode("83cf0f2896adee7eb8b5f01fcad3912212c437e0073e911fb90022d3e760183c8c4b450b6a0a6c3ac6a5776a2d1064510d1fec758c921cc22b0e17e63aaf4bcb5ed66304de9cf809bd274ca73bab4af5a6e9c76a4bc09e76eae8991ef5ece45a") + .expect("Failed to decode public key bytes"); + let pub_key = <TinyBLS381 as EngineBLS>::PublicKeyGroup::deserialize_compressed(&*pk_bytes) + .expect("Failed to deserialize public key"); + + let message = { + let mut hasher = sha2::Sha256::new(); + hasher.update(reveal_round.to_be_bytes()); + hasher.finalize().to_vec() + }; + let identity = Identity::new(b"", vec![message]); + + let mut commits = Vec::new(); + for (i, hotkey) in hotkeys.iter().enumerate() { + // Each neuron will assign weights to all neurons, including itself + let values: Vec<u16> = (0..num_neurons as u16) + .map(|v| (v + i as u16 + 1) * 10) + .collect(); + let payload = WeightsTlockPayload { + values: values.clone(), + uids: neuron_uids.clone(), + version_key, + }; + let serialized_payload = payload.encode(); + + let rng = ChaCha20Rng::seed_from_u64(i as u64); + + let ct = tle::<TinyBLS381, AESGCMStreamCipherProvider, ChaCha20Rng>( + pub_key, + esk, + &serialized_payload, + identity.clone(), + rng, + ) + .expect("Encryption failed"); + + let mut commit_bytes = Vec::new(); + ct.serialize_compressed(&mut commit_bytes) + .expect("Failed to serialize commit"); + + // Submit the commit + assert_ok!(SubtensorModule::do_commit_crv3_weights( + RuntimeOrigin::signed(*hotkey), + netuid, + commit_bytes + .try_into() + .expect("Failed to convert commit data"), + reveal_round + )); + + // Store the expected weights for later comparison + commits.push((hotkey, payload)); + } + + // Insert the pulse + let sig_bytes = hex::decode("b44679b9a59af2ec876b1a6b1ad52ea9b1615fc3982b19576350f93447cb1125e342b73a8dd2bacbe47e4b6b63ed5e39") + .expect("Failed to decode signature bytes"); + + pallet_drand::Pulses::<Test>::insert( + reveal_round, + Pulse { + round: reveal_round, + randomness: vec![0; 32] + .try_into() + .expect("Failed to convert randomness vector"), + signature: sig_bytes + .try_into() + .expect("Failed to convert signature bytes"), + }, + ); + + // Advance epoch to trigger reveal + step_epochs(1, netuid); + + // Verify weights for all hotkeys + let weights_sparse = SubtensorModule::get_weights_sparse(netuid); + + // Set acceptable delta for `I32F32` weights + let delta = I32F32::from_num(0.0001); + + for (hotkey, expected_payload) in commits { + let neuron_uid = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkey) + .expect("Failed to get neuron UID for hotkey") as usize; + let weights = weights_sparse + .get(neuron_uid) + .cloned() + .unwrap_or_default(); + + assert!( + !weights.is_empty(), + "Weights for neuron_uid {} should be set", + neuron_uid + ); + + // Normalize expected weights + let expected_weights: Vec<(u16, I32F32)> = expected_payload + .uids + .iter() + .zip(expected_payload.values.iter()) + .map(|(&uid, &value)| (uid, I32F32::from_num(value))) + .collect(); + + let total_expected_weight: I32F32 = + expected_weights.iter().map(|&(_, w)| w).sum(); + + let normalized_expected_weights: Vec<(u16, I32F32)> = expected_weights + .iter() + .map(|&(uid, w)| (uid, w / total_expected_weight * I32F32::from_num(30))) + .collect(); + + // Normalize actual weights + let total_weight: I32F32 = weights.iter().map(|&(_, w)| w).sum(); + + let normalized_weights: Vec<(u16, I32F32)> = weights + .iter() + .map(|&(uid, w)| (uid, w / total_weight * I32F32::from_num(30))) + .collect(); + + // Compare expected and actual weights with acceptable delta + for ((uid_expected, weight_expected), (uid_actual, weight_actual)) in + normalized_expected_weights.iter().zip(normalized_weights.iter()) + { + assert_eq!( + uid_expected, uid_actual, + "UID mismatch: expected {}, got {}", + uid_expected, uid_actual + ); + + let diff = (*weight_expected - *weight_actual).abs(); + assert!( + diff <= delta, + "Weight mismatch for uid {}: expected {}, got {}, diff {}", + uid_expected, + weight_expected, + weight_actual, + diff + ); + } + } + + // Verify that commits storage is empty + let cur_epoch = SubtensorModule::get_epoch_index( + netuid, + SubtensorModule::get_current_block_as_u64(), + ); + let commits = CRV3WeightCommits::<Test>::get(netuid, cur_epoch); + assert!( + commits.is_empty(), + "Expected no commits left in storage after reveal" + ); + }); +} + +#[test] +fn test_reveal_crv3_commits_max_neurons() { + new_test_ext(100).execute_with(|| { + use ark_serialize::CanonicalSerialize; + + let netuid: u16 = 1; + let reveal_round: u64 = 1000; + + add_network(netuid, 5, 0); + SubtensorModule::set_commit_reveal_weights_enabled(netuid, true); + SubtensorModule::set_reveal_period(netuid, 1); + SubtensorModule::set_weights_set_rate_limit(netuid, 0); + SubtensorModule::set_max_registrations_per_block(netuid, 10000); + SubtensorModule::set_target_registrations_per_interval(netuid, 10000); + SubtensorModule::set_max_allowed_uids(netuid, 10024); + + let num_neurons = 1_024; + let mut hotkeys = Vec::new(); + let mut neuron_uids = Vec::new(); + for i in 0..num_neurons { + let hotkey: AccountId = U256::from(i + 1); + register_ok_neuron(netuid, hotkey, U256::from(i + 100), 100_000); + SubtensorModule::set_validator_permit_for_uid(netuid, i as u16, true); + hotkeys.push(hotkey); + neuron_uids.push( + SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkey) + .expect("Failed to get neuron UID"), + ); + } + + let version_key = SubtensorModule::get_weights_version_key(netuid); + + // Prepare payloads and commits for 3 hotkeys + let esk = [2; 32]; + let pk_bytes = hex::decode("83cf0f2896adee7eb8b5f01fcad3912212c437e0073e911fb90022d3e760183c8c4b450b6a0a6c3ac6a5776a2d1064510d1fec758c921cc22b0e17e63aaf4bcb5ed66304de9cf809bd274ca73bab4af5a6e9c76a4bc09e76eae8991ef5ece45a") + .expect("Failed to decode public key bytes"); + let pub_key = <TinyBLS381 as EngineBLS>::PublicKeyGroup::deserialize_compressed(&*pk_bytes) + .expect("Failed to deserialize public key"); + + let message = { + let mut hasher = sha2::Sha256::new(); + hasher.update(reveal_round.to_be_bytes()); + hasher.finalize().to_vec() + }; + let identity = Identity::new(b"", vec![message]); + + let hotkeys_to_commit = &hotkeys[0..3]; // First 3 hotkeys will submit weight commits + let mut commits = Vec::new(); + for (i, hotkey) in hotkeys_to_commit.iter().enumerate() { + // Each neuron will assign weights to all neurons + let values: Vec<u16> = vec![10; num_neurons]; // Assign weight of 10 to each neuron + let payload = WeightsTlockPayload { + values: values.clone(), + uids: neuron_uids.clone(), + version_key, + }; + let serialized_payload = payload.encode(); + + let rng = ChaCha20Rng::seed_from_u64(i as u64); + + let ct = tle::<TinyBLS381, AESGCMStreamCipherProvider, ChaCha20Rng>( + pub_key, + esk, + &serialized_payload, + identity.clone(), + rng, + ) + .expect("Encryption failed"); + + let mut commit_bytes = Vec::new(); + ct.serialize_compressed(&mut commit_bytes) + .expect("Failed to serialize commit"); + + // Submit the commit + assert_ok!(SubtensorModule::do_commit_crv3_weights( + RuntimeOrigin::signed(*hotkey), + netuid, + commit_bytes + .try_into() + .expect("Failed to convert commit data"), + reveal_round + )); + + // Store the expected weights for later comparison + commits.push((hotkey, payload)); + } + + // Insert the pulse + let sig_bytes = hex::decode("b44679b9a59af2ec876b1a6b1ad52ea9b1615fc3982b19576350f93447cb1125e342b73a8dd2bacbe47e4b6b63ed5e39") + .expect("Failed to decode signature bytes"); + + pallet_drand::Pulses::<Test>::insert( + reveal_round, + Pulse { + round: reveal_round, + randomness: vec![0; 32] + .try_into() + .expect("Failed to convert randomness vector"), + signature: sig_bytes + .try_into() + .expect("Failed to convert signature bytes"), + }, + ); + + // Advance epoch to trigger reveal + step_epochs(1, netuid); + + // Verify weights for the hotkeys that submitted commits + let weights_sparse = SubtensorModule::get_weights_sparse(netuid); + + // Set acceptable delta for `I32F32` weights + let delta = I32F32::from_num(0.0001); // Adjust delta as needed + + for (hotkey, expected_payload) in commits { + let neuron_uid = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkey) + .expect("Failed to get neuron UID for hotkey") as usize; + let weights = weights_sparse + .get(neuron_uid) + .cloned() + .unwrap_or_default(); + + assert!( + !weights.is_empty(), + "Weights for neuron_uid {} should be set", + neuron_uid + ); + + // Normalize expected weights + let expected_weights: Vec<(u16, I32F32)> = expected_payload + .uids + .iter() + .zip(expected_payload.values.iter()) + .map(|(&uid, &value)| (uid, I32F32::from_num(value))) + .collect(); + + let total_expected_weight: I32F32 = + expected_weights.iter().map(|&(_, w)| w).sum(); + + let normalized_expected_weights: Vec<(u16, I32F32)> = expected_weights + .iter() + .map(|&(uid, w)| (uid, w / total_expected_weight * I32F32::from_num(30))) + .collect(); + + // Normalize actual weights + let total_weight: I32F32 = weights.iter().map(|&(_, w)| w).sum(); + + let normalized_weights: Vec<(u16, I32F32)> = weights + .iter() + .map(|&(uid, w)| (uid, w / total_weight * I32F32::from_num(30))) + .collect(); + + // Compare expected and actual weights with acceptable delta + for ((uid_expected, weight_expected), (uid_actual, weight_actual)) in + normalized_expected_weights.iter().zip(normalized_weights.iter()) + { + assert_eq!( + uid_expected, uid_actual, + "UID mismatch: expected {}, got {}", + uid_expected, uid_actual + ); + + let diff = (*weight_expected - *weight_actual).abs(); + assert!( + diff <= delta, + "Weight mismatch for uid {}: expected {}, got {}, diff {}", + uid_expected, + weight_expected, + weight_actual, + diff + ); + } + } + + // Verify that commits storage is empty + let cur_epoch = SubtensorModule::get_epoch_index( + netuid, + SubtensorModule::get_current_block_as_u64(), + ); + let commits = CRV3WeightCommits::<Test>::get(netuid, cur_epoch); + assert!( + commits.is_empty(), + "Expected no commits left in storage after reveal" + ); + }); +} From a4ccd6de9474dbb2cd00d16a07c62defdc9e21a7 Mon Sep 17 00:00:00 2001 From: johnreedv <johnreedv@pm.me> Date: Wed, 4 Dec 2024 11:46:07 -0800 Subject: [PATCH 2/3] clippy --- pallets/subtensor/src/tests/weights.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/subtensor/src/tests/weights.rs b/pallets/subtensor/src/tests/weights.rs index f8dc8ea23..fd959870e 100644 --- a/pallets/subtensor/src/tests/weights.rs +++ b/pallets/subtensor/src/tests/weights.rs @@ -5501,7 +5501,7 @@ fn test_reveal_crv3_commits_multiple_valid_commits_all_processed() { let delta = I32F32::from_num(0.0001); for (hotkey, expected_payload) in commits { - let neuron_uid = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkey) + let neuron_uid = SubtensorModule::get_uid_for_net_and_hotkey(netuid, hotkey) .expect("Failed to get neuron UID for hotkey") as usize; let weights = weights_sparse .get(neuron_uid) @@ -5687,7 +5687,7 @@ fn test_reveal_crv3_commits_max_neurons() { let delta = I32F32::from_num(0.0001); // Adjust delta as needed for (hotkey, expected_payload) in commits { - let neuron_uid = SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkey) + let neuron_uid = SubtensorModule::get_uid_for_net_and_hotkey(netuid, hotkey) .expect("Failed to get neuron UID for hotkey") as usize; let weights = weights_sparse .get(neuron_uid) From e54ac130971e0d53ada6350a8fa2fbb1fac358f3 Mon Sep 17 00:00:00 2001 From: johnreedv <johnreedv@pm.me> Date: Wed, 4 Dec 2024 12:16:50 -0800 Subject: [PATCH 3/3] bump spec --- runtime/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs index a38b5071d..777111bdc 100644 --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -220,7 +220,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: 213, + spec_version: 214, impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1,