Skip to content

Commit 4914867

Browse files
authored
Merge pull request #1059 from opentensor/spiigot/hotfix-crv3-devnet
Fix CRv3 Devnet
2 parents 39e661d + e54ac13 commit 4914867

File tree

4 files changed

+376
-5
lines changed

4 files changed

+376
-5
lines changed

pallets/subtensor/src/coinbase/run_coinbase.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -327,9 +327,6 @@ impl<T: Config> Pallet<T> {
327327
);
328328
continue;
329329
};
330-
331-
// If we reached here, we sucessfully set weights!
332-
return Ok(());
333330
}
334331

335332
Ok(())

pallets/subtensor/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ mod tests;
5454
// apparently this is stabilized since rust 1.36
5555
extern crate alloc;
5656

57-
pub const MAX_CRV3_COMMIT_SIZE_BYTES: u32 = 2048;
57+
pub const MAX_CRV3_COMMIT_SIZE_BYTES: u32 = 5000;
5858

5959
#[deny(missing_docs)]
6060
#[import_section(errors::errors)]

pallets/subtensor/src/tests/weights.rs

Lines changed: 374 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5384,3 +5384,377 @@ fn test_reveal_crv3_commits_removes_past_epoch_commits() {
53845384
);
53855385
});
53865386
}
5387+
5388+
#[test]
5389+
fn test_reveal_crv3_commits_multiple_valid_commits_all_processed() {
5390+
new_test_ext(100).execute_with(|| {
5391+
use ark_serialize::CanonicalSerialize;
5392+
5393+
let netuid: u16 = 1;
5394+
let reveal_round: u64 = 1000;
5395+
5396+
// Initialize the network
5397+
add_network(netuid, 5, 0);
5398+
SubtensorModule::set_commit_reveal_weights_enabled(netuid, true);
5399+
SubtensorModule::set_reveal_period(netuid, 1);
5400+
SubtensorModule::set_weights_set_rate_limit(netuid, 0);
5401+
SubtensorModule::set_max_registrations_per_block(netuid, 100);
5402+
SubtensorModule::set_target_registrations_per_interval(netuid, 100);
5403+
5404+
// Register multiple neurons (e.g., 5 neurons)
5405+
let num_neurons = 5;
5406+
let mut hotkeys = Vec::new();
5407+
let mut neuron_uids = Vec::new();
5408+
for i in 0..num_neurons {
5409+
let hotkey: AccountId = U256::from(i + 1);
5410+
register_ok_neuron(netuid, hotkey, U256::from(i + 100), 100_000);
5411+
SubtensorModule::set_validator_permit_for_uid(netuid, i as u16, true);
5412+
hotkeys.push(hotkey);
5413+
neuron_uids.push(
5414+
SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkey)
5415+
.expect("Failed to get neuron UID"),
5416+
);
5417+
}
5418+
5419+
let version_key = SubtensorModule::get_weights_version_key(netuid);
5420+
5421+
// Prepare payloads and commits for each hotkey
5422+
let esk = [2; 32];
5423+
let pk_bytes = hex::decode("83cf0f2896adee7eb8b5f01fcad3912212c437e0073e911fb90022d3e760183c8c4b450b6a0a6c3ac6a5776a2d1064510d1fec758c921cc22b0e17e63aaf4bcb5ed66304de9cf809bd274ca73bab4af5a6e9c76a4bc09e76eae8991ef5ece45a")
5424+
.expect("Failed to decode public key bytes");
5425+
let pub_key = <TinyBLS381 as EngineBLS>::PublicKeyGroup::deserialize_compressed(&*pk_bytes)
5426+
.expect("Failed to deserialize public key");
5427+
5428+
let message = {
5429+
let mut hasher = sha2::Sha256::new();
5430+
hasher.update(reveal_round.to_be_bytes());
5431+
hasher.finalize().to_vec()
5432+
};
5433+
let identity = Identity::new(b"", vec![message]);
5434+
5435+
let mut commits = Vec::new();
5436+
for (i, hotkey) in hotkeys.iter().enumerate() {
5437+
// Each neuron will assign weights to all neurons, including itself
5438+
let values: Vec<u16> = (0..num_neurons as u16)
5439+
.map(|v| (v + i as u16 + 1) * 10)
5440+
.collect();
5441+
let payload = WeightsTlockPayload {
5442+
values: values.clone(),
5443+
uids: neuron_uids.clone(),
5444+
version_key,
5445+
};
5446+
let serialized_payload = payload.encode();
5447+
5448+
let rng = ChaCha20Rng::seed_from_u64(i as u64);
5449+
5450+
let ct = tle::<TinyBLS381, AESGCMStreamCipherProvider, ChaCha20Rng>(
5451+
pub_key,
5452+
esk,
5453+
&serialized_payload,
5454+
identity.clone(),
5455+
rng,
5456+
)
5457+
.expect("Encryption failed");
5458+
5459+
let mut commit_bytes = Vec::new();
5460+
ct.serialize_compressed(&mut commit_bytes)
5461+
.expect("Failed to serialize commit");
5462+
5463+
// Submit the commit
5464+
assert_ok!(SubtensorModule::do_commit_crv3_weights(
5465+
RuntimeOrigin::signed(*hotkey),
5466+
netuid,
5467+
commit_bytes
5468+
.try_into()
5469+
.expect("Failed to convert commit data"),
5470+
reveal_round
5471+
));
5472+
5473+
// Store the expected weights for later comparison
5474+
commits.push((hotkey, payload));
5475+
}
5476+
5477+
// Insert the pulse
5478+
let sig_bytes = hex::decode("b44679b9a59af2ec876b1a6b1ad52ea9b1615fc3982b19576350f93447cb1125e342b73a8dd2bacbe47e4b6b63ed5e39")
5479+
.expect("Failed to decode signature bytes");
5480+
5481+
pallet_drand::Pulses::<Test>::insert(
5482+
reveal_round,
5483+
Pulse {
5484+
round: reveal_round,
5485+
randomness: vec![0; 32]
5486+
.try_into()
5487+
.expect("Failed to convert randomness vector"),
5488+
signature: sig_bytes
5489+
.try_into()
5490+
.expect("Failed to convert signature bytes"),
5491+
},
5492+
);
5493+
5494+
// Advance epoch to trigger reveal
5495+
step_epochs(1, netuid);
5496+
5497+
// Verify weights for all hotkeys
5498+
let weights_sparse = SubtensorModule::get_weights_sparse(netuid);
5499+
5500+
// Set acceptable delta for `I32F32` weights
5501+
let delta = I32F32::from_num(0.0001);
5502+
5503+
for (hotkey, expected_payload) in commits {
5504+
let neuron_uid = SubtensorModule::get_uid_for_net_and_hotkey(netuid, hotkey)
5505+
.expect("Failed to get neuron UID for hotkey") as usize;
5506+
let weights = weights_sparse
5507+
.get(neuron_uid)
5508+
.cloned()
5509+
.unwrap_or_default();
5510+
5511+
assert!(
5512+
!weights.is_empty(),
5513+
"Weights for neuron_uid {} should be set",
5514+
neuron_uid
5515+
);
5516+
5517+
// Normalize expected weights
5518+
let expected_weights: Vec<(u16, I32F32)> = expected_payload
5519+
.uids
5520+
.iter()
5521+
.zip(expected_payload.values.iter())
5522+
.map(|(&uid, &value)| (uid, I32F32::from_num(value)))
5523+
.collect();
5524+
5525+
let total_expected_weight: I32F32 =
5526+
expected_weights.iter().map(|&(_, w)| w).sum();
5527+
5528+
let normalized_expected_weights: Vec<(u16, I32F32)> = expected_weights
5529+
.iter()
5530+
.map(|&(uid, w)| (uid, w / total_expected_weight * I32F32::from_num(30)))
5531+
.collect();
5532+
5533+
// Normalize actual weights
5534+
let total_weight: I32F32 = weights.iter().map(|&(_, w)| w).sum();
5535+
5536+
let normalized_weights: Vec<(u16, I32F32)> = weights
5537+
.iter()
5538+
.map(|&(uid, w)| (uid, w / total_weight * I32F32::from_num(30)))
5539+
.collect();
5540+
5541+
// Compare expected and actual weights with acceptable delta
5542+
for ((uid_expected, weight_expected), (uid_actual, weight_actual)) in
5543+
normalized_expected_weights.iter().zip(normalized_weights.iter())
5544+
{
5545+
assert_eq!(
5546+
uid_expected, uid_actual,
5547+
"UID mismatch: expected {}, got {}",
5548+
uid_expected, uid_actual
5549+
);
5550+
5551+
let diff = (*weight_expected - *weight_actual).abs();
5552+
assert!(
5553+
diff <= delta,
5554+
"Weight mismatch for uid {}: expected {}, got {}, diff {}",
5555+
uid_expected,
5556+
weight_expected,
5557+
weight_actual,
5558+
diff
5559+
);
5560+
}
5561+
}
5562+
5563+
// Verify that commits storage is empty
5564+
let cur_epoch = SubtensorModule::get_epoch_index(
5565+
netuid,
5566+
SubtensorModule::get_current_block_as_u64(),
5567+
);
5568+
let commits = CRV3WeightCommits::<Test>::get(netuid, cur_epoch);
5569+
assert!(
5570+
commits.is_empty(),
5571+
"Expected no commits left in storage after reveal"
5572+
);
5573+
});
5574+
}
5575+
5576+
#[test]
5577+
fn test_reveal_crv3_commits_max_neurons() {
5578+
new_test_ext(100).execute_with(|| {
5579+
use ark_serialize::CanonicalSerialize;
5580+
5581+
let netuid: u16 = 1;
5582+
let reveal_round: u64 = 1000;
5583+
5584+
add_network(netuid, 5, 0);
5585+
SubtensorModule::set_commit_reveal_weights_enabled(netuid, true);
5586+
SubtensorModule::set_reveal_period(netuid, 1);
5587+
SubtensorModule::set_weights_set_rate_limit(netuid, 0);
5588+
SubtensorModule::set_max_registrations_per_block(netuid, 10000);
5589+
SubtensorModule::set_target_registrations_per_interval(netuid, 10000);
5590+
SubtensorModule::set_max_allowed_uids(netuid, 10024);
5591+
5592+
let num_neurons = 1_024;
5593+
let mut hotkeys = Vec::new();
5594+
let mut neuron_uids = Vec::new();
5595+
for i in 0..num_neurons {
5596+
let hotkey: AccountId = U256::from(i + 1);
5597+
register_ok_neuron(netuid, hotkey, U256::from(i + 100), 100_000);
5598+
SubtensorModule::set_validator_permit_for_uid(netuid, i as u16, true);
5599+
hotkeys.push(hotkey);
5600+
neuron_uids.push(
5601+
SubtensorModule::get_uid_for_net_and_hotkey(netuid, &hotkey)
5602+
.expect("Failed to get neuron UID"),
5603+
);
5604+
}
5605+
5606+
let version_key = SubtensorModule::get_weights_version_key(netuid);
5607+
5608+
// Prepare payloads and commits for 3 hotkeys
5609+
let esk = [2; 32];
5610+
let pk_bytes = hex::decode("83cf0f2896adee7eb8b5f01fcad3912212c437e0073e911fb90022d3e760183c8c4b450b6a0a6c3ac6a5776a2d1064510d1fec758c921cc22b0e17e63aaf4bcb5ed66304de9cf809bd274ca73bab4af5a6e9c76a4bc09e76eae8991ef5ece45a")
5611+
.expect("Failed to decode public key bytes");
5612+
let pub_key = <TinyBLS381 as EngineBLS>::PublicKeyGroup::deserialize_compressed(&*pk_bytes)
5613+
.expect("Failed to deserialize public key");
5614+
5615+
let message = {
5616+
let mut hasher = sha2::Sha256::new();
5617+
hasher.update(reveal_round.to_be_bytes());
5618+
hasher.finalize().to_vec()
5619+
};
5620+
let identity = Identity::new(b"", vec![message]);
5621+
5622+
let hotkeys_to_commit = &hotkeys[0..3]; // First 3 hotkeys will submit weight commits
5623+
let mut commits = Vec::new();
5624+
for (i, hotkey) in hotkeys_to_commit.iter().enumerate() {
5625+
// Each neuron will assign weights to all neurons
5626+
let values: Vec<u16> = vec![10; num_neurons]; // Assign weight of 10 to each neuron
5627+
let payload = WeightsTlockPayload {
5628+
values: values.clone(),
5629+
uids: neuron_uids.clone(),
5630+
version_key,
5631+
};
5632+
let serialized_payload = payload.encode();
5633+
5634+
let rng = ChaCha20Rng::seed_from_u64(i as u64);
5635+
5636+
let ct = tle::<TinyBLS381, AESGCMStreamCipherProvider, ChaCha20Rng>(
5637+
pub_key,
5638+
esk,
5639+
&serialized_payload,
5640+
identity.clone(),
5641+
rng,
5642+
)
5643+
.expect("Encryption failed");
5644+
5645+
let mut commit_bytes = Vec::new();
5646+
ct.serialize_compressed(&mut commit_bytes)
5647+
.expect("Failed to serialize commit");
5648+
5649+
// Submit the commit
5650+
assert_ok!(SubtensorModule::do_commit_crv3_weights(
5651+
RuntimeOrigin::signed(*hotkey),
5652+
netuid,
5653+
commit_bytes
5654+
.try_into()
5655+
.expect("Failed to convert commit data"),
5656+
reveal_round
5657+
));
5658+
5659+
// Store the expected weights for later comparison
5660+
commits.push((hotkey, payload));
5661+
}
5662+
5663+
// Insert the pulse
5664+
let sig_bytes = hex::decode("b44679b9a59af2ec876b1a6b1ad52ea9b1615fc3982b19576350f93447cb1125e342b73a8dd2bacbe47e4b6b63ed5e39")
5665+
.expect("Failed to decode signature bytes");
5666+
5667+
pallet_drand::Pulses::<Test>::insert(
5668+
reveal_round,
5669+
Pulse {
5670+
round: reveal_round,
5671+
randomness: vec![0; 32]
5672+
.try_into()
5673+
.expect("Failed to convert randomness vector"),
5674+
signature: sig_bytes
5675+
.try_into()
5676+
.expect("Failed to convert signature bytes"),
5677+
},
5678+
);
5679+
5680+
// Advance epoch to trigger reveal
5681+
step_epochs(1, netuid);
5682+
5683+
// Verify weights for the hotkeys that submitted commits
5684+
let weights_sparse = SubtensorModule::get_weights_sparse(netuid);
5685+
5686+
// Set acceptable delta for `I32F32` weights
5687+
let delta = I32F32::from_num(0.0001); // Adjust delta as needed
5688+
5689+
for (hotkey, expected_payload) in commits {
5690+
let neuron_uid = SubtensorModule::get_uid_for_net_and_hotkey(netuid, hotkey)
5691+
.expect("Failed to get neuron UID for hotkey") as usize;
5692+
let weights = weights_sparse
5693+
.get(neuron_uid)
5694+
.cloned()
5695+
.unwrap_or_default();
5696+
5697+
assert!(
5698+
!weights.is_empty(),
5699+
"Weights for neuron_uid {} should be set",
5700+
neuron_uid
5701+
);
5702+
5703+
// Normalize expected weights
5704+
let expected_weights: Vec<(u16, I32F32)> = expected_payload
5705+
.uids
5706+
.iter()
5707+
.zip(expected_payload.values.iter())
5708+
.map(|(&uid, &value)| (uid, I32F32::from_num(value)))
5709+
.collect();
5710+
5711+
let total_expected_weight: I32F32 =
5712+
expected_weights.iter().map(|&(_, w)| w).sum();
5713+
5714+
let normalized_expected_weights: Vec<(u16, I32F32)> = expected_weights
5715+
.iter()
5716+
.map(|&(uid, w)| (uid, w / total_expected_weight * I32F32::from_num(30)))
5717+
.collect();
5718+
5719+
// Normalize actual weights
5720+
let total_weight: I32F32 = weights.iter().map(|&(_, w)| w).sum();
5721+
5722+
let normalized_weights: Vec<(u16, I32F32)> = weights
5723+
.iter()
5724+
.map(|&(uid, w)| (uid, w / total_weight * I32F32::from_num(30)))
5725+
.collect();
5726+
5727+
// Compare expected and actual weights with acceptable delta
5728+
for ((uid_expected, weight_expected), (uid_actual, weight_actual)) in
5729+
normalized_expected_weights.iter().zip(normalized_weights.iter())
5730+
{
5731+
assert_eq!(
5732+
uid_expected, uid_actual,
5733+
"UID mismatch: expected {}, got {}",
5734+
uid_expected, uid_actual
5735+
);
5736+
5737+
let diff = (*weight_expected - *weight_actual).abs();
5738+
assert!(
5739+
diff <= delta,
5740+
"Weight mismatch for uid {}: expected {}, got {}, diff {}",
5741+
uid_expected,
5742+
weight_expected,
5743+
weight_actual,
5744+
diff
5745+
);
5746+
}
5747+
}
5748+
5749+
// Verify that commits storage is empty
5750+
let cur_epoch = SubtensorModule::get_epoch_index(
5751+
netuid,
5752+
SubtensorModule::get_current_block_as_u64(),
5753+
);
5754+
let commits = CRV3WeightCommits::<Test>::get(netuid, cur_epoch);
5755+
assert!(
5756+
commits.is_empty(),
5757+
"Expected no commits left in storage after reveal"
5758+
);
5759+
});
5760+
}

0 commit comments

Comments
 (0)