diff --git a/Cargo.lock b/Cargo.lock index 42522da321..2e66bfd099 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1779,7 +1779,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1145d32e826a7748b69ee8fc62d3e6355ff7f1051df53141e7048162fc90481b" dependencies = [ "data-encoding", - "syn 2.0.96", + "syn 1.0.109", ] [[package]] @@ -6126,9 +6126,9 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl" -version = "0.10.69" +version = "0.10.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5e534d133a060a3c19daec1eb3e98ec6f4685978834f2dbadfe2ec215bab64e" +checksum = "61cfb4e166a8bb8c9b55c500bc2308550148ece889be90f609377e58140f42c6" dependencies = [ "bitflags 2.8.0", "cfg-if", @@ -6167,9 +6167,9 @@ dependencies = [ [[package]] name = "openssl-sys" -version = "0.9.104" +version = "0.9.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" +checksum = "8b22d5b84be05a8d6947c7cb71f7c849aa0f112acd4bf51c2a7c1c988ac0a9dc" dependencies = [ "cc", "libc", diff --git a/runtime/src/precompiles/mod.rs b/runtime/src/precompiles/mod.rs index d820f50ec3..3857affb7d 100644 --- a/runtime/src/precompiles/mod.rs +++ b/runtime/src/precompiles/mod.rs @@ -3,7 +3,7 @@ extern crate alloc; use alloc::format; use core::marker::PhantomData; -use frame_support::dispatch::{GetDispatchInfo, Pays}; +use crate::{Runtime, RuntimeCall}; use pallet_evm::{ ExitError, ExitSucceed, GasWeightMapping, IsPrecompileResult, Precompile, PrecompileFailure, @@ -12,13 +12,11 @@ use pallet_evm::{ use pallet_evm_precompile_modexp::Modexp; use pallet_evm_precompile_sha3fips::Sha3FIPS256; use pallet_evm_precompile_simple::{ECRecover, ECRecoverPublicKey, Identity, Ripemd160, Sha256}; -use sp_core::{hashing::keccak_256, H160}; -use sp_runtime::{traits::Dispatchable, AccountId32}; - -use crate::{Runtime, RuntimeCall}; +use frame_support::dispatch::{GetDispatchInfo, Pays}; use frame_system::RawOrigin; - +use sp_core::{hashing::keccak_256, H160}; +use sp_runtime::{traits::Dispatchable, AccountId32}; use sp_std::vec; // Include custom precompiles diff --git a/runtime/src/precompiles/neuron.rs b/runtime/src/precompiles/neuron.rs index 97167c9009..317f99242c 100644 --- a/runtime/src/precompiles/neuron.rs +++ b/runtime/src/precompiles/neuron.rs @@ -1,17 +1,25 @@ -use pallet_evm::{ExitError, PrecompileFailure, PrecompileHandle, PrecompileResult}; +use pallet_evm::{ + AddressMapping, ExitError, HashedAddressMapping, PrecompileFailure, PrecompileHandle, + PrecompileResult, +}; use crate::precompiles::{ - contract_to_origin, get_method_id, get_pubkey, get_slice, parse_netuid, - try_dispatch_runtime_call, + get_method_id, get_pubkey, get_slice, parse_netuid, try_dispatch_runtime_call, }; +use crate::{Runtime, RuntimeCall}; +use frame_system::RawOrigin; +use sp_core::H256; +use sp_runtime::traits::BlakeTwo256; use sp_runtime::AccountId32; use sp_std::vec; +use sp_std::vec::Vec; -use crate::{Runtime, RuntimeCall}; pub const NEURON_PRECOMPILE_INDEX: u64 = 2052; - +// max paramter lenght 4K +pub const MAX_PARAMETER_SIZE: usize = 4 * 1024; // ss58 public key i.e., the contract sends funds it received to the destination address from the // method parameter. +#[allow(dead_code)] const CONTRACT_ADDRESS_SS58: [u8; 32] = [ 0xbc, 0x46, 0x35, 0x79, 0xbc, 0x99, 0xf9, 0xee, 0x7c, 0x59, 0xed, 0xee, 0x20, 0x61, 0xa3, 0x09, 0xd2, 0x1e, 0x68, 0xd5, 0x39, 0xb6, 0x40, 0xec, 0x66, 0x46, 0x90, 0x30, 0xab, 0x74, 0xc1, 0xdb, @@ -26,7 +34,29 @@ impl NeuronPrecompile { .get(4..) .map_or_else(vec::Vec::new, |slice| slice.to_vec()); // Avoiding borrowing conflicts + if method_input.len() > MAX_PARAMETER_SIZE { + log::error!( + "method parameter data length as {} is too long", + method_input.len() + ); + return Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }); + } + match method_id { + id if id == get_method_id("setWeights(uint16,uint16[],uint16[],uint64)") => { + Self::set_weights(handle, &method_input) + } + id if id == get_method_id("commitWeights(uint16,uint256)") => { + Self::commit_weights(handle, &method_input) + } + id if id + == get_method_id("revealWeights(uint16,uint16[],uint16[],uint16[],uint64)") => + { + Self::reveal_weights(handle, &method_input) + } + id if id == get_method_id("burnedRegister(uint16,bytes32)") => { Self::burned_register(handle, &method_input) } @@ -37,6 +67,55 @@ impl NeuronPrecompile { } } + pub fn set_weights(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { + let (netuid, dests, weights, version_key) = Self::parse_netuid_dests_weights(data)?; + let call = RuntimeCall::SubtensorModule(pallet_subtensor::Call::::set_weights { + netuid, + dests, + weights, + version_key, + }); + let account_id = + as AddressMapping>::into_account_id( + handle.context().caller, + ); + + try_dispatch_runtime_call(handle, call, RawOrigin::Signed(account_id)) + } + + pub fn commit_weights(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { + let (netuid, commit_hash) = Self::parse_netuid_commit_hash(data)?; + + let call = + RuntimeCall::SubtensorModule(pallet_subtensor::Call::::commit_weights { + netuid, + commit_hash, + }); + let account_id = + as AddressMapping>::into_account_id( + handle.context().caller, + ); + try_dispatch_runtime_call(handle, call, RawOrigin::Signed(account_id)) + } + + pub fn reveal_weights(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { + let (netuid, uids, values, salt, version_key) = + Self::parse_netuid_dests_weights_salt(data)?; + let call = + RuntimeCall::SubtensorModule(pallet_subtensor::Call::::reveal_weights { + netuid, + uids, + values, + salt, + version_key, + }); + let account_id = + as AddressMapping>::into_account_id( + handle.context().caller, + ); + try_dispatch_runtime_call(handle, call, RawOrigin::Signed(account_id)) + } + pub fn burned_register(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { let (netuid, hotkey) = Self::parse_netuid_hotkey_parameter(data)?; let call = @@ -44,7 +123,12 @@ impl NeuronPrecompile { netuid, hotkey, }); - try_dispatch_runtime_call(handle, call, contract_to_origin(&CONTRACT_ADDRESS_SS58)?) + + let account_id = + as AddressMapping>::into_account_id( + handle.context().caller, + ); + try_dispatch_runtime_call(handle, call, RawOrigin::Signed(account_id)) } fn parse_netuid_hotkey_parameter(data: &[u8]) -> Result<(u16, AccountId32), PrecompileFailure> { @@ -53,10 +137,279 @@ impl NeuronPrecompile { exit_status: ExitError::InvalidRange, }); } + let netuid = parse_netuid(data, 30)?; let (hotkey, _) = get_pubkey(get_slice(data, 32, 64)?)?; Ok((netuid, hotkey)) } + + fn parse_netuid_dests_weights( + data: &[u8], + ) -> Result<(u16, Vec, Vec, u64), PrecompileFailure> { + let data_len = data.len(); + if data_len < 4 * 32 { + return Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }); + } + let mut netuid_vec = [0u8; 2]; + netuid_vec.copy_from_slice(get_slice(data, 30, 32)?); + let netuid = u16::from_be_bytes(netuid_vec); + + // get the neuron amount in sebnet + let subnet_size = pallet_subtensor::Pallet::::get_subnetwork_n(netuid) as usize; + + let mut first_position_vec = [0u8; 2]; + first_position_vec.copy_from_slice(get_slice(data, 62, 64)?); + let first_position = u16::from_be_bytes(first_position_vec) as usize; + + if first_position > data_len { + log::error!("position for uids data as {} is too large", first_position); + return Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }); + } + + let mut second_position_vec = [0u8; 2]; + second_position_vec.copy_from_slice(get_slice(data, 94, 96)?); + let second_position = u16::from_be_bytes(second_position_vec) as usize; + + if second_position > data_len { + log::error!("position for uids data as {} is too large", first_position); + return Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }); + } + + let mut version_key_vec = [0u8; 8]; + version_key_vec.copy_from_slice(get_slice(data, 120, 128)?); + let version_key = u64::from_be_bytes(version_key_vec); + + let mut dests = vec![]; + let mut weights = vec![]; + + let mut dests_len_vec = [0u8; 2]; + dests_len_vec.copy_from_slice(get_slice(data, first_position + 30, first_position + 32)?); + let dests_len = u16::from_be_bytes(dests_len_vec) as usize; + + if dests_len > subnet_size { + log::error!( + "uids len as {} in set weight is more than neurons {} in subnet {}", + dests_len, + subnet_size, + netuid + ); + return Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }); + } + + for i in 0..dests_len { + let mut tmp_vec = [0u8; 2]; + let from = first_position + .saturating_add(62) + .saturating_add(i.saturating_mul(32)); + let to = from.saturating_add(2); + tmp_vec.copy_from_slice(get_slice(data, from, to)?); + let dest = u16::from_be_bytes(tmp_vec); + dests.push(dest); + } + + let mut weights_len_vec = [0u8; 2]; + weights_len_vec.copy_from_slice(get_slice( + data, + second_position + 30, + second_position + 32, + )?); + let weights_len = u16::from_be_bytes(weights_len_vec) as usize; + + if weights_len > subnet_size { + log::error!( + "weights len as {} in set weight is more than neurons {} in subnet {}", + weights_len, + subnet_size, + netuid + ); + return Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }); + } + + for i in 0..weights_len { + let mut tmp_vec = [0u8; 2]; + let from = second_position + .saturating_add(62) + .saturating_add(i.saturating_mul(32)); + let to = from.saturating_add(2); + tmp_vec.copy_from_slice(get_slice(data, from, to)?); + let weight = u16::from_be_bytes(tmp_vec); + weights.push(weight); + } + + Ok((netuid, dests, weights, version_key)) + } + + fn parse_netuid_commit_hash(data: &[u8]) -> Result<(u16, H256), PrecompileFailure> { + if data.len() < 2 * 32 { + return Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }); + } + + let mut netuid_vec = [0u8; 2]; + netuid_vec.copy_from_slice(get_slice(data, 30, 32)?); + let netuid = u16::from_be_bytes(netuid_vec); + let commit_hash = H256::from_slice(get_slice(data, 32, 64)?); + + Ok((netuid, commit_hash)) + } + + fn parse_netuid_dests_weights_salt( + data: &[u8], + ) -> Result<(u16, Vec, Vec, Vec, u64), PrecompileFailure> { + let data_len = data.len(); + if data_len < 5 * 32 { + return Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }); + } + + let netuid = parse_netuid(data, 30)?; + + // get the neuron amount in sebnet + let subnet_size = pallet_subtensor::Pallet::::get_subnetwork_n(netuid) as usize; + + let mut first_position_vec = [0u8; 2]; + first_position_vec.copy_from_slice(get_slice(data, 62, 64)?); + let first_position = u16::from_be_bytes(first_position_vec) as usize; + + if first_position > data_len { + log::error!("position for uids data as {} is too large", first_position); + return Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }); + } + + let mut second_position_vec = [0u8; 2]; + second_position_vec.copy_from_slice(get_slice(data, 94, 96)?); + let second_position = u16::from_be_bytes(second_position_vec) as usize; + + if second_position > data_len { + log::error!( + "position for values data as {} is too large", + first_position + ); + return Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }); + } + + let mut third_position_vec = [0u8; 2]; + third_position_vec.copy_from_slice(get_slice(data, 126, 128)?); + let third_position = u16::from_be_bytes(third_position_vec) as usize; + + if third_position > data_len { + log::error!("position for salt data as {} is too large", first_position); + return Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }); + } + + let mut version_key_vec = [0u8; 8]; + version_key_vec.copy_from_slice(get_slice(data, 152, 160)?); + let version_key = u64::from_be_bytes(version_key_vec); + + let mut uids = vec![]; + let mut values = vec![]; + let mut salt = vec![]; + + let mut uids_len_vec = [0u8; 2]; + uids_len_vec.copy_from_slice(get_slice(data, first_position + 30, first_position + 32)?); + let uids_len = u16::from_be_bytes(uids_len_vec) as usize; + + if uids_len > subnet_size { + log::error!( + "uids len as {} in reveal weight is more than neurons {} in subnet {}", + uids_len, + subnet_size, + netuid + ); + return Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }); + } + + for i in 0..uids_len { + let mut tmp_vec = [0u8; 2]; + let from = first_position + .saturating_add(62) + .saturating_add(i.saturating_mul(32)); + let to = from.saturating_add(2); + tmp_vec.copy_from_slice(get_slice(data, from, to)?); + let uid = u16::from_be_bytes(tmp_vec); + uids.push(uid); + } + + let mut values_len_vec = [0u8; 2]; + values_len_vec.copy_from_slice(get_slice( + data, + second_position + 30, + second_position + 32, + )?); + let values_len = u16::from_be_bytes(values_len_vec) as usize; + + if values_len > subnet_size { + log::error!( + "values len as {} in reveal weight is more than neurons {} in subnet {}", + values_len, + subnet_size, + netuid + ); + return Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }); + } + + for i in 0..values_len { + let mut tmp_vec = [0u8; 2]; + let from = second_position + .saturating_add(62) + .saturating_add(i.saturating_mul(32)); + let to = from.saturating_add(2); + tmp_vec.copy_from_slice(get_slice(data, from, to)?); + let value = u16::from_be_bytes(tmp_vec); + values.push(value); + } + + let mut salt_len_vec = [0u8; 2]; + salt_len_vec.copy_from_slice(get_slice(data, third_position + 30, third_position + 32)?); + let salt_len = u16::from_be_bytes(salt_len_vec) as usize; + + if salt_len > subnet_size { + log::error!( + "salt len as {} in reveal weight is more than neurons {} in subnet {}", + salt_len, + subnet_size, + netuid + ); + return Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }); + } + + for i in 0..salt_len { + let mut tmp_vec = [0u8; 2]; + let from = third_position + .saturating_add(62) + .saturating_add(i.saturating_mul(32)); + let to = from.saturating_add(2); + tmp_vec.copy_from_slice(get_slice(data, from, to)?); + let value = u16::from_be_bytes(tmp_vec); + salt.push(value); + } + + Ok((netuid, uids, values, salt, version_key)) + } } diff --git a/runtime/src/precompiles/solidity/neuron.abi b/runtime/src/precompiles/solidity/neuron.abi index 09a14eb2db..804c9e8f16 100644 --- a/runtime/src/precompiles/solidity/neuron.abi +++ b/runtime/src/precompiles/solidity/neuron.abi @@ -1,3 +1,4 @@ + [ { "inputs": [ @@ -16,5 +17,84 @@ "outputs": [], "stateMutability": "payable", "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint256", + "name": "commitHash", + "type": "uint256" + } + ], + "name": "commitWeights", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint16[]", + "name": "uids", + "type": "uint16[]" + }, + { + "internalType": "uint16[]", + "name": "values", + "type": "uint16[]" + }, + { + "internalType": "uint16[]", + "name": "salt", + "type": "uint16[]" + }, + { + "internalType": "uint64", + "name": "versionKey", + "type": "uint64" + } + ], + "name": "revealWeights", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint16[]", + "name": "dests", + "type": "uint16[]" + }, + { + "internalType": "uint16[]", + "name": "weights", + "type": "uint16[]" + }, + { + "internalType": "uint64", + "name": "versionKey", + "type": "uint64" + } + ], + "name": "setWeights", + "outputs": [], + "stateMutability": "payable", + "type": "function" } -] +] \ No newline at end of file diff --git a/runtime/src/precompiles/solidity/neuron.sol b/runtime/src/precompiles/solidity/neuron.sol index a768423398..ae002c47ab 100644 --- a/runtime/src/precompiles/solidity/neuron.sol +++ b/runtime/src/precompiles/solidity/neuron.sol @@ -11,4 +11,44 @@ interface INeuron { * @param hotkey The hotkey public key (32 bytes). */ function burnedRegister(uint16 netuid, bytes32 hotkey) external payable; + + /** + * @dev Sets the weights for a neuron. + * + * @param netuid The subnet to set the weights for (uint16). + * @param dests The destinations of the weights (uint16[]). + * @param weights The weights to set (uint16[]). + * @param versionKey The version key for the weights (uint64). + */ + function setWeights( + uint16 netuid, + uint16[] memory dests, + uint16[] memory weights, + uint64 versionKey + ) external payable; + + /** + * @dev Commits the weights for a neuron. + * + * @param netuid The subnet to commit the weights for (uint16). + * @param commitHash The commit hash for the weights (uint256). + */ + function commitWeights(uint16 netuid, uint256 commitHash) external payable; + + /** + * @dev Reveals the weights for a neuron. + * + * @param netuid The subnet to reveal the weights for (uint16). + * @param uids The unique identifiers for the weights (uint16[]). + * @param values The values of the weights (uint16[]). + * @param salt The salt values for the weights (uint16[]). + * @param versionKey The version key for the weights (uint64). + */ + function revealWeights( + uint16 netuid, + uint16[] memory uids, + uint16[] memory values, + uint16[] memory salt, + uint64 versionKey + ) external payable; }