use super::*; pub(super) fn seed_swap_reserves(netuid: NetUid) { let tao_reserve = TaoBalance::from(150_000_000_000_u64); let alpha_in = AlphaBalance::from(100_000_000_000_u64); set_reserves::(netuid, tao_reserve, alpha_in); } pub(super) fn set_reserves( netuid: NetUid, tao_reserve: TaoBalance, alpha_in: AlphaBalance, ) { SubnetTAO::::insert(netuid, tao_reserve); SubnetAlphaIn::::insert(netuid, alpha_in); } pub(super) fn benchmark_registration_burn() -> TaoBalance { TaoBalance::from(1_000_000) } pub(super) fn add_balance_to_coldkey_account(coldkey: &T::AccountId, tao: TaoBalance) { let credit = Subtensor::::mint_tao(tao); let _ = Subtensor::::spend_tao(coldkey, credit, tao).unwrap(); } /// Seed a standing collateral row and keep the coldkey index in sync. pub(super) fn seed_miner_collateral_position( netuid: NetUid, hotkey: &T::AccountId, coldkey: &T::AccountId, locked: AlphaBalance, ) { MinerCollateral::::insert( (netuid, hotkey, coldkey), MinerCollateralState { locked, drain_ratio: U64F64::saturating_from_num(1), min_locked: AlphaBalance::ZERO, earned: AlphaBalance::ZERO, }, ); let aggregate = ColdkeyMinerCollateral::::get(netuid, coldkey).saturating_add(locked); ColdkeyMinerCollateral::::insert(netuid, coldkey, aggregate); ColdkeyCollateralHotkeys::::mutate(netuid, coldkey, |hotkeys| { if !hotkeys.contains(hotkey) { hotkeys .try_push(hotkey.clone()) .expect("benchmark collateral index within MAX_COLDKEY_COLLATERAL_HOTKEYS"); } }); } /// This helper funds an account with: /// - 2x burn fee /// - 100x DefaultMinStake pub(super) fn fund_for_registration(netuid: NetUid, who: &T::AccountId) { let burn = Subtensor::::get_burn(netuid); let min_stake = DefaultMinStake::::get(); let deposit = burn .saturating_mul(2.into()) .saturating_add(min_stake.saturating_mul(100.into())); add_balance_to_coldkey_account::(who, deposit.into()); } pub(super) fn dense_benchmark_weights(uid_count: u16) -> Vec<(u16, u16)> { (0..uid_count) .map(|uid| (uid, u16::MAX)) .collect::>() } pub(super) fn seed_benchmark_neuron( netuid: NetUid, hotkey_label: &'static str, coldkey_label: &'static str, seed: u32, stake: AlphaBalance, ) -> (T::AccountId, T::AccountId) { let hotkey: T::AccountId = account(hotkey_label, seed, 1); let coldkey: T::AccountId = account(coldkey_label, seed, 2); assert_ok!(Subtensor::::create_account_if_non_existent( &coldkey, &hotkey )); Subtensor::::append_neuron(netuid, &hotkey, 0); Subtensor::::increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &coldkey, netuid, stake, ); (hotkey, coldkey) } pub(super) fn setup_full_subnet_registration_benchmark( netuid: NetUid, hotkey_label: &'static str, coldkey_label: &'static str, ) { let uid_count = DefaultMaxAllowedUids::::get(); assert!( uid_count > 0, "registration benchmark requires at least one subnet UID" ); Subtensor::::init_new_network(netuid, 1); SubtokenEnabled::::insert(netuid, true); Subtensor::::set_network_registration_allowed(netuid, true); Subtensor::::set_max_allowed_uids(netuid, uid_count); Subtensor::::set_max_registrations_per_block(netuid, uid_count); Subtensor::::set_target_registrations_per_interval(netuid, uid_count); Subtensor::::set_burn(netuid, benchmark_registration_burn()); seed_swap_reserves::(netuid); let netuid_index = NetUidStorageIndex::from(netuid); let dense_weights = dense_benchmark_weights(uid_count); for uid in 0..uid_count { seed_benchmark_neuron::( netuid, hotkey_label, coldkey_label, u32::from(uid), AlphaBalance::ZERO, ); Subtensor::::set_validator_permit_for_uid(netuid, uid, true); Weights::::insert(netuid_index, uid, dense_weights.clone()); Bonds::::insert(netuid_index, uid, dense_weights.clone()); } assert_eq!(Subtensor::::get_subnetwork_n(netuid), uid_count); } pub(super) fn setup_full_root_registration_benchmark() { Subtensor::::init_new_network(NetUid::ROOT, 1); SubtokenEnabled::::insert(NetUid::ROOT, true); Subtensor::::set_network_registration_allowed(NetUid::ROOT, true); FirstEmissionBlockNumber::::insert(NetUid::ROOT, 1); let root_validator_count = Subtensor::::get_max_allowed_validators(NetUid::ROOT); assert!( root_validator_count > 0, "root registration benchmark requires at least one root validator" ); Subtensor::::set_max_allowed_uids(NetUid::ROOT, root_validator_count); Subtensor::::set_max_registrations_per_block( NetUid::ROOT, root_validator_count.saturating_add(1), ); Subtensor::::set_target_registrations_per_interval( NetUid::ROOT, root_validator_count.saturating_add(1), ); let netuid_index = NetUidStorageIndex::from(NetUid::ROOT); let dense_weights = dense_benchmark_weights(root_validator_count); for uid in 0..root_validator_count { seed_benchmark_neuron::( NetUid::ROOT, "root_register_existing_hot", "root_register_existing_cold", u32::from(uid), AlphaBalance::from(1_u64), ); Subtensor::::set_validator_permit_for_uid(NetUid::ROOT, uid, true); Weights::::insert(netuid_index, uid, dense_weights.clone()); Bonds::::insert(netuid_index, uid, dense_weights.clone()); } assert_eq!( Subtensor::::get_subnetwork_n(NetUid::ROOT), root_validator_count ); } /// Add a zero lock to a random hotkey just so that the lock records exist pub(super) fn add_lock(coldkey: &T::AccountId, netuid: NetUid) { let hotkey: T::AccountId = account("RandomHotkey", 0, 999); Lock::::insert( (coldkey, netuid, hotkey.clone()), LockState { locked_mass: AlphaBalance::ZERO, conviction: U64F64::from_num(0), last_update: 0, }, ); HotkeyLock::::insert( netuid, hotkey, LockState { locked_mass: AlphaBalance::ZERO, conviction: U64F64::from_num(0), last_update: 0, }, ); } pub(super) fn set_benchmark_block_number(block_number: u64) { let block_number: BlockNumberFor = match block_number.try_into() { Ok(block_number) => block_number, Err(_) => panic!("benchmark block number must fit into BlockNumberFor"), }; frame_system::Pallet::::set_block_number(block_number); } /// Seed a mainnet-like block_step state without measuring registration costs. /// /// The measured block_step path iterates over subnets repeatedly: burn-price /// updates, coinbase, moving prices, root proportion, pending children, /// root dividend auto-claims, and root coldkey map population. Keep the setup /// intentionally large so this hook benchmark reflects a 128-subnet mainnet /// state instead of a one-subnet toy state. /// /// Only the runtime-capped number of subnet epochs are made due in the measured /// block. The remaining subnets are fully populated and live, but not eligible /// for epoch execution in this block. This mirrors production behavior where /// `MaxEpochsPerBlock` bounds the number of Yuma epochs that can execute in a /// single block. pub(super) fn setup_block_step_benchmark() { const MAINNET_SUBNETS: u16 = 128; const MAINNET_NEURONS_PER_SUBNET: u16 = 256; const MAINNET_VALIDATORS_PER_SUBNET: u16 = 128; const TEMPO: u16 = 360; const CURRENT_BLOCK: u64 = 7_000; const SUBNET_TAO_RESERVE: u64 = 1_000_000_000_000_000; const SUBNET_ALPHA_RESERVE: u64 = 1_000_000_000_000_000_000; const VALIDATOR_ALPHA_STAKE: u64 = 1_000_000_000; set_benchmark_block_number::(CURRENT_BLOCK); let max_epochs_this_block = u16::from(Subtensor::::get_max_epochs_per_block()).min(MAINNET_SUBNETS); assert!( max_epochs_this_block > 0, "block_step benchmark requires MaxEpochsPerBlock > 0" ); let dense_weights = (0..MAINNET_NEURONS_PER_SUBNET) .map(|dest_uid| (dest_uid, u16::MAX)) .collect::>(); Subtensor::::init_new_network(NetUid::ROOT, TEMPO); Subtensor::::set_network_registration_allowed(NetUid::ROOT, true); Subtensor::::set_max_allowed_uids(NetUid::ROOT, MAINNET_NEURONS_PER_SUBNET); FirstEmissionBlockNumber::::insert(NetUid::ROOT, CURRENT_BLOCK); LastEpochBlock::::insert(NetUid::ROOT, CURRENT_BLOCK); LastMechansimStepBlock::::insert(NetUid::ROOT, CURRENT_BLOCK); BlocksSinceLastStep::::insert(NetUid::ROOT, 0); PendingEpochAt::::insert(NetUid::ROOT, 0); SubtokenEnabled::::insert(NetUid::ROOT, true); SubnetEmissionEnabled::::insert(NetUid::ROOT, true); set_reserves::( NetUid::ROOT, TaoBalance::from(SUBNET_TAO_RESERVE), AlphaBalance::from(SUBNET_ALPHA_RESERVE), ); SubnetAlphaOut::::insert(NetUid::ROOT, AlphaBalance::from(SUBNET_ALPHA_RESERVE)); // Root is also populated because block_step ends by rebuilding root coldkey // staking maps. These root neurons mirror the high-cardinality account map // shape seen on a populated mainnet chain. let root_index = NetUidStorageIndex::from(NetUid::ROOT); for uid in 0..MAINNET_NEURONS_PER_SUBNET { let hotkey: T::AccountId = account("block_step_root_hot", 0, u32::from(uid)); let coldkey: T::AccountId = account("block_step_root_cold", 0, u32::from(uid)); Owner::::insert(&hotkey, &coldkey); Subtensor::::append_neuron(NetUid::ROOT, &hotkey, 0); Subtensor::::set_validator_permit_for_uid(NetUid::ROOT, uid, true); Subtensor::::increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &coldkey, NetUid::ROOT, AlphaBalance::from(VALIDATOR_ALPHA_STAKE), ); if uid < MAINNET_VALIDATORS_PER_SUBNET { Weights::::insert(root_index, uid, dense_weights.clone()); Bonds::::insert(root_index, uid, dense_weights.clone()); } } // 128 live non-root subnets, each with 256 registered neurons. The first 128 // neurons per subnet are validator-permit neurons with dense weight and bond // rows. Only the first `MaxEpochsPerBlock` subnets are scheduled to run their // epoch in this measured block; the rest remain live ambient state. for subnet_index in 1..=MAINNET_SUBNETS { let netuid = NetUid::from(subnet_index); let netuid_index = NetUidStorageIndex::from(netuid); let subnet_owner: T::AccountId = account("block_step_subnet_owner", u32::from(subnet_index), 0); let epoch_is_due_this_block = subnet_index <= max_epochs_this_block; Subtensor::::init_new_network(netuid, TEMPO); SubtokenEnabled::::insert(netuid, true); SubnetEmissionEnabled::::insert(netuid, true); SubnetOwner::::insert(netuid, subnet_owner); Subtensor::::set_network_registration_allowed(netuid, true); Subtensor::::set_max_allowed_uids(netuid, MAINNET_NEURONS_PER_SUBNET); Subtensor::::set_max_registrations_per_block(netuid, MAINNET_NEURONS_PER_SUBNET); Subtensor::::set_target_registrations_per_interval(netuid, MAINNET_NEURONS_PER_SUBNET); Subtensor::::set_burn(netuid, benchmark_registration_burn()); set_reserves::( netuid, TaoBalance::from(SUBNET_TAO_RESERVE), AlphaBalance::from(SUBNET_ALPHA_RESERVE), ); SubnetAlphaOut::::insert(netuid, AlphaBalance::from(SUBNET_ALPHA_RESERVE)); FirstEmissionBlockNumber::::insert(netuid, CURRENT_BLOCK); if epoch_is_due_this_block { PendingEpochAt::::insert(netuid, CURRENT_BLOCK); LastEpochBlock::::insert(netuid, 0); LastMechansimStepBlock::::insert(netuid, 0); BlocksSinceLastStep::::insert(netuid, CURRENT_BLOCK); } else { PendingEpochAt::::insert(netuid, 0); LastEpochBlock::::insert(netuid, CURRENT_BLOCK); LastMechansimStepBlock::::insert(netuid, CURRENT_BLOCK); BlocksSinceLastStep::::insert(netuid, 0); } for uid in 0..MAINNET_NEURONS_PER_SUBNET { let hotkey: T::AccountId = account("block_step_hot", u32::from(subnet_index), u32::from(uid)); let coldkey: T::AccountId = account("block_step_cold", u32::from(subnet_index), u32::from(uid)); Owner::::insert(&hotkey, &coldkey); Subtensor::::append_neuron(netuid, &hotkey, 0); Subtensor::::increase_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &coldkey, netuid, AlphaBalance::from(VALIDATOR_ALPHA_STAKE), ); // Worst case for coinbase: every incentivized miner has standing // collateral that must be settled. Alternate below-floor capture // (stake write into the lock) and above-floor drain (release back // to free stake) so both settle branches are measured. Only seed // epoch-due subnets so ambient live state stays cheap. if epoch_is_due_this_block { let (locked, min_locked) = if uid % 2 == 0 { ( AlphaBalance::from(VALIDATOR_ALPHA_STAKE / 4), AlphaBalance::from(VALIDATOR_ALPHA_STAKE), ) } else { ( AlphaBalance::from(VALIDATOR_ALPHA_STAKE), AlphaBalance::from(VALIDATOR_ALPHA_STAKE / 4), ) }; MinerCollateral::::insert( (netuid, &hotkey, &coldkey), MinerCollateralState { locked, drain_ratio: U64F64::saturating_from_num(1), min_locked, earned: AlphaBalance::ZERO, }, ); ColdkeyMinerCollateral::::insert(netuid, &coldkey, locked); ColdkeyCollateralHotkeys::::mutate(netuid, &coldkey, |hotkeys| { if !hotkeys.contains(&hotkey) { let _ = hotkeys.try_push(hotkey.clone()); } }); } if uid < MAINNET_VALIDATORS_PER_SUBNET { Subtensor::::set_validator_permit_for_uid(netuid, uid, true); Weights::::insert(netuid_index, uid, dense_weights.clone()); Bonds::::insert(netuid_index, uid, dense_weights.clone()); } } } } pub(super) fn runtime_call(call: Call) -> ::RuntimeCall { ::RuntimeCall::from(call).into() } pub(super) fn setup_extension_neuron(netuid: NetUid, hotkey: &T::AccountId) { Subtensor::::init_new_network(netuid, 0); Subtensor::::set_max_allowed_uids(netuid, GLOBAL_MAX_SUBNET_COUNT); Subtensor::::append_neuron(netuid, hotkey, 0); } pub(super) fn benchmark_evm_secret_key() -> libsecp256k1::SecretKey { let seed = [42u8; 32]; match libsecp256k1::SecretKey::parse(&seed) { Ok(secret_key) => secret_key, Err(_) => panic!("benchmark EVM secret key must be valid"), } } pub(super) fn evm_key_from_secret_key(secret_key: &libsecp256k1::SecretKey) -> H160 { let public_key = libsecp256k1::PublicKey::from_secret_key(secret_key); let uncompressed = public_key.serialize(); let public_key_without_prefix = match uncompressed.get(1..) { Some(public_key_without_prefix) => public_key_without_prefix, None => panic!("uncompressed secp256k1 public key must contain a prefix byte"), }; let hashed_public_key = sp_io::hashing::keccak_256(public_key_without_prefix); let evm_key_bytes = match hashed_public_key.get(12..) { Some(evm_key_bytes) => evm_key_bytes, None => panic!("keccak256 hash must be 32 bytes"), }; H160::from_slice(evm_key_bytes) } pub(super) fn signature_for_associate_evm_key( hotkey: &T::AccountId, block_number: u64, secret_key: &libsecp256k1::SecretKey, ) -> ecdsa::Signature { let block_hash = sp_io::hashing::keccak_256(block_number.encode().as_ref()); let mut message = hotkey.encode(); message.extend_from_slice(&block_hash); let message_hash = Subtensor::::hash_message_eip191(message); let secp_message = libsecp256k1::Message::parse(&message_hash); let (secp_signature, recovery_id) = libsecp256k1::sign(&secp_message, secret_key); let mut signature = [0u8; 65]; let serialized_signature = secp_signature.serialize(); let signature_bytes = match signature.get_mut(..64) { Some(signature_bytes) => signature_bytes, None => panic!("benchmark ECDSA signature buffer must contain 64 signature bytes"), }; signature_bytes.copy_from_slice(&serialized_signature); let recovery_id_byte = match signature.get_mut(64) { Some(recovery_id_byte) => recovery_id_byte, None => panic!("benchmark ECDSA signature buffer must contain a recovery id byte"), }; *recovery_id_byte = recovery_id.serialize(); ecdsa::Signature::from_raw(signature) } pub(super) fn setup_worst_case_registered_subnet( label: &'static str, netuid: NetUid, uid_count: u32, ) -> (T::AccountId, T::AccountId, Vec, Vec) { let uid_count = uid_count.clamp(1, 4096); let mut uids = Vec::with_capacity(uid_count as usize); let mut weights = Vec::with_capacity(uid_count as usize); let mut signer_hotkey: Option = None; let mut signer_coldkey: Option = None; Subtensor::::init_new_network(netuid, 1); SubtokenEnabled::::insert(netuid, true); Subtensor::::set_network_registration_allowed(netuid, true); Subtensor::::set_max_allowed_uids(netuid, 4096); Subtensor::::set_max_registrations_per_block(netuid, 4096); Subtensor::::set_target_registrations_per_interval(netuid, 4096); Subtensor::::set_difficulty(netuid, 1); Subtensor::::set_weights_set_rate_limit(netuid, 0); Subtensor::::set_stake_threshold(0); set_reserves::( netuid, TaoBalance::from(1_000_000_000_000_u64), AlphaBalance::from(1_000_000_000_000_000_u64), ); for seed in 0..uid_count { let hotkey: T::AccountId = account(label, seed, 1); let coldkey: T::AccountId = account(label, seed, 2); Burn::::insert(netuid, benchmark_registration_burn()); RegistrationsThisInterval::::insert(netuid, 0); fund_for_registration::(netuid, &coldkey); assert_ok!(Subtensor::::burned_register( RawOrigin::Signed(coldkey.clone()).into(), netuid, hotkey.clone(), )); let uid = Subtensor::::get_uid_for_net_and_hotkey(netuid, &hotkey).unwrap(); Subtensor::::set_validator_permit_for_uid(netuid, uid, true); uids.push(uid); weights.push(uid.saturating_add(1)); if signer_hotkey.is_none() { signer_hotkey = Some(hotkey); signer_coldkey = Some(coldkey); } } ( signer_hotkey.unwrap(), signer_coldkey.unwrap(), uids, weights, ) } pub(super) fn setup_mechanism_weight_benchmark( _mecid: subtensor_runtime_common::MechId, uid_count: u32, ) -> (NetUid, T::AccountId, Vec, Vec, Vec, u64) { let netuid = NetUid::from(1); let version_key: u64 = 0; let (hotkey, _coldkey, uids, weight_values) = setup_worst_case_registered_subnet::("mechanism", netuid, uid_count); let salt: Vec = vec![u16::MAX; uid_count.clamp(1, 4096) as usize]; Subtensor::::set_commit_reveal_weights_enabled(netuid, true); (netuid, hotkey, uids, weight_values, salt, version_key) }