From bc8c514aa39b307c7e8a944152d189641cba16ff Mon Sep 17 00:00:00 2001 From: open-junius Date: Tue, 10 Dec 2024 21:15:23 +0800 Subject: [PATCH 01/23] add subnet precompile contract --- runtime/src/precompiles/mod.rs | 98 +++++++++++- runtime/src/precompiles/solidity/subnet.abi | 32 ++++ runtime/src/precompiles/solidity/subnet.sol | 10 ++ runtime/src/precompiles/staking.rs | 160 ++++++++++---------- runtime/src/precompiles/subnet.rs | 130 ++++++++++++++++ 5 files changed, 345 insertions(+), 85 deletions(-) create mode 100644 runtime/src/precompiles/solidity/subnet.abi create mode 100644 runtime/src/precompiles/solidity/subnet.sol create mode 100644 runtime/src/precompiles/subnet.rs diff --git a/runtime/src/precompiles/mod.rs b/runtime/src/precompiles/mod.rs index 22f2a4881..ff8efa599 100644 --- a/runtime/src/precompiles/mod.rs +++ b/runtime/src/precompiles/mod.rs @@ -3,21 +3,35 @@ use sp_core::{hashing::keccak_256, H160}; use sp_runtime::AccountId32; use pallet_evm::{ - ExitError, IsPrecompileResult, Precompile, PrecompileFailure, PrecompileHandle, + AddressMapping, BalanceConverter, ExitError, ExitSucceed, HashedAddressMapping, + IsPrecompileResult, Precompile, PrecompileFailure, PrecompileHandle, PrecompileOutput, PrecompileResult, PrecompileSet, }; use pallet_evm_precompile_modexp::Modexp; use pallet_evm_precompile_sha3fips::Sha3FIPS256; use pallet_evm_precompile_simple::{ECRecover, ECRecoverPublicKey, Identity, Ripemd160, Sha256}; +use frame_system::RawOrigin; + +use sp_core::crypto::Ss58Codec; +use sp_core::U256; +use sp_runtime::traits::Dispatchable; +use sp_runtime::traits::{BlakeTwo256, UniqueSaturatedInto}; + +use sp_std::vec; + +use crate::{Runtime, RuntimeCall}; + // Include custom precompiles mod balance_transfer; mod ed25519; mod staking; +mod subnet; use balance_transfer::*; use ed25519::*; use staking::*; +use subnet::*; pub struct FrontierPrecompiles(PhantomData); @@ -37,7 +51,7 @@ where pub fn new() -> Self { Self(Default::default()) } - pub fn used_addresses() -> [H160; 10] { + pub fn used_addresses() -> [H160; 11] { [ hash(1), hash(2), @@ -49,6 +63,7 @@ where hash(EDVERIFY_PRECOMPILE_INDEX), hash(BALANCE_TRANSFER_INDEX), hash(STAKING_PRECOMPILE_INDEX), + hash(SUBNET_PRECOMPILE_INDEX), ] } } @@ -73,6 +88,7 @@ where Some(BalanceTransferPrecompile::execute(handle)) } a if a == hash(STAKING_PRECOMPILE_INDEX) => Some(StakingPrecompile::execute(handle)), + a if a == hash(SUBNET_PRECOMPILE_INDEX) => Some(SubnetPrecompile::execute(handle)), _ => None, } } @@ -118,8 +134,86 @@ pub fn get_slice(data: &[u8], from: usize, to: usize) -> Result<&[u8], Precompil if let Some(slice) = maybe_slice { Ok(slice) } else { + log::error!( + "fail to get slice from data, {:?}, from {}, to {}", + &data, + from, + to + ); Err(PrecompileFailure::Error { exit_status: ExitError::InvalidRange, }) } } + +fn transfer_back_to_caller( + account_id: &AccountId32, + amount: U256, +) -> Result<(), PrecompileFailure> { + // this is staking smart contract's(0x0000000000000000000000000000000000000801) sr25519 address + let smart_contract_account_id = + match AccountId32::from_ss58check("5CwnBK9Ack1mhznmCnwiibCNQc174pYQVktYW3ayRpLm4K2X") { + Ok(addr) => addr, + Err(_) => { + return Err(PrecompileFailure::Error { + exit_status: ExitError::Other("Invalid SS58 address".into()), + }); + } + }; + let amount_sub = + ::BalanceConverter::into_substrate_balance(amount) + .ok_or(ExitError::OutOfFund)?; + + // Create a transfer call from the smart contract to the caller + let transfer_call = + RuntimeCall::Balances(pallet_balances::Call::::transfer_allow_death { + dest: account_id.clone().into(), + value: amount_sub.unique_saturated_into(), + }); + + // Execute the transfer + let transfer_result = + transfer_call.dispatch(RawOrigin::Signed(smart_contract_account_id).into()); + + if let Err(dispatch_error) = transfer_result { + log::error!( + "Transfer back to caller failed. Error: {:?}", + dispatch_error + ); + return Err(PrecompileFailure::Error { + exit_status: ExitError::Other("Transfer back to caller failed".into()), + }); + } + + Ok(()) +} + +fn dispatch(handle: &mut impl PrecompileHandle, call: RuntimeCall) -> PrecompileResult { + let account_id = + as AddressMapping>::into_account_id( + handle.context().caller, + ); + + // Transfer the amount back to the caller before executing the staking operation + // let caller = handle.context().caller; + let amount = handle.context().apparent_value; + + if !amount.is_zero() { + transfer_back_to_caller(&account_id, amount)?; + } + + let result = call.dispatch(RawOrigin::Signed(account_id.clone()).into()); + match &result { + Ok(post_info) => log::info!("Dispatch succeeded. Post info: {:?}", post_info), + Err(dispatch_error) => log::error!("Dispatch failed. Error: {:?}", dispatch_error), + } + match result { + Ok(_) => Ok(PrecompileOutput { + exit_status: ExitSucceed::Returned, + output: vec![], + }), + Err(_) => Err(PrecompileFailure::Error { + exit_status: ExitError::Other("Subtensor call failed".into()), + }), + } +} diff --git a/runtime/src/precompiles/solidity/subnet.abi b/runtime/src/precompiles/solidity/subnet.abi new file mode 100644 index 000000000..a89cf91f1 --- /dev/null +++ b/runtime/src/precompiles/solidity/subnet.abi @@ -0,0 +1,32 @@ +[ + { + "inputs": [ + { + "internalType": "bytes", + "name": "subnetName", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "githubRepo", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "subnetContact", + "type": "bytes" + } + ], + "name": "registerNetwork", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "registerNetwork", + "outputs": [], + "stateMutability": "payable", + "type": "function" + } +] \ No newline at end of file diff --git a/runtime/src/precompiles/solidity/subnet.sol b/runtime/src/precompiles/solidity/subnet.sol new file mode 100644 index 000000000..e2857ad63 --- /dev/null +++ b/runtime/src/precompiles/solidity/subnet.sol @@ -0,0 +1,10 @@ +pragma solidity ^0.8.0; + +address constant ISTAKING_ADDRESS = 0x0000000000000000000000000000000000000803; + +interface ISubnet { + /// Registers a new network without specifying details. + function registerNetwork() external payable; + /// Registers a new network with specified subnet name, GitHub repository, and contact information. + function registerNetwork(bytes subnetName, bytes githubRepo, bytes subnetContact) external payable; +} \ No newline at end of file diff --git a/runtime/src/precompiles/staking.rs b/runtime/src/precompiles/staking.rs index e6237dfcf..fc1e7b512 100644 --- a/runtime/src/precompiles/staking.rs +++ b/runtime/src/precompiles/staking.rs @@ -25,18 +25,12 @@ // - Precompile checks the result of do_remove_stake and, in case of a failure, reverts the transaction. // -use frame_system::RawOrigin; -use pallet_evm::{AddressMapping, BalanceConverter, HashedAddressMapping}; -use pallet_evm::{ - ExitError, ExitSucceed, PrecompileFailure, PrecompileHandle, PrecompileOutput, PrecompileResult, -}; -use sp_core::crypto::Ss58Codec; +use pallet_evm::BalanceConverter; +use pallet_evm::{ExitError, PrecompileFailure, PrecompileHandle, PrecompileResult}; use sp_core::U256; -use sp_runtime::traits::Dispatchable; -use sp_runtime::traits::{BlakeTwo256, UniqueSaturatedInto}; -use sp_runtime::AccountId32; +use sp_runtime::traits::UniqueSaturatedInto; -use crate::precompiles::{get_method_id, get_slice}; +use crate::precompiles::{dispatch, get_method_id, get_slice}; use sp_std::vec; use crate::{Runtime, RuntimeCall}; @@ -78,7 +72,7 @@ impl StakingPrecompile { amount_staked: amount_sub.unique_saturated_into(), }); // Dispatch the add_stake call - Self::dispatch(handle, call) + dispatch(handle, call) } fn remove_stake(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { let hotkey = Self::parse_hotkey(data)?.into(); @@ -98,7 +92,7 @@ impl StakingPrecompile { hotkey, amount_unstaked: amount_sub.unique_saturated_into(), }); - Self::dispatch(handle, call) + dispatch(handle, call) } fn parse_hotkey(data: &[u8]) -> Result<[u8; 32], PrecompileFailure> { @@ -112,75 +106,75 @@ impl StakingPrecompile { Ok(hotkey) } - fn dispatch(handle: &mut impl PrecompileHandle, call: RuntimeCall) -> PrecompileResult { - let account_id = - as AddressMapping>::into_account_id( - handle.context().caller, - ); - - // Transfer the amount back to the caller before executing the staking operation - // let caller = handle.context().caller; - let amount = handle.context().apparent_value; - - if !amount.is_zero() { - Self::transfer_back_to_caller(&account_id, amount)?; - } - - let result = call.dispatch(RawOrigin::Signed(account_id.clone()).into()); - match &result { - Ok(post_info) => log::info!("Dispatch succeeded. Post info: {:?}", post_info), - Err(dispatch_error) => log::error!("Dispatch failed. Error: {:?}", dispatch_error), - } - match result { - Ok(_) => Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: vec![], - }), - Err(_) => Err(PrecompileFailure::Error { - exit_status: ExitError::Other("Subtensor call failed".into()), - }), - } - } - - fn transfer_back_to_caller( - account_id: &AccountId32, - amount: U256, - ) -> Result<(), PrecompileFailure> { - // this is staking smart contract's(0x0000000000000000000000000000000000000801) sr25519 address - let smart_contract_account_id = - match AccountId32::from_ss58check("5CwnBK9Ack1mhznmCnwiibCNQc174pYQVktYW3ayRpLm4K2X") { - Ok(addr) => addr, - Err(_) => { - return Err(PrecompileFailure::Error { - exit_status: ExitError::Other("Invalid SS58 address".into()), - }); - } - }; - let amount_sub = - ::BalanceConverter::into_substrate_balance(amount) - .ok_or(ExitError::OutOfFund)?; - - // Create a transfer call from the smart contract to the caller - let transfer_call = - RuntimeCall::Balances(pallet_balances::Call::::transfer_allow_death { - dest: account_id.clone().into(), - value: amount_sub.unique_saturated_into(), - }); - - // Execute the transfer - let transfer_result = - transfer_call.dispatch(RawOrigin::Signed(smart_contract_account_id).into()); - - if let Err(dispatch_error) = transfer_result { - log::error!( - "Transfer back to caller failed. Error: {:?}", - dispatch_error - ); - return Err(PrecompileFailure::Error { - exit_status: ExitError::Other("Transfer back to caller failed".into()), - }); - } - - Ok(()) - } + // fn dispatch(handle: &mut impl PrecompileHandle, call: RuntimeCall) -> PrecompileResult { + // let account_id = + // as AddressMapping>::into_account_id( + // handle.context().caller, + // ); + + // // Transfer the amount back to the caller before executing the staking operation + // // let caller = handle.context().caller; + // let amount = handle.context().apparent_value; + + // if !amount.is_zero() { + // Self::transfer_back_to_caller(&account_id, amount)?; + // } + + // let result = call.dispatch(RawOrigin::Signed(account_id.clone()).into()); + // match &result { + // Ok(post_info) => log::info!("Dispatch succeeded. Post info: {:?}", post_info), + // Err(dispatch_error) => log::error!("Dispatch failed. Error: {:?}", dispatch_error), + // } + // match result { + // Ok(_) => Ok(PrecompileOutput { + // exit_status: ExitSucceed::Returned, + // output: vec![], + // }), + // Err(_) => Err(PrecompileFailure::Error { + // exit_status: ExitError::Other("Subtensor call failed".into()), + // }), + // } + // } + + // fn transfer_back_to_caller( + // account_id: &AccountId32, + // amount: U256, + // ) -> Result<(), PrecompileFailure> { + // // this is staking smart contract's(0x0000000000000000000000000000000000000801) sr25519 address + // let smart_contract_account_id = + // match AccountId32::from_ss58check("5CwnBK9Ack1mhznmCnwiibCNQc174pYQVktYW3ayRpLm4K2X") { + // Ok(addr) => addr, + // Err(_) => { + // return Err(PrecompileFailure::Error { + // exit_status: ExitError::Other("Invalid SS58 address".into()), + // }); + // } + // }; + // let amount_sub = + // ::BalanceConverter::into_substrate_balance(amount) + // .ok_or(ExitError::OutOfFund)?; + + // // Create a transfer call from the smart contract to the caller + // let transfer_call = + // RuntimeCall::Balances(pallet_balances::Call::::transfer_allow_death { + // dest: account_id.clone().into(), + // value: amount_sub.unique_saturated_into(), + // }); + + // // Execute the transfer + // let transfer_result = + // transfer_call.dispatch(RawOrigin::Signed(smart_contract_account_id).into()); + + // if let Err(dispatch_error) = transfer_result { + // log::error!( + // "Transfer back to caller failed. Error: {:?}", + // dispatch_error + // ); + // return Err(PrecompileFailure::Error { + // exit_status: ExitError::Other("Transfer back to caller failed".into()), + // }); + // } + + // Ok(()) + // } } diff --git a/runtime/src/precompiles/subnet.rs b/runtime/src/precompiles/subnet.rs new file mode 100644 index 000000000..a74bbf53a --- /dev/null +++ b/runtime/src/precompiles/subnet.rs @@ -0,0 +1,130 @@ +use crate::precompiles::{dispatch, get_method_id, get_slice}; +use crate::{Runtime, RuntimeCall}; +use pallet_evm::{ExitError, PrecompileFailure, PrecompileHandle, PrecompileResult}; +use sp_std::vec; + +pub const SUBNET_PRECOMPILE_INDEX: u64 = 2051; +// three bytes with max lenght 1K +pub const MAX_PARAMETER_SIZE: usize = 3 * 1024; + +pub struct SubnetPrecompile; + +impl SubnetPrecompile { + pub fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult { + let txdata = handle.input(); + if txdata.len() > MAX_PARAMETER_SIZE { + log::error!("the length of subnet call is {} ", txdata.len()); + return Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }); + } + let method_id = get_slice(txdata, 0, 4)?; + let method_input = txdata + .get(4..) + .map_or_else(vec::Vec::new, |slice| slice.to_vec()); // Avoiding borrowing conflicts + + match method_id { + id if id == get_method_id("registerNetwork(bytes,bytes,bytes)") => { + Self::register_network(handle, &method_input) + } + id if id == get_method_id("registerNetwork()") => { + Self::register_network(handle, &[0_u8; 0]) + } + _ => Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }), + } + } + + fn register_network(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { + let call = if data.is_empty() { + RuntimeCall::SubtensorModule( + pallet_subtensor::Call::::register_network_with_identity { + identity: None, + }, + ) + } else { + let (subnet_name, github_repo, subnet_contact) = + Self::parse_register_network_parameters(data)?; + + let identity: pallet_subtensor::SubnetIdentityOf = pallet_subtensor::SubnetIdentityOf { + subnet_name, + github_repo, + subnet_contact, + }; + + // Create the register_network callcle + RuntimeCall::SubtensorModule( + pallet_subtensor::Call::::register_network_with_identity { + identity: Some(identity), + }, + ) + }; + + // Dispatch the register_network call + dispatch(handle, call) + } + + fn parse_register_network_parameters( + data: &[u8], + ) -> Result<(vec::Vec, vec::Vec, vec::Vec), PrecompileFailure> { + let mut buf = [0_u8; 4]; + + // get all start point for three data items: name, repo and contact + buf.copy_from_slice(get_slice(data, 28, 32)?); + let subnet_name_start: usize = u32::from_be_bytes(buf) as usize; + + buf.copy_from_slice(get_slice(data, 60, 64)?); + let github_repo_start: usize = u32::from_be_bytes(buf) as usize; + + buf.copy_from_slice(get_slice(data, 92, 96)?); + let subnet_contact_start: usize = u32::from_be_bytes(buf) as usize; + + // get name + buf.copy_from_slice(get_slice( + data, + subnet_name_start + 28, + subnet_name_start + 32, + )?); + let subnet_name_len: usize = u32::from_be_bytes(buf) as usize; + + let mut name_vec = vec![0; subnet_name_len]; + name_vec.copy_from_slice(get_slice( + data, + subnet_name_start + 32, + subnet_name_start + subnet_name_len + 32, + )?); + + // get repo data + buf.copy_from_slice(get_slice( + data, + github_repo_start + 28, + github_repo_start + 32, + )?); + let github_repo_len: usize = u32::from_be_bytes(buf) as usize; + + let mut repo_vec = vec![0; github_repo_len]; + repo_vec.copy_from_slice(get_slice( + data, + github_repo_start + 32, + github_repo_start + github_repo_len + 32, + )?); + + // get contact data + buf.copy_from_slice(get_slice( + data, + subnet_contact_start + 28, + subnet_contact_start + 32, + )?); + let subnet_contact_len: usize = u32::from_be_bytes(buf) as usize; + + let mut contact_vec = vec![0; subnet_contact_len]; + contact_vec.copy_from_slice(get_slice( + data, + subnet_contact_start + 32, + subnet_contact_start + subnet_contact_len + 32, + )?); + + Ok((name_vec, repo_vec, contact_vec)) + } +} From 157032bc03c1341adb5651ec481902584572034a Mon Sep 17 00:00:00 2001 From: open-junius Date: Tue, 10 Dec 2024 23:55:11 +0800 Subject: [PATCH 02/23] remove commented code --- runtime/src/precompiles/staking.rs | 72 ------------------------------ 1 file changed, 72 deletions(-) diff --git a/runtime/src/precompiles/staking.rs b/runtime/src/precompiles/staking.rs index fc1e7b512..e8d33c05e 100644 --- a/runtime/src/precompiles/staking.rs +++ b/runtime/src/precompiles/staking.rs @@ -105,76 +105,4 @@ impl StakingPrecompile { hotkey.copy_from_slice(get_slice(data, 0, 32)?); Ok(hotkey) } - - // fn dispatch(handle: &mut impl PrecompileHandle, call: RuntimeCall) -> PrecompileResult { - // let account_id = - // as AddressMapping>::into_account_id( - // handle.context().caller, - // ); - - // // Transfer the amount back to the caller before executing the staking operation - // // let caller = handle.context().caller; - // let amount = handle.context().apparent_value; - - // if !amount.is_zero() { - // Self::transfer_back_to_caller(&account_id, amount)?; - // } - - // let result = call.dispatch(RawOrigin::Signed(account_id.clone()).into()); - // match &result { - // Ok(post_info) => log::info!("Dispatch succeeded. Post info: {:?}", post_info), - // Err(dispatch_error) => log::error!("Dispatch failed. Error: {:?}", dispatch_error), - // } - // match result { - // Ok(_) => Ok(PrecompileOutput { - // exit_status: ExitSucceed::Returned, - // output: vec![], - // }), - // Err(_) => Err(PrecompileFailure::Error { - // exit_status: ExitError::Other("Subtensor call failed".into()), - // }), - // } - // } - - // fn transfer_back_to_caller( - // account_id: &AccountId32, - // amount: U256, - // ) -> Result<(), PrecompileFailure> { - // // this is staking smart contract's(0x0000000000000000000000000000000000000801) sr25519 address - // let smart_contract_account_id = - // match AccountId32::from_ss58check("5CwnBK9Ack1mhznmCnwiibCNQc174pYQVktYW3ayRpLm4K2X") { - // Ok(addr) => addr, - // Err(_) => { - // return Err(PrecompileFailure::Error { - // exit_status: ExitError::Other("Invalid SS58 address".into()), - // }); - // } - // }; - // let amount_sub = - // ::BalanceConverter::into_substrate_balance(amount) - // .ok_or(ExitError::OutOfFund)?; - - // // Create a transfer call from the smart contract to the caller - // let transfer_call = - // RuntimeCall::Balances(pallet_balances::Call::::transfer_allow_death { - // dest: account_id.clone().into(), - // value: amount_sub.unique_saturated_into(), - // }); - - // // Execute the transfer - // let transfer_result = - // transfer_call.dispatch(RawOrigin::Signed(smart_contract_account_id).into()); - - // if let Err(dispatch_error) = transfer_result { - // log::error!( - // "Transfer back to caller failed. Error: {:?}", - // dispatch_error - // ); - // return Err(PrecompileFailure::Error { - // exit_status: ExitError::Other("Transfer back to caller failed".into()), - // }); - // } - - // Ok(()) - // } } From 5435ccd467dd53a8200285c52550070a5ee05801 Mon Sep 17 00:00:00 2001 From: open-junius Date: Wed, 11 Dec 2024 00:17:09 +0800 Subject: [PATCH 03/23] refactor code --- runtime/src/precompiles/mod.rs | 28 +++++++++++++++++----------- runtime/src/precompiles/staking.rs | 8 +++++--- runtime/src/precompiles/subnet.rs | 6 +++++- 3 files changed, 27 insertions(+), 15 deletions(-) diff --git a/runtime/src/precompiles/mod.rs b/runtime/src/precompiles/mod.rs index ff8efa599..7f54d2e3c 100644 --- a/runtime/src/precompiles/mod.rs +++ b/runtime/src/precompiles/mod.rs @@ -146,20 +146,22 @@ pub fn get_slice(data: &[u8], from: usize, to: usize) -> Result<&[u8], Precompil } } +/// The function return the token to smart contract fn transfer_back_to_caller( + smart_contract_address: &str, account_id: &AccountId32, amount: U256, ) -> Result<(), PrecompileFailure> { // this is staking smart contract's(0x0000000000000000000000000000000000000801) sr25519 address - let smart_contract_account_id = - match AccountId32::from_ss58check("5CwnBK9Ack1mhznmCnwiibCNQc174pYQVktYW3ayRpLm4K2X") { - Ok(addr) => addr, - Err(_) => { - return Err(PrecompileFailure::Error { - exit_status: ExitError::Other("Invalid SS58 address".into()), - }); - } - }; + let smart_contract_account_id = match AccountId32::from_ss58check(smart_contract_address) { + // match AccountId32::from_ss58check("5CwnBK9Ack1mhznmCnwiibCNQc174pYQVktYW3ayRpLm4K2X") { + Ok(addr) => addr, + Err(_) => { + return Err(PrecompileFailure::Error { + exit_status: ExitError::Other("Invalid SS58 address".into()), + }); + } + }; let amount_sub = ::BalanceConverter::into_substrate_balance(amount) .ok_or(ExitError::OutOfFund)?; @@ -188,7 +190,11 @@ fn transfer_back_to_caller( Ok(()) } -fn dispatch(handle: &mut impl PrecompileHandle, call: RuntimeCall) -> PrecompileResult { +fn dispatch( + handle: &mut impl PrecompileHandle, + call: RuntimeCall, + smart_contract_address: &str, +) -> PrecompileResult { let account_id = as AddressMapping>::into_account_id( handle.context().caller, @@ -199,7 +205,7 @@ fn dispatch(handle: &mut impl PrecompileHandle, call: RuntimeCall) -> Precompile let amount = handle.context().apparent_value; if !amount.is_zero() { - transfer_back_to_caller(&account_id, amount)?; + transfer_back_to_caller(smart_contract_address, &account_id, amount)?; } let result = call.dispatch(RawOrigin::Signed(account_id.clone()).into()); diff --git a/runtime/src/precompiles/staking.rs b/runtime/src/precompiles/staking.rs index e8d33c05e..9454485ff 100644 --- a/runtime/src/precompiles/staking.rs +++ b/runtime/src/precompiles/staking.rs @@ -35,7 +35,9 @@ use sp_std::vec; use crate::{Runtime, RuntimeCall}; pub const STAKING_PRECOMPILE_INDEX: u64 = 2049; - +// this is staking smart contract's(0x0000000000000000000000000000000000000801) sr25519 address +pub const STAKING_CONTRACT_ADDRESS: &'static str = + "5CwnBK9Ack1mhznmCnwiibCNQc174pYQVktYW3ayRpLm4K2X"; pub struct StakingPrecompile; impl StakingPrecompile { @@ -72,7 +74,7 @@ impl StakingPrecompile { amount_staked: amount_sub.unique_saturated_into(), }); // Dispatch the add_stake call - dispatch(handle, call) + dispatch(handle, call, STAKING_CONTRACT_ADDRESS) } fn remove_stake(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { let hotkey = Self::parse_hotkey(data)?.into(); @@ -92,7 +94,7 @@ impl StakingPrecompile { hotkey, amount_unstaked: amount_sub.unique_saturated_into(), }); - dispatch(handle, call) + dispatch(handle, call, STAKING_CONTRACT_ADDRESS) } fn parse_hotkey(data: &[u8]) -> Result<[u8; 32], PrecompileFailure> { diff --git a/runtime/src/precompiles/subnet.rs b/runtime/src/precompiles/subnet.rs index a74bbf53a..f68e089b3 100644 --- a/runtime/src/precompiles/subnet.rs +++ b/runtime/src/precompiles/subnet.rs @@ -7,6 +7,10 @@ pub const SUBNET_PRECOMPILE_INDEX: u64 = 2051; // three bytes with max lenght 1K pub const MAX_PARAMETER_SIZE: usize = 3 * 1024; +// this is staking smart contract's(0x0000000000000000000000000000000000000803) sr25519 address +pub const STAKING_CONTRACT_ADDRESS: &'static str = + "5DPSUCb5mZFfizvBDSnRoAqmxV5Bmov2CS3xV773qU6VP1w2"; + pub struct SubnetPrecompile; impl SubnetPrecompile { @@ -62,7 +66,7 @@ impl SubnetPrecompile { }; // Dispatch the register_network call - dispatch(handle, call) + dispatch(handle, call, STAKING_CONTRACT_ADDRESS) } fn parse_register_network_parameters( From 71699085a6d9b554630abb4d9d19e54c607d33c1 Mon Sep 17 00:00:00 2001 From: open-junius Date: Wed, 11 Dec 2024 09:42:35 +0800 Subject: [PATCH 04/23] fix clippy --- runtime/src/precompiles/staking.rs | 3 +-- runtime/src/precompiles/subnet.rs | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/runtime/src/precompiles/staking.rs b/runtime/src/precompiles/staking.rs index 9454485ff..31c692cd6 100644 --- a/runtime/src/precompiles/staking.rs +++ b/runtime/src/precompiles/staking.rs @@ -36,8 +36,7 @@ use sp_std::vec; use crate::{Runtime, RuntimeCall}; pub const STAKING_PRECOMPILE_INDEX: u64 = 2049; // this is staking smart contract's(0x0000000000000000000000000000000000000801) sr25519 address -pub const STAKING_CONTRACT_ADDRESS: &'static str = - "5CwnBK9Ack1mhznmCnwiibCNQc174pYQVktYW3ayRpLm4K2X"; +pub const STAKING_CONTRACT_ADDRESS: &str = "5CwnBK9Ack1mhznmCnwiibCNQc174pYQVktYW3ayRpLm4K2X"; pub struct StakingPrecompile; impl StakingPrecompile { diff --git a/runtime/src/precompiles/subnet.rs b/runtime/src/precompiles/subnet.rs index f68e089b3..9230a01e9 100644 --- a/runtime/src/precompiles/subnet.rs +++ b/runtime/src/precompiles/subnet.rs @@ -8,8 +8,7 @@ pub const SUBNET_PRECOMPILE_INDEX: u64 = 2051; pub const MAX_PARAMETER_SIZE: usize = 3 * 1024; // this is staking smart contract's(0x0000000000000000000000000000000000000803) sr25519 address -pub const STAKING_CONTRACT_ADDRESS: &'static str = - "5DPSUCb5mZFfizvBDSnRoAqmxV5Bmov2CS3xV773qU6VP1w2"; +pub const STAKING_CONTRACT_ADDRESS: &str = "5DPSUCb5mZFfizvBDSnRoAqmxV5Bmov2CS3xV773qU6VP1w2"; pub struct SubnetPrecompile; From cdda2f68ac3d50dc2f9c3631328f4bd8f6abbe90 Mon Sep 17 00:00:00 2001 From: open-junius Date: Wed, 11 Dec 2024 10:59:41 +0800 Subject: [PATCH 05/23] fix compile error --- runtime/src/precompiles/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/precompiles/mod.rs b/runtime/src/precompiles/mod.rs index 4d8abdac2..398a80a05 100644 --- a/runtime/src/precompiles/mod.rs +++ b/runtime/src/precompiles/mod.rs @@ -53,7 +53,7 @@ where pub fn new() -> Self { Self(Default::default()) } - pub fn used_addresses() -> [H160; 11] { + pub fn used_addresses() -> [H160; 12] { [ hash(1), hash(2), From e565825b2a0b852d614bc5496f5d43b17763552d Mon Sep 17 00:00:00 2001 From: open-junius Date: Mon, 16 Dec 2024 20:30:54 +0800 Subject: [PATCH 06/23] add memory storage --- runtime/src/precompiles/solidity/subnet.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/precompiles/solidity/subnet.sol b/runtime/src/precompiles/solidity/subnet.sol index e2857ad63..581788b03 100644 --- a/runtime/src/precompiles/solidity/subnet.sol +++ b/runtime/src/precompiles/solidity/subnet.sol @@ -6,5 +6,5 @@ interface ISubnet { /// Registers a new network without specifying details. function registerNetwork() external payable; /// Registers a new network with specified subnet name, GitHub repository, and contact information. - function registerNetwork(bytes subnetName, bytes githubRepo, bytes subnetContact) external payable; + function registerNetwork(bytes memory subnetName, bytes memory githubRepo, bytes memory subnetContact) external payable; } \ No newline at end of file From 6a0f6f91accff4748b1ccafe258ca6f1a44536e8 Mon Sep 17 00:00:00 2001 From: open-junius Date: Fri, 20 Dec 2024 18:33:52 +0800 Subject: [PATCH 07/23] add subnets precompile --- runtime/src/precompiles/mod.rs | 6 +- runtime/src/precompiles/solidity/staking.sol | 44 +++++++------ runtime/src/precompiles/solidity/subnets.abi | 20 ++++++ runtime/src/precompiles/solidity/subnets.sol | 14 ++++ runtime/src/precompiles/subnets.rs | 68 ++++++++++++++++++++ 5 files changed, 131 insertions(+), 21 deletions(-) create mode 100644 runtime/src/precompiles/solidity/subnets.abi create mode 100644 runtime/src/precompiles/solidity/subnets.sol create mode 100644 runtime/src/precompiles/subnets.rs diff --git a/runtime/src/precompiles/mod.rs b/runtime/src/precompiles/mod.rs index e13516e95..20d013194 100644 --- a/runtime/src/precompiles/mod.rs +++ b/runtime/src/precompiles/mod.rs @@ -15,11 +15,13 @@ mod balance_transfer; mod ed25519; mod metagraph; mod staking; +mod subnets; use balance_transfer::*; use ed25519::*; use metagraph::*; use staking::*; +use subnets::*; pub struct FrontierPrecompiles(PhantomData); @@ -39,7 +41,7 @@ where pub fn new() -> Self { Self(Default::default()) } - pub fn used_addresses() -> [H160; 11] { + pub fn used_addresses() -> [H160; 12] { [ hash(1), hash(2), @@ -52,6 +54,7 @@ where hash(BALANCE_TRANSFER_INDEX), hash(STAKING_PRECOMPILE_INDEX), hash(METAGRAPH_PRECOMPILE_INDEX), + hash(SUBNETS_PRECOMPILE_INDEX), ] } } @@ -79,6 +82,7 @@ where a if a == hash(METAGRAPH_PRECOMPILE_INDEX) => { Some(MetagraphPrecompile::execute(handle)) } + a if a == hash(SUBNETS_PRECOMPILE_INDEX) => Some(SubnetsPrecompile::execute(handle)), _ => None, } diff --git a/runtime/src/precompiles/solidity/staking.sol b/runtime/src/precompiles/solidity/staking.sol index ec7fb7297..057fa2dce 100644 --- a/runtime/src/precompiles/solidity/staking.sol +++ b/runtime/src/precompiles/solidity/staking.sol @@ -3,26 +3,26 @@ pragma solidity ^0.8.0; address constant ISTAKING_ADDRESS = 0x0000000000000000000000000000000000000801; interface IStaking { - /** - * @dev Adds a subtensor stake corresponding to the value sent with the transaction, associated - * with the `hotkey`. - * - * This function allows external accounts and contracts to stake TAO into the subtensor pallet, - * which effectively calls `add_stake` on the subtensor pallet with specified hotkey as a parameter - * and coldkey being the hashed address mapping of H160 sender address to Substrate ss58 address as - * implemented in Frontier HashedAddressMapping: - * https://github.com/polkadot-evm/frontier/blob/2e219e17a526125da003e64ef22ec037917083fa/frame/evm/src/lib.rs#L739 - * - * @param hotkey The hotkey public key (32 bytes). - * @param netuid The subnet to stake to (uint16). Currently a noop, functionality will be enabled with RAO. - * - * Requirements: - * - `hotkey` must be a valid hotkey registered on the network, ensuring that the stake is - * correctly attributed. - */ - function addStake(bytes32 hotkey, uint16 netuid) external payable; + /** + * @dev Adds a subtensor stake corresponding to the value sent with the transaction, associated + * with the `hotkey`. + * + * This function allows external accounts and contracts to stake TAO into the subtensor pallet, + * which effectively calls `add_stake` on the subtensor pallet with specified hotkey as a parameter + * and coldkey being the hashed address mapping of H160 sender address to Substrate ss58 address as + * implemented in Frontier HashedAddressMapping: + * https://github.com/polkadot-evm/frontier/blob/2e219e17a526125da003e64ef22ec037917083fa/frame/evm/src/lib.rs#L739 + * + * @param hotkey The hotkey public key (32 bytes). + * @param netuid The subnet to stake to (uint16). Currently a noop, functionality will be enabled with RAO. + * + * Requirements: + * - `hotkey` must be a valid hotkey registered on the network, ensuring that the stake is + * correctly attributed. + */ + function addStake(bytes32 hotkey, uint16 netuid) external payable; - /** + /** * @dev Removes a subtensor stake `amount` from the specified `hotkey`. * * This function allows external accounts and contracts to unstake TAO from the subtensor pallet, @@ -41,5 +41,9 @@ interface IStaking { * correctly attributed. * - The existing stake amount must be not lower than specified amount */ - function removeStake(bytes32 hotkey, uint256 amount, uint16 netuid) external; + function removeStake( + bytes32 hotkey, + uint256 amount, + uint16 netuid + ) external; } diff --git a/runtime/src/precompiles/solidity/subnets.abi b/runtime/src/precompiles/solidity/subnets.abi new file mode 100644 index 000000000..09a14eb2d --- /dev/null +++ b/runtime/src/precompiles/solidity/subnets.abi @@ -0,0 +1,20 @@ +[ + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + } + ], + "name": "burnedRegister", + "outputs": [], + "stateMutability": "payable", + "type": "function" + } +] diff --git a/runtime/src/precompiles/solidity/subnets.sol b/runtime/src/precompiles/solidity/subnets.sol new file mode 100644 index 000000000..5cbf72779 --- /dev/null +++ b/runtime/src/precompiles/solidity/subnets.sol @@ -0,0 +1,14 @@ +pragma solidity ^0.8.0; + +address constant ISUBNETS_ADDRESS = 0x0000000000000000000000000000000000000804; + +interface ISubnets { + /** + * @dev Registers a neuron by calling `do_burned_registration` internally with the origin set to the ss58 mirror of the H160 address. + * This allows the H160 to further call neuron-related methods and receive emissions. + * + * @param netuid The subnet to register the neuron to (uint16). + * @param hotkey The hotkey public key (32 bytes). + */ + function burnedRegister(uint16 netuid, bytes32 hotkey) external payable; +} diff --git a/runtime/src/precompiles/subnets.rs b/runtime/src/precompiles/subnets.rs new file mode 100644 index 000000000..ffc0c71a0 --- /dev/null +++ b/runtime/src/precompiles/subnets.rs @@ -0,0 +1,68 @@ +use pallet_evm::{ + ExitError, ExitSucceed, PrecompileFailure, PrecompileHandle, PrecompileOutput, PrecompileResult, +}; + +use crate::precompiles::{get_method_id, get_slice}; +use sp_std::vec; + +use crate::{Runtime, RuntimeCall}; +pub const SUBNETS_PRECOMPILE_INDEX: u64 = 2052; + +// this is subnets smart contract's(0x0000000000000000000000000000000000000804) sr25519 address +pub const SUBNETS_CONTRACT_ADDRESS: &str = "5GKZiUUgTnWSz3BgiVBMehEKkLszsG4ZXnvgWpWFUFKqrqyn"; + +pub struct SubnetsPrecompile; + +impl SubnetsPrecompile { + pub fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult { + let txdata = handle.input(); + let method_id = get_slice(txdata, 0, 4)?; + let method_input = txdata + .get(4..) + .map_or_else(vec::Vec::new, |slice| slice.to_vec()); // Avoiding borrowing conflicts + + match method_id { + id if id == get_method_id("burnedRegister(uint16,bytes32)") => { + Self::burned_register(handle, &method_input) + } + + _ => Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }), + } + } + + pub fn burned_register(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { + let (netuid, hotkey) = Self::parse_netuid_hotkey_parameter(data)?; + let call = + RuntimeCall::SubtensorModule(pallet_subtensor::Call::::burned_register { + netuid, + hotkey: hotkey.into(), + }); + Self::dispatch(handle, call) + } + + fn parse_netuid_hotkey_parameter(data: &[u8]) -> Result<(u16, [u8; 32]), PrecompileFailure> { + if data.len() < 64 { + 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 mut parameter = [0u8; 32]; + parameter.copy_from_slice(get_slice(data, 32, 64)?); + + Ok((netuid, parameter)) + } + + // will remove it after merge with other PR + fn dispatch(_handle: &mut impl PrecompileHandle, _call: RuntimeCall) -> PrecompileResult { + Ok(PrecompileOutput { + exit_status: ExitSucceed::Returned, + output: vec![], + }) + } +} From 164323825311e1a23918cb94fefcd1221e2061a7 Mon Sep 17 00:00:00 2001 From: open-junius Date: Thu, 16 Jan 2025 20:41:55 +0800 Subject: [PATCH 08/23] fix compilation --- runtime/src/precompiles/subnet.rs | 33 +++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/runtime/src/precompiles/subnet.rs b/runtime/src/precompiles/subnet.rs index 9230a01e9..01eb331e6 100644 --- a/runtime/src/precompiles/subnet.rs +++ b/runtime/src/precompiles/subnet.rs @@ -27,10 +27,10 @@ impl SubnetPrecompile { .map_or_else(vec::Vec::new, |slice| slice.to_vec()); // Avoiding borrowing conflicts match method_id { - id if id == get_method_id("registerNetwork(bytes,bytes,bytes)") => { + id if id == get_method_id("registerNetwork(bytes32,bytes,bytes,bytes)") => { Self::register_network(handle, &method_input) } - id if id == get_method_id("registerNetwork()") => { + id if id == get_method_id("registerNetwork(bytes32)") => { Self::register_network(handle, &[0_u8; 0]) } _ => Err(PrecompileFailure::Error { @@ -41,13 +41,15 @@ impl SubnetPrecompile { fn register_network(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { let call = if data.is_empty() { + let hotkey = Self::parse_pub_key(data)?.into(); RuntimeCall::SubtensorModule( pallet_subtensor::Call::::register_network_with_identity { + hotkey, identity: None, }, ) } else { - let (subnet_name, github_repo, subnet_contact) = + let (pubkey, subnet_name, github_repo, subnet_contact) = Self::parse_register_network_parameters(data)?; let identity: pallet_subtensor::SubnetIdentityOf = pallet_subtensor::SubnetIdentityOf { @@ -59,6 +61,7 @@ impl SubnetPrecompile { // Create the register_network callcle RuntimeCall::SubtensorModule( pallet_subtensor::Call::::register_network_with_identity { + hotkey: pubkey.into(), identity: Some(identity), }, ) @@ -68,19 +71,33 @@ impl SubnetPrecompile { dispatch(handle, call, STAKING_CONTRACT_ADDRESS) } + fn parse_pub_key(data: &[u8]) -> Result<[u8; 32], PrecompileFailure> { + if data.len() < 32 { + return Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }); + } + let mut pubkey = [0u8; 32]; + pubkey.copy_from_slice(get_slice(data, 0, 32)?); + Ok(pubkey) + } + fn parse_register_network_parameters( data: &[u8], - ) -> Result<(vec::Vec, vec::Vec, vec::Vec), PrecompileFailure> { + ) -> Result<([u8; 32], vec::Vec, vec::Vec, vec::Vec), PrecompileFailure> { + let mut pubkey = [0u8; 32]; + pubkey.copy_from_slice(get_slice(data, 0, 32)?); + let mut buf = [0_u8; 4]; // get all start point for three data items: name, repo and contact - buf.copy_from_slice(get_slice(data, 28, 32)?); + buf.copy_from_slice(get_slice(data, 60, 64)?); let subnet_name_start: usize = u32::from_be_bytes(buf) as usize; - buf.copy_from_slice(get_slice(data, 60, 64)?); + buf.copy_from_slice(get_slice(data, 92, 96)?); let github_repo_start: usize = u32::from_be_bytes(buf) as usize; - buf.copy_from_slice(get_slice(data, 92, 96)?); + buf.copy_from_slice(get_slice(data, 124, 128)?); let subnet_contact_start: usize = u32::from_be_bytes(buf) as usize; // get name @@ -128,6 +145,6 @@ impl SubnetPrecompile { subnet_contact_start + subnet_contact_len + 32, )?); - Ok((name_vec, repo_vec, contact_vec)) + Ok((pubkey, name_vec, repo_vec, contact_vec)) } } From 5040e52262f9c20e496c20d391ee53c7eb94b2dc Mon Sep 17 00:00:00 2001 From: open-junius Date: Thu, 16 Jan 2025 21:09:33 +0800 Subject: [PATCH 09/23] update sol --- runtime/src/precompiles/solidity/subnet.abi | 25 +++++++++++++++------ runtime/src/precompiles/solidity/subnet.sol | 15 ++++++++----- 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/runtime/src/precompiles/solidity/subnet.abi b/runtime/src/precompiles/solidity/subnet.abi index a89cf91f1..a11879241 100644 --- a/runtime/src/precompiles/solidity/subnet.abi +++ b/runtime/src/precompiles/solidity/subnet.abi @@ -1,6 +1,24 @@ [ { "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + } + ], + "name": "registerNetwork", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "hotkey", + "type": "bytes32" + }, { "internalType": "bytes", "name": "subnetName", @@ -21,12 +39,5 @@ "outputs": [], "stateMutability": "payable", "type": "function" - }, - { - "inputs": [], - "name": "registerNetwork", - "outputs": [], - "stateMutability": "payable", - "type": "function" } ] \ No newline at end of file diff --git a/runtime/src/precompiles/solidity/subnet.sol b/runtime/src/precompiles/solidity/subnet.sol index 581788b03..c3d0a4a13 100644 --- a/runtime/src/precompiles/solidity/subnet.sol +++ b/runtime/src/precompiles/solidity/subnet.sol @@ -3,8 +3,13 @@ pragma solidity ^0.8.0; address constant ISTAKING_ADDRESS = 0x0000000000000000000000000000000000000803; interface ISubnet { - /// Registers a new network without specifying details. - function registerNetwork() external payable; - /// Registers a new network with specified subnet name, GitHub repository, and contact information. - function registerNetwork(bytes memory subnetName, bytes memory githubRepo, bytes memory subnetContact) external payable; -} \ No newline at end of file + /// Registers a new network without specifying details. + function registerNetwork(bytes32 hotkey) external payable; + /// Registers a new network with specified subnet name, GitHub repository, and contact information. + function registerNetwork( + bytes32 hotkey, + bytes memory subnetName, + bytes memory githubRepo, + bytes memory subnetContact + ) external payable; +} From 1341e7ac336119b36eea34b0a47bc7cd5544ce59 Mon Sep 17 00:00:00 2001 From: open-junius Date: Fri, 17 Jan 2025 22:40:33 +0800 Subject: [PATCH 10/23] rename contract --- runtime/src/precompiles/mod.rs | 8 ++++---- runtime/src/precompiles/{subnets.rs => neuron.rs} | 12 ++++++------ .../precompiles/solidity/{subnets.abi => neuron.abi} | 0 .../precompiles/solidity/{subnets.sol => neuron.sol} | 4 ++-- 4 files changed, 12 insertions(+), 12 deletions(-) rename runtime/src/precompiles/{subnets.rs => neuron.rs} (83%) rename runtime/src/precompiles/solidity/{subnets.abi => neuron.abi} (100%) rename runtime/src/precompiles/solidity/{subnets.sol => neuron.sol} (82%) diff --git a/runtime/src/precompiles/mod.rs b/runtime/src/precompiles/mod.rs index c1524e19f..3d2c19ae9 100644 --- a/runtime/src/precompiles/mod.rs +++ b/runtime/src/precompiles/mod.rs @@ -26,16 +26,16 @@ use crate::{Runtime, RuntimeCall}; mod balance_transfer; mod ed25519; mod metagraph; +mod neuron; mod staking; mod subnet; -mod subnets; use balance_transfer::*; use ed25519::*; use metagraph::*; +use neuron::*; use staking::*; use subnet::*; -use subnets::*; pub struct FrontierPrecompiles(PhantomData); impl Default for FrontierPrecompiles where @@ -67,7 +67,7 @@ where hash(STAKING_PRECOMPILE_INDEX), hash(SUBNET_PRECOMPILE_INDEX), hash(METAGRAPH_PRECOMPILE_INDEX), - hash(SUBNETS_PRECOMPILE_INDEX), + hash(NEURON_PRECOMPILE_INDEX), ] } } @@ -96,7 +96,7 @@ where a if a == hash(METAGRAPH_PRECOMPILE_INDEX) => { Some(MetagraphPrecompile::execute(handle)) } - a if a == hash(SUBNETS_PRECOMPILE_INDEX) => Some(SubnetsPrecompile::execute(handle)), + a if a == hash(NEURON_PRECOMPILE_INDEX) => Some(NeuronPrecompile::execute(handle)), _ => None, } diff --git a/runtime/src/precompiles/subnets.rs b/runtime/src/precompiles/neuron.rs similarity index 83% rename from runtime/src/precompiles/subnets.rs rename to runtime/src/precompiles/neuron.rs index 19be425d5..341683cb3 100644 --- a/runtime/src/precompiles/subnets.rs +++ b/runtime/src/precompiles/neuron.rs @@ -4,14 +4,14 @@ use crate::precompiles::{dispatch, get_method_id, get_slice}; use sp_std::vec; use crate::{Runtime, RuntimeCall}; -pub const SUBNETS_PRECOMPILE_INDEX: u64 = 2052; +pub const NEURON_PRECOMPILE_INDEX: u64 = 2052; -// this is subnets smart contract's(0x0000000000000000000000000000000000000804) sr25519 address -pub const SUBNETS_CONTRACT_ADDRESS: &str = "5GKZiUUgTnWSz3BgiVBMehEKkLszsG4ZXnvgWpWFUFKqrqyn"; +// this is neuron smart contract's(0x0000000000000000000000000000000000000804) sr25519 address +pub const NEURON_CONTRACT_ADDRESS: &str = "5GKZiUUgTnWSz3BgiVBMehEKkLszsG4ZXnvgWpWFUFKqrqyn"; -pub struct SubnetsPrecompile; +pub struct NeuronPrecompile; -impl SubnetsPrecompile { +impl NeuronPrecompile { pub fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult { let txdata = handle.input(); let method_id = get_slice(txdata, 0, 4)?; @@ -37,7 +37,7 @@ impl SubnetsPrecompile { netuid, hotkey: hotkey.into(), }); - dispatch(handle, call, SUBNETS_CONTRACT_ADDRESS) + dispatch(handle, call, NEURON_CONTRACT_ADDRESS) } fn parse_netuid_hotkey_parameter(data: &[u8]) -> Result<(u16, [u8; 32]), PrecompileFailure> { diff --git a/runtime/src/precompiles/solidity/subnets.abi b/runtime/src/precompiles/solidity/neuron.abi similarity index 100% rename from runtime/src/precompiles/solidity/subnets.abi rename to runtime/src/precompiles/solidity/neuron.abi diff --git a/runtime/src/precompiles/solidity/subnets.sol b/runtime/src/precompiles/solidity/neuron.sol similarity index 82% rename from runtime/src/precompiles/solidity/subnets.sol rename to runtime/src/precompiles/solidity/neuron.sol index 5cbf72779..a76842339 100644 --- a/runtime/src/precompiles/solidity/subnets.sol +++ b/runtime/src/precompiles/solidity/neuron.sol @@ -1,8 +1,8 @@ pragma solidity ^0.8.0; -address constant ISUBNETS_ADDRESS = 0x0000000000000000000000000000000000000804; +address constant INeuron_ADDRESS = 0x0000000000000000000000000000000000000804; -interface ISubnets { +interface INeuron { /** * @dev Registers a neuron by calling `do_burned_registration` internally with the origin set to the ss58 mirror of the H160 address. * This allows the H160 to further call neuron-related methods and receive emissions. From 23f9b87f37737fde2765899ebf7b4fdd28d496b2 Mon Sep 17 00:00:00 2001 From: open-junius Date: Mon, 20 Jan 2025 10:05:24 +0800 Subject: [PATCH 11/23] fix e2e test --- runtime/src/precompiles/subnet.rs | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/runtime/src/precompiles/subnet.rs b/runtime/src/precompiles/subnet.rs index 01eb331e6..db3adfc1b 100644 --- a/runtime/src/precompiles/subnet.rs +++ b/runtime/src/precompiles/subnet.rs @@ -31,7 +31,7 @@ impl SubnetPrecompile { Self::register_network(handle, &method_input) } id if id == get_method_id("registerNetwork(bytes32)") => { - Self::register_network(handle, &[0_u8; 0]) + Self::register_network(handle, &method_input) } _ => Err(PrecompileFailure::Error { exit_status: ExitError::InvalidRange, @@ -40,15 +40,17 @@ impl SubnetPrecompile { } fn register_network(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { - let call = if data.is_empty() { - let hotkey = Self::parse_pub_key(data)?.into(); + let call = if data.len() == 32 { + let mut hotkey = [0u8; 32]; + hotkey.copy_from_slice(get_slice(data, 0, 32)?); + RuntimeCall::SubtensorModule( pallet_subtensor::Call::::register_network_with_identity { - hotkey, + hotkey: hotkey.into(), identity: None, }, ) - } else { + } else if data.len() > 32 { let (pubkey, subnet_name, github_repo, subnet_contact) = Self::parse_register_network_parameters(data)?; @@ -65,23 +67,16 @@ impl SubnetPrecompile { identity: Some(identity), }, ) + } else { + return Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }); }; // Dispatch the register_network call dispatch(handle, call, STAKING_CONTRACT_ADDRESS) } - fn parse_pub_key(data: &[u8]) -> Result<[u8; 32], PrecompileFailure> { - if data.len() < 32 { - return Err(PrecompileFailure::Error { - exit_status: ExitError::InvalidRange, - }); - } - let mut pubkey = [0u8; 32]; - pubkey.copy_from_slice(get_slice(data, 0, 32)?); - Ok(pubkey) - } - fn parse_register_network_parameters( data: &[u8], ) -> Result<([u8; 32], vec::Vec, vec::Vec, vec::Vec), PrecompileFailure> { From 9de1927a0a866b3c768432972e315c6744c63a18 Mon Sep 17 00:00:00 2001 From: open-junius Date: Mon, 20 Jan 2025 10:22:41 +0800 Subject: [PATCH 12/23] commit Cargo.lock --- runtime/src/precompiles/subnet.rs | 67 +++++++++++++++++-------------- 1 file changed, 36 insertions(+), 31 deletions(-) diff --git a/runtime/src/precompiles/subnet.rs b/runtime/src/precompiles/subnet.rs index db3adfc1b..f883a6996 100644 --- a/runtime/src/precompiles/subnet.rs +++ b/runtime/src/precompiles/subnet.rs @@ -40,37 +40,42 @@ impl SubnetPrecompile { } fn register_network(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { - let call = if data.len() == 32 { - let mut hotkey = [0u8; 32]; - hotkey.copy_from_slice(get_slice(data, 0, 32)?); - - RuntimeCall::SubtensorModule( - pallet_subtensor::Call::::register_network_with_identity { - hotkey: hotkey.into(), - identity: None, - }, - ) - } else if data.len() > 32 { - let (pubkey, subnet_name, github_repo, subnet_contact) = - Self::parse_register_network_parameters(data)?; - - let identity: pallet_subtensor::SubnetIdentityOf = pallet_subtensor::SubnetIdentityOf { - subnet_name, - github_repo, - subnet_contact, - }; - - // Create the register_network callcle - RuntimeCall::SubtensorModule( - pallet_subtensor::Call::::register_network_with_identity { - hotkey: pubkey.into(), - identity: Some(identity), - }, - ) - } else { - return Err(PrecompileFailure::Error { - exit_status: ExitError::InvalidRange, - }); + let call = match data.len() { + 32 => { + let mut hotkey = [0u8; 32]; + hotkey.copy_from_slice(get_slice(data, 0, 32)?); + + RuntimeCall::SubtensorModule( + pallet_subtensor::Call::::register_network_with_identity { + hotkey: hotkey.into(), + identity: None, + }, + ) + } + 32.. => { + let (pubkey, subnet_name, github_repo, subnet_contact) = + Self::parse_register_network_parameters(data)?; + + let identity: pallet_subtensor::SubnetIdentityOf = + pallet_subtensor::SubnetIdentityOf { + subnet_name, + github_repo, + subnet_contact, + }; + + // Create the register_network callcle + RuntimeCall::SubtensorModule( + pallet_subtensor::Call::::register_network_with_identity { + hotkey: pubkey.into(), + identity: Some(identity), + }, + ) + } + _ => { + return Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }); + } }; // Dispatch the register_network call From 3d05f7bcf325970bba94ef55cad5ebfd59f7e6e4 Mon Sep 17 00:00:00 2001 From: open-junius Date: Mon, 20 Jan 2025 10:23:09 +0800 Subject: [PATCH 13/23] cargo clippy --- runtime/src/precompiles/subnet.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/src/precompiles/subnet.rs b/runtime/src/precompiles/subnet.rs index f883a6996..c1827f77a 100644 --- a/runtime/src/precompiles/subnet.rs +++ b/runtime/src/precompiles/subnet.rs @@ -52,7 +52,7 @@ impl SubnetPrecompile { }, ) } - 32.. => { + 33.. => { let (pubkey, subnet_name, github_repo, subnet_contact) = Self::parse_register_network_parameters(data)?; From 1a34d3318c7fb341bbb44477338c74c4883f0684 Mon Sep 17 00:00:00 2001 From: open-junius Date: Thu, 23 Jan 2025 09:49:36 +0800 Subject: [PATCH 14/23] refactor helper --- runtime/src/precompiles/mod.rs | 11 +++++++++++ runtime/src/precompiles/subnet.rs | 9 +++------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/runtime/src/precompiles/mod.rs b/runtime/src/precompiles/mod.rs index 3d2c19ae9..3c62d4b8c 100644 --- a/runtime/src/precompiles/mod.rs +++ b/runtime/src/precompiles/mod.rs @@ -232,3 +232,14 @@ fn dispatch( }), } } + +pub fn get_pubkey(data: &[u8]) -> Result<(AccountId32, Vec), PrecompileFailure> { + let mut pubkey = [0u8; 32]; + pubkey.copy_from_slice(data.get_slice(0, 32)?); + + Ok(( + pubkey.into(), + data.get(4..) + .map_or_else(vec::Vec::new, |slice| slice.to_vec()), + )) +} diff --git a/runtime/src/precompiles/subnet.rs b/runtime/src/precompiles/subnet.rs index c1827f77a..9c9fd859f 100644 --- a/runtime/src/precompiles/subnet.rs +++ b/runtime/src/precompiles/subnet.rs @@ -1,4 +1,4 @@ -use crate::precompiles::{dispatch, get_method_id, get_slice}; +use crate::precompiles::{dispatch, get_method_id, get_pubkey, get_slice}; use crate::{Runtime, RuntimeCall}; use pallet_evm::{ExitError, PrecompileFailure, PrecompileHandle, PrecompileResult}; use sp_std::vec; @@ -42,8 +42,7 @@ impl SubnetPrecompile { fn register_network(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { let call = match data.len() { 32 => { - let mut hotkey = [0u8; 32]; - hotkey.copy_from_slice(get_slice(data, 0, 32)?); + let (hotkey, _) = get_pubkey(data); RuntimeCall::SubtensorModule( pallet_subtensor::Call::::register_network_with_identity { @@ -85,11 +84,9 @@ impl SubnetPrecompile { fn parse_register_network_parameters( data: &[u8], ) -> Result<([u8; 32], vec::Vec, vec::Vec, vec::Vec), PrecompileFailure> { - let mut pubkey = [0u8; 32]; - pubkey.copy_from_slice(get_slice(data, 0, 32)?); + let (pubkey, _) = get_pubkey(data)?; let mut buf = [0_u8; 4]; - // get all start point for three data items: name, repo and contact buf.copy_from_slice(get_slice(data, 60, 64)?); let subnet_name_start: usize = u32::from_be_bytes(buf) as usize; From 541518e091b2fe4150293a740f54f437fc47006a Mon Sep 17 00:00:00 2001 From: open-junius Date: Thu, 23 Jan 2025 23:28:03 +0800 Subject: [PATCH 15/23] fix compilation error --- runtime/src/precompiles/mod.rs | 4 ++-- runtime/src/precompiles/subnet.rs | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/runtime/src/precompiles/mod.rs b/runtime/src/precompiles/mod.rs index 3c62d4b8c..de4f4fc1f 100644 --- a/runtime/src/precompiles/mod.rs +++ b/runtime/src/precompiles/mod.rs @@ -233,9 +233,9 @@ fn dispatch( } } -pub fn get_pubkey(data: &[u8]) -> Result<(AccountId32, Vec), PrecompileFailure> { +pub fn get_pubkey(data: &[u8]) -> Result<(AccountId32, vec::Vec), PrecompileFailure> { let mut pubkey = [0u8; 32]; - pubkey.copy_from_slice(data.get_slice(0, 32)?); + pubkey.copy_from_slice(get_slice(data, 0, 32)?); Ok(( pubkey.into(), diff --git a/runtime/src/precompiles/subnet.rs b/runtime/src/precompiles/subnet.rs index 9c9fd859f..7537ea8ae 100644 --- a/runtime/src/precompiles/subnet.rs +++ b/runtime/src/precompiles/subnet.rs @@ -1,6 +1,7 @@ use crate::precompiles::{dispatch, get_method_id, get_pubkey, get_slice}; use crate::{Runtime, RuntimeCall}; use pallet_evm::{ExitError, PrecompileFailure, PrecompileHandle, PrecompileResult}; +use sp_runtime::AccountId32; use sp_std::vec; pub const SUBNET_PRECOMPILE_INDEX: u64 = 2051; @@ -42,7 +43,7 @@ impl SubnetPrecompile { fn register_network(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { let call = match data.len() { 32 => { - let (hotkey, _) = get_pubkey(data); + let (hotkey, _) = get_pubkey(data)?; RuntimeCall::SubtensorModule( pallet_subtensor::Call::::register_network_with_identity { @@ -83,7 +84,7 @@ impl SubnetPrecompile { fn parse_register_network_parameters( data: &[u8], - ) -> Result<([u8; 32], vec::Vec, vec::Vec, vec::Vec), PrecompileFailure> { + ) -> Result<(AccountId32, vec::Vec, vec::Vec, vec::Vec), PrecompileFailure> { let (pubkey, _) = get_pubkey(data)?; let mut buf = [0_u8; 4]; From d148c2b3a357ad5229edbed46da06517624fe68c Mon Sep 17 00:00:00 2001 From: open-junius Date: Thu, 23 Jan 2025 23:44:21 +0800 Subject: [PATCH 16/23] refactor get pubkey method --- runtime/src/precompiles/neuron.rs | 12 +++---- runtime/src/precompiles/solidity/subnet.sol | 2 +- runtime/src/precompiles/staking.rs | 38 ++++----------------- runtime/src/precompiles/subnet.rs | 6 ++-- 4 files changed, 17 insertions(+), 41 deletions(-) diff --git a/runtime/src/precompiles/neuron.rs b/runtime/src/precompiles/neuron.rs index 341683cb3..efa0b5fec 100644 --- a/runtime/src/precompiles/neuron.rs +++ b/runtime/src/precompiles/neuron.rs @@ -1,6 +1,7 @@ use pallet_evm::{ExitError, PrecompileFailure, PrecompileHandle, PrecompileResult}; -use crate::precompiles::{dispatch, get_method_id, get_slice}; +use crate::precompiles::{dispatch, get_method_id, get_pubkey, get_slice}; +use sp_runtime::AccountId32; use sp_std::vec; use crate::{Runtime, RuntimeCall}; @@ -35,12 +36,12 @@ impl NeuronPrecompile { let call = RuntimeCall::SubtensorModule(pallet_subtensor::Call::::burned_register { netuid, - hotkey: hotkey.into(), + hotkey, }); dispatch(handle, call, NEURON_CONTRACT_ADDRESS) } - fn parse_netuid_hotkey_parameter(data: &[u8]) -> Result<(u16, [u8; 32]), PrecompileFailure> { + fn parse_netuid_hotkey_parameter(data: &[u8]) -> Result<(u16, AccountId32), PrecompileFailure> { if data.len() < 64 { return Err(PrecompileFailure::Error { exit_status: ExitError::InvalidRange, @@ -50,9 +51,8 @@ impl NeuronPrecompile { netuid_vec.copy_from_slice(get_slice(data, 30, 32)?); let netuid = u16::from_be_bytes(netuid_vec); - let mut parameter = [0u8; 32]; - parameter.copy_from_slice(get_slice(data, 32, 64)?); + let (hotkey, _) = get_pubkey(get_slice(data, 32, 64)?)?; - Ok((netuid, parameter)) + Ok((netuid, hotkey)) } } diff --git a/runtime/src/precompiles/solidity/subnet.sol b/runtime/src/precompiles/solidity/subnet.sol index c3d0a4a13..c6639891a 100644 --- a/runtime/src/precompiles/solidity/subnet.sol +++ b/runtime/src/precompiles/solidity/subnet.sol @@ -1,6 +1,6 @@ pragma solidity ^0.8.0; -address constant ISTAKING_ADDRESS = 0x0000000000000000000000000000000000000803; +address constant ISUBNET_ADDRESS = 0x0000000000000000000000000000000000000803; interface ISubnet { /// Registers a new network without specifying details. diff --git a/runtime/src/precompiles/staking.rs b/runtime/src/precompiles/staking.rs index a29031352..f549ee1c3 100644 --- a/runtime/src/precompiles/staking.rs +++ b/runtime/src/precompiles/staking.rs @@ -31,10 +31,9 @@ use pallet_evm::{ }; use sp_core::U256; use sp_runtime::traits::{StaticLookup, UniqueSaturatedInto}; -use sp_runtime::AccountId32; use crate::{ - precompiles::{dispatch, get_method_id, get_slice}, + precompiles::{dispatch, get_method_id, get_pubkey, get_slice}, ProxyType, }; use sp_std::vec; @@ -71,7 +70,7 @@ impl StakingPrecompile { } fn add_stake(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { - let hotkey = Self::parse_pub_key(data)?.into(); + let (hotkey, _) = get_pubkey(data)?; let amount: U256 = handle.context().apparent_value; let netuid = Self::parse_netuid(data, 0x3E)?; @@ -90,7 +89,7 @@ impl StakingPrecompile { } fn remove_stake(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { - let hotkey = Self::parse_pub_key(data)?.into(); + let (hotkey, _) = get_pubkey(data)?; let netuid = Self::parse_netuid(data, 0x5E)?; // We have to treat this as uint256 (because of Solidity ABI encoding rules, it pads uint64), @@ -113,7 +112,7 @@ impl StakingPrecompile { } fn add_proxy(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { - let delegate = AccountId32::from(Self::parse_pub_key(data)?); + let (delegate, _) = get_pubkey(data)?; let delegate = ::Lookup::unlookup(delegate); let call = RuntimeCall::Proxy(pallet_proxy::Call::::add_proxy { delegate, @@ -125,7 +124,7 @@ impl StakingPrecompile { } fn remove_proxy(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { - let delegate = AccountId32::from(Self::parse_pub_key(data)?); + let (delegate, _) = get_pubkey(data)?; let delegate = ::Lookup::unlookup(delegate); let call = RuntimeCall::Proxy(pallet_proxy::Call::::remove_proxy { delegate, @@ -137,7 +136,8 @@ impl StakingPrecompile { } fn get_stake(data: &[u8]) -> PrecompileResult { - let (hotkey, coldkey) = Self::parse_hotkey_coldkey(data)?; + let (hotkey, left_data) = get_pubkey(data)?; + let (coldkey, _) = get_pubkey(&left_data)?; let netuid = Self::parse_netuid(data, 0x5E)?; let stake = pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( @@ -162,30 +162,6 @@ impl StakingPrecompile { }) } - fn parse_hotkey_coldkey(data: &[u8]) -> Result<([u8; 32], [u8; 32]), PrecompileFailure> { - if data.len() < 64 { - return Err(PrecompileFailure::Error { - exit_status: ExitError::InvalidRange, - }); - } - let mut hotkey = [0u8; 32]; - hotkey.copy_from_slice(get_slice(data, 0, 32)?); - let mut coldkey = [0u8; 32]; - coldkey.copy_from_slice(get_slice(data, 32, 64)?); - Ok((hotkey, coldkey)) - } - - fn parse_pub_key(data: &[u8]) -> Result<[u8; 32], PrecompileFailure> { - if data.len() < 32 { - return Err(PrecompileFailure::Error { - exit_status: ExitError::InvalidRange, - }); - } - let mut pubkey = [0u8; 32]; - pubkey.copy_from_slice(get_slice(data, 0, 32)?); - Ok(pubkey) - } - fn parse_netuid(data: &[u8], offset: usize) -> Result { if data.len() < offset + 2 { return Err(PrecompileFailure::Error { diff --git a/runtime/src/precompiles/subnet.rs b/runtime/src/precompiles/subnet.rs index 7537ea8ae..3dba5a262 100644 --- a/runtime/src/precompiles/subnet.rs +++ b/runtime/src/precompiles/subnet.rs @@ -47,13 +47,13 @@ impl SubnetPrecompile { RuntimeCall::SubtensorModule( pallet_subtensor::Call::::register_network_with_identity { - hotkey: hotkey.into(), + hotkey, identity: None, }, ) } 33.. => { - let (pubkey, subnet_name, github_repo, subnet_contact) = + let (hotkey, subnet_name, github_repo, subnet_contact) = Self::parse_register_network_parameters(data)?; let identity: pallet_subtensor::SubnetIdentityOf = @@ -66,7 +66,7 @@ impl SubnetPrecompile { // Create the register_network callcle RuntimeCall::SubtensorModule( pallet_subtensor::Call::::register_network_with_identity { - hotkey: pubkey.into(), + hotkey, identity: Some(identity), }, ) From 96a1d08dd15581752480661122e25b1207ba8979 Mon Sep 17 00:00:00 2001 From: open-junius Date: Thu, 23 Jan 2025 23:55:31 +0800 Subject: [PATCH 17/23] cargo clippy --- runtime/src/precompiles/staking.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/src/precompiles/staking.rs b/runtime/src/precompiles/staking.rs index f549ee1c3..f6e358758 100644 --- a/runtime/src/precompiles/staking.rs +++ b/runtime/src/precompiles/staking.rs @@ -141,8 +141,8 @@ impl StakingPrecompile { let netuid = Self::parse_netuid(data, 0x5E)?; let stake = pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey.into(), - &coldkey.into(), + &hotkey, + &coldkey, netuid, ); From 241d6204893936fad235cb2d247f5972bad2f1da Mon Sep 17 00:00:00 2001 From: open-junius Date: Thu, 23 Jan 2025 23:55:57 +0800 Subject: [PATCH 18/23] cargo fmt --- runtime/src/precompiles/staking.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/runtime/src/precompiles/staking.rs b/runtime/src/precompiles/staking.rs index f6e358758..f9a0968f4 100644 --- a/runtime/src/precompiles/staking.rs +++ b/runtime/src/precompiles/staking.rs @@ -141,9 +141,7 @@ impl StakingPrecompile { let netuid = Self::parse_netuid(data, 0x5E)?; let stake = pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( - &hotkey, - &coldkey, - netuid, + &hotkey, &coldkey, netuid, ); // Convert to EVM decimals From a0819b4d5bb2806a771ba98c0f1a2274b0183cbb Mon Sep 17 00:00:00 2001 From: open-junius Date: Fri, 24 Jan 2025 00:43:02 +0800 Subject: [PATCH 19/23] remove byte_to_account_id --- runtime/src/precompiles/balance_transfer.rs | 6 +++--- runtime/src/precompiles/mod.rs | 12 ------------ 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/runtime/src/precompiles/balance_transfer.rs b/runtime/src/precompiles/balance_transfer.rs index 99d911b02..895e91a62 100644 --- a/runtime/src/precompiles/balance_transfer.rs +++ b/runtime/src/precompiles/balance_transfer.rs @@ -9,7 +9,7 @@ use sp_std::vec; use crate::{Runtime, RuntimeCall}; -use crate::precompiles::{bytes_to_account_id, get_method_id, get_slice}; +use crate::precompiles::{get_method_id, get_pubkey, get_slice}; pub const BALANCE_TRANSFER_INDEX: u64 = 2048; @@ -47,8 +47,8 @@ impl BalanceTransferPrecompile { 0x62, 0x93, 0x70, 0x5d, ]; let address_bytes_dst: &[u8] = get_slice(txdata, 4, 36)?; - let account_id_src = bytes_to_account_id(&ADDRESS_BYTES_SRC)?; - let account_id_dst = bytes_to_account_id(address_bytes_dst)?; + let (account_id_src, _) = get_pubkey(&ADDRESS_BYTES_SRC)?; + let (account_id_dst, _) = get_pubkey(address_bytes_dst)?; let call = RuntimeCall::Balances(pallet_balances::Call::::transfer_allow_death { diff --git a/runtime/src/precompiles/mod.rs b/runtime/src/precompiles/mod.rs index de4f4fc1f..567e7836a 100644 --- a/runtime/src/precompiles/mod.rs +++ b/runtime/src/precompiles/mod.rs @@ -124,18 +124,6 @@ pub fn get_method_id(method_signature: &str) -> [u8; 4] { [hash[0], hash[1], hash[2], hash[3]] } -/// Convert bytes to AccountId32 with PrecompileFailure as Error -/// which consumes all gas -/// -pub fn bytes_to_account_id(account_id_bytes: &[u8]) -> Result { - AccountId32::try_from(account_id_bytes).map_err(|_| { - log::info!("Error parsing account id bytes {:?}", account_id_bytes); - PrecompileFailure::Error { - exit_status: ExitError::InvalidRange, - } - }) -} - /// Takes a slice from bytes with PrecompileFailure as Error /// pub fn get_slice(data: &[u8], from: usize, to: usize) -> Result<&[u8], PrecompileFailure> { From f644f267244b4da4b5d8b4ba007f8e99bf881bde Mon Sep 17 00:00:00 2001 From: open-junius Date: Thu, 30 Jan 2025 10:54:47 +0800 Subject: [PATCH 20/23] refactor code --- runtime/src/precompiles/balance_transfer.rs | 9 +- runtime/src/precompiles/mod.rs | 107 ++++---------------- runtime/src/precompiles/neuron.rs | 20 ++-- runtime/src/precompiles/staking.rs | 27 +++-- runtime/src/precompiles/subnet.rs | 15 ++- 5 files changed, 67 insertions(+), 111 deletions(-) diff --git a/runtime/src/precompiles/balance_transfer.rs b/runtime/src/precompiles/balance_transfer.rs index 25dfcfc9f..6154cf8a5 100644 --- a/runtime/src/precompiles/balance_transfer.rs +++ b/runtime/src/precompiles/balance_transfer.rs @@ -1,11 +1,12 @@ -use frame_system::RawOrigin; use pallet_evm::{ BalanceConverter, ExitError, ExitSucceed, PrecompileHandle, PrecompileOutput, PrecompileResult, }; use sp_runtime::traits::UniqueSaturatedInto; use sp_std::vec; -use crate::precompiles::{get_method_id, get_pubkey, get_slice, try_dispatch_runtime_call}; +use crate::precompiles::{ + contract_to_origin, get_method_id, get_pubkey, get_slice, try_dispatch_runtime_call, +}; use crate::Runtime; pub const BALANCE_TRANSFER_INDEX: u64 = 2048; @@ -47,15 +48,13 @@ impl BalanceTransferPrecompile { } let address_bytes_dst = get_slice(txdata, 4, 36)?; - let (account_id_src, _) = get_pubkey(&CONTRACT_ADDRESS_SS58)?; let (account_id_dst, _) = get_pubkey(address_bytes_dst)?; let call = pallet_balances::Call::::transfer_allow_death { dest: account_id_dst.into(), value: amount_sub.unique_saturated_into(), }; - let origin = RawOrigin::Signed(account_id_src); - try_dispatch_runtime_call(handle, call, origin) + try_dispatch_runtime_call(handle, call, contract_to_origin(&CONTRACT_ADDRESS_SS58)?) } } diff --git a/runtime/src/precompiles/mod.rs b/runtime/src/precompiles/mod.rs index e4009d39d..d820f50ec 100644 --- a/runtime/src/precompiles/mod.rs +++ b/runtime/src/precompiles/mod.rs @@ -6,9 +6,8 @@ use core::marker::PhantomData; use frame_support::dispatch::{GetDispatchInfo, Pays}; use pallet_evm::{ - AddressMapping, BalanceConverter, ExitError, ExitSucceed, GasWeightMapping, - HashedAddressMapping, IsPrecompileResult, Precompile, PrecompileFailure, PrecompileHandle, - PrecompileOutput, PrecompileResult, PrecompileSet, + ExitError, ExitSucceed, GasWeightMapping, IsPrecompileResult, Precompile, PrecompileFailure, + PrecompileHandle, PrecompileOutput, PrecompileResult, PrecompileSet, }; use pallet_evm_precompile_modexp::Modexp; use pallet_evm_precompile_sha3fips::Sha3FIPS256; @@ -20,10 +19,6 @@ use crate::{Runtime, RuntimeCall}; use frame_system::RawOrigin; -use sp_core::crypto::Ss58Codec; -use sp_core::U256; -use sp_runtime::traits::{BlakeTwo256, UniqueSaturatedInto}; - use sp_std::vec; // Include custom precompiles @@ -147,84 +142,6 @@ pub fn get_slice(data: &[u8], from: usize, to: usize) -> Result<&[u8], Precompil } } -/// The function return the token to smart contract -fn transfer_back_to_caller( - smart_contract_address: &str, - account_id: &AccountId32, - amount: U256, -) -> Result<(), PrecompileFailure> { - // this is staking smart contract's(0x0000000000000000000000000000000000000801) sr25519 address - let smart_contract_account_id = match AccountId32::from_ss58check(smart_contract_address) { - // match AccountId32::from_ss58check("5CwnBK9Ack1mhznmCnwiibCNQc174pYQVktYW3ayRpLm4K2X") { - Ok(addr) => addr, - Err(_) => { - return Err(PrecompileFailure::Error { - exit_status: ExitError::Other("Invalid SS58 address".into()), - }); - } - }; - let amount_sub = - ::BalanceConverter::into_substrate_balance(amount) - .ok_or(ExitError::OutOfFund)?; - - // Create a transfer call from the smart contract to the caller - let transfer_call = - RuntimeCall::Balances(pallet_balances::Call::::transfer_allow_death { - dest: account_id.clone().into(), - value: amount_sub.unique_saturated_into(), - }); - - // Execute the transfer - let transfer_result = - transfer_call.dispatch(RawOrigin::Signed(smart_contract_account_id).into()); - - if let Err(dispatch_error) = transfer_result { - log::error!( - "Transfer back to caller failed. Error: {:?}", - dispatch_error - ); - return Err(PrecompileFailure::Error { - exit_status: ExitError::Other("Transfer back to caller failed".into()), - }); - } - - Ok(()) -} - -fn dispatch( - handle: &mut impl PrecompileHandle, - call: RuntimeCall, - smart_contract_address: &str, -) -> PrecompileResult { - let account_id = - as AddressMapping>::into_account_id( - handle.context().caller, - ); - - // Transfer the amount back to the caller before executing the staking operation - // let caller = handle.context().caller; - let amount = handle.context().apparent_value; - - if !amount.is_zero() { - transfer_back_to_caller(smart_contract_address, &account_id, amount)?; - } - - let result = call.dispatch(RawOrigin::Signed(account_id.clone()).into()); - match &result { - Ok(post_info) => log::info!("Dispatch succeeded. Post info: {:?}", post_info), - Err(dispatch_error) => log::error!("Dispatch failed. Error: {:?}", dispatch_error), - } - match result { - Ok(_) => Ok(PrecompileOutput { - exit_status: ExitSucceed::Returned, - output: vec![], - }), - Err(_) => Err(PrecompileFailure::Error { - exit_status: ExitError::Other("Subtensor call failed".into()), - }), - } -} - pub fn get_pubkey(data: &[u8]) -> Result<(AccountId32, vec::Vec), PrecompileFailure> { let mut pubkey = [0u8; 32]; pubkey.copy_from_slice(get_slice(data, 0, 32)?); @@ -235,6 +152,26 @@ pub fn get_pubkey(data: &[u8]) -> Result<(AccountId32, vec::Vec), Precompile .map_or_else(vec::Vec::new, |slice| slice.to_vec()), )) } + +fn parse_netuid(data: &[u8], offset: usize) -> Result { + if data.len() < offset + 2 { + return Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }); + } + + let mut netuid_bytes = [0u8; 2]; + netuid_bytes.copy_from_slice(get_slice(data, offset, offset + 2)?); + let netuid: u16 = netuid_bytes[1] as u16 | ((netuid_bytes[0] as u16) << 8u16); + + Ok(netuid) +} + +fn contract_to_origin(contract: &[u8; 32]) -> Result, PrecompileFailure> { + let (account_id, _) = get_pubkey(contract)?; + Ok(RawOrigin::Signed(account_id)) +} + /// Dispatches a runtime call, but also checks and records the gas costs. fn try_dispatch_runtime_call( handle: &mut impl PrecompileHandle, diff --git a/runtime/src/precompiles/neuron.rs b/runtime/src/precompiles/neuron.rs index efa0b5fec..97167c900 100644 --- a/runtime/src/precompiles/neuron.rs +++ b/runtime/src/precompiles/neuron.rs @@ -1,15 +1,21 @@ use pallet_evm::{ExitError, PrecompileFailure, PrecompileHandle, PrecompileResult}; -use crate::precompiles::{dispatch, get_method_id, get_pubkey, get_slice}; +use crate::precompiles::{ + contract_to_origin, get_method_id, get_pubkey, get_slice, parse_netuid, + try_dispatch_runtime_call, +}; use sp_runtime::AccountId32; use sp_std::vec; use crate::{Runtime, RuntimeCall}; pub const NEURON_PRECOMPILE_INDEX: u64 = 2052; -// this is neuron smart contract's(0x0000000000000000000000000000000000000804) sr25519 address -pub const NEURON_CONTRACT_ADDRESS: &str = "5GKZiUUgTnWSz3BgiVBMehEKkLszsG4ZXnvgWpWFUFKqrqyn"; - +// ss58 public key i.e., the contract sends funds it received to the destination address from the +// method parameter. +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, +]; pub struct NeuronPrecompile; impl NeuronPrecompile { @@ -38,7 +44,7 @@ impl NeuronPrecompile { netuid, hotkey, }); - dispatch(handle, call, NEURON_CONTRACT_ADDRESS) + try_dispatch_runtime_call(handle, call, contract_to_origin(&CONTRACT_ADDRESS_SS58)?) } fn parse_netuid_hotkey_parameter(data: &[u8]) -> Result<(u16, AccountId32), PrecompileFailure> { @@ -47,9 +53,7 @@ impl NeuronPrecompile { 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 netuid = parse_netuid(data, 30)?; let (hotkey, _) = get_pubkey(get_slice(data, 32, 64)?)?; diff --git a/runtime/src/precompiles/staking.rs b/runtime/src/precompiles/staking.rs index d588799fc..3c3ee7ad9 100644 --- a/runtime/src/precompiles/staking.rs +++ b/runtime/src/precompiles/staking.rs @@ -25,7 +25,10 @@ // - Precompile checks the result of do_remove_stake and, in case of a failure, reverts the transaction. // -use crate::precompiles::{dispatch, get_method_id, get_pubkey, get_slice}; +use crate::precompiles::{ + contract_to_origin, get_method_id, get_pubkey, get_slice, parse_netuid, + try_dispatch_runtime_call, +}; use crate::{ProxyType, Runtime, RuntimeCall}; use pallet_evm::{ BalanceConverter, ExitError, ExitSucceed, PrecompileFailure, PrecompileHandle, @@ -36,8 +39,14 @@ use sp_runtime::traits::{StaticLookup, UniqueSaturatedInto}; use sp_std::vec; pub const STAKING_PRECOMPILE_INDEX: u64 = 2049; -// this is staking smart contract's(0x0000000000000000000000000000000000000801) sr25519 address -pub const STAKING_CONTRACT_ADDRESS: &str = "5CwnBK9Ack1mhznmCnwiibCNQc174pYQVktYW3ayRpLm4K2X"; + +// ss58 public key i.e., the contract sends funds it received to the destination address from the +// method parameter. +const CONTRACT_ADDRESS_SS58: [u8; 32] = [ + 0x26, 0xf4, 0x10, 0x1e, 0x52, 0xb7, 0x57, 0x34, 0x33, 0x24, 0x5b, 0xc3, 0x0a, 0xe1, 0x8b, 0x63, + 0x99, 0x53, 0xd8, 0x41, 0x79, 0x33, 0x03, 0x61, 0x4d, 0xfa, 0xcf, 0xf0, 0x37, 0xf7, 0x12, 0x94, +]; + pub struct StakingPrecompile; impl StakingPrecompile { @@ -74,14 +83,16 @@ impl StakingPrecompile { ::BalanceConverter::into_substrate_balance(amount) .ok_or(ExitError::OutOfFund)?; + // let (account_id_src, _) = get_pubkey(&CONTRACT_ADDRESS_SS58)?; // Create the add_stake call let call = RuntimeCall::SubtensorModule(pallet_subtensor::Call::::add_stake { hotkey, netuid, amount_staked: amount_sub.unique_saturated_into(), }); + // let origin = RawOrigin::Signed(account_id_src); // Dispatch the add_stake call - dispatch(handle, call, STAKING_CONTRACT_ADDRESS) + try_dispatch_runtime_call(handle, call, contract_to_origin(&CONTRACT_ADDRESS_SS58)?) } fn remove_stake(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { @@ -104,7 +115,7 @@ impl StakingPrecompile { netuid, amount_unstaked: amount_sub.unique_saturated_into(), }); - dispatch(handle, call, STAKING_CONTRACT_ADDRESS) + try_dispatch_runtime_call(handle, call, contract_to_origin(&CONTRACT_ADDRESS_SS58)?) } fn add_proxy(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { @@ -116,7 +127,7 @@ impl StakingPrecompile { delay: 0, }); - dispatch(handle, call, STAKING_CONTRACT_ADDRESS) + try_dispatch_runtime_call(handle, call, contract_to_origin(&CONTRACT_ADDRESS_SS58)?) } fn remove_proxy(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { @@ -128,13 +139,13 @@ impl StakingPrecompile { delay: 0, }); - dispatch(handle, call, STAKING_CONTRACT_ADDRESS) + try_dispatch_runtime_call(handle, call, contract_to_origin(&CONTRACT_ADDRESS_SS58)?) } fn get_stake(data: &[u8]) -> PrecompileResult { let (hotkey, left_data) = get_pubkey(data)?; let (coldkey, _) = get_pubkey(&left_data)?; - let netuid = Self::parse_netuid(data, 0x5E)?; + let netuid = parse_netuid(data, 0x5E)?; let stake = pallet_subtensor::Pallet::::get_stake_for_hotkey_and_coldkey_on_subnet( &hotkey, &coldkey, netuid, diff --git a/runtime/src/precompiles/subnet.rs b/runtime/src/precompiles/subnet.rs index 3dba5a262..edef73ff0 100644 --- a/runtime/src/precompiles/subnet.rs +++ b/runtime/src/precompiles/subnet.rs @@ -1,4 +1,6 @@ -use crate::precompiles::{dispatch, get_method_id, get_pubkey, get_slice}; +use crate::precompiles::{ + contract_to_origin, get_method_id, get_pubkey, get_slice, try_dispatch_runtime_call, +}; use crate::{Runtime, RuntimeCall}; use pallet_evm::{ExitError, PrecompileFailure, PrecompileHandle, PrecompileResult}; use sp_runtime::AccountId32; @@ -8,9 +10,12 @@ pub const SUBNET_PRECOMPILE_INDEX: u64 = 2051; // three bytes with max lenght 1K pub const MAX_PARAMETER_SIZE: usize = 3 * 1024; -// this is staking smart contract's(0x0000000000000000000000000000000000000803) sr25519 address -pub const STAKING_CONTRACT_ADDRESS: &str = "5DPSUCb5mZFfizvBDSnRoAqmxV5Bmov2CS3xV773qU6VP1w2"; - +// ss58 public key i.e., the contract sends funds it received to the destination address from the +// method parameter. +const CONTRACT_ADDRESS_SS58: [u8; 32] = [ + 0x3a, 0x86, 0x18, 0xfb, 0xbb, 0x1b, 0xbc, 0x47, 0x86, 0x64, 0xff, 0x53, 0x46, 0x18, 0x0c, 0x35, + 0xd0, 0x9f, 0xac, 0x26, 0xf2, 0x02, 0x70, 0x85, 0xb3, 0x1c, 0x56, 0xc1, 0x06, 0x3c, 0x1c, 0xd3, +]; pub struct SubnetPrecompile; impl SubnetPrecompile { @@ -79,7 +84,7 @@ impl SubnetPrecompile { }; // Dispatch the register_network call - dispatch(handle, call, STAKING_CONTRACT_ADDRESS) + try_dispatch_runtime_call(handle, call, contract_to_origin(&CONTRACT_ADDRESS_SS58)?) } fn parse_register_network_parameters( From f25cabc641df8f379ac3a2d6d0030f42477f9df0 Mon Sep 17 00:00:00 2001 From: open-junius Date: Thu, 30 Jan 2025 18:22:38 +0800 Subject: [PATCH 21/23] add len check --- runtime/src/precompiles/subnet.rs | 59 +++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/runtime/src/precompiles/subnet.rs b/runtime/src/precompiles/subnet.rs index edef73ff0..36f861d48 100644 --- a/runtime/src/precompiles/subnet.rs +++ b/runtime/src/precompiles/subnet.rs @@ -7,8 +7,10 @@ use sp_runtime::AccountId32; use sp_std::vec; pub const SUBNET_PRECOMPILE_INDEX: u64 = 2051; +// bytes with max lenght 1K +pub const MAX_SINGLE_PARAMETER_SIZE: usize = 1024; // three bytes with max lenght 1K -pub const MAX_PARAMETER_SIZE: usize = 3 * 1024; +pub const MAX_PARAMETER_SIZE: usize = 3 * MAX_SINGLE_PARAMETER_SIZE; // ss58 public key i.e., the contract sends funds it received to the destination address from the // method parameter. @@ -90,18 +92,46 @@ impl SubnetPrecompile { fn parse_register_network_parameters( data: &[u8], ) -> Result<(AccountId32, vec::Vec, vec::Vec, vec::Vec), PrecompileFailure> { - let (pubkey, _) = get_pubkey(data)?; + let (pubkey, dynamic_params) = get_pubkey(data)?; + let dynamic_data_len = dynamic_params.len(); let mut buf = [0_u8; 4]; // get all start point for three data items: name, repo and contact buf.copy_from_slice(get_slice(data, 60, 64)?); let subnet_name_start: usize = u32::from_be_bytes(buf) as usize; + if subnet_name_start > dynamic_data_len { + log::error!( + "the start position of subnet name as {} is too big ", + subnet_name_start + ); + return Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }); + } buf.copy_from_slice(get_slice(data, 92, 96)?); let github_repo_start: usize = u32::from_be_bytes(buf) as usize; + if github_repo_start > dynamic_data_len { + log::error!( + "the start position of github repo as {} is too big ", + github_repo_start + ); + return Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }); + } buf.copy_from_slice(get_slice(data, 124, 128)?); let subnet_contact_start: usize = u32::from_be_bytes(buf) as usize; + if subnet_contact_start > dynamic_data_len { + log::error!( + "the start position of subnet contact as {} is too big ", + subnet_contact_start + ); + return Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }); + } // get name buf.copy_from_slice(get_slice( @@ -111,6 +141,13 @@ impl SubnetPrecompile { )?); let subnet_name_len: usize = u32::from_be_bytes(buf) as usize; + if subnet_name_len > MAX_SINGLE_PARAMETER_SIZE { + log::error!("the length of subnet nae as {} is too big", subnet_name_len); + return Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }); + } + let mut name_vec = vec![0; subnet_name_len]; name_vec.copy_from_slice(get_slice( data, @@ -125,6 +162,15 @@ impl SubnetPrecompile { github_repo_start + 32, )?); let github_repo_len: usize = u32::from_be_bytes(buf) as usize; + if github_repo_len > MAX_SINGLE_PARAMETER_SIZE { + log::error!( + "the length of github repo as {} is too big", + github_repo_len + ); + return Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }); + } let mut repo_vec = vec![0; github_repo_len]; repo_vec.copy_from_slice(get_slice( @@ -140,6 +186,15 @@ impl SubnetPrecompile { subnet_contact_start + 32, )?); let subnet_contact_len: usize = u32::from_be_bytes(buf) as usize; + if subnet_contact_len > MAX_SINGLE_PARAMETER_SIZE { + log::error!( + "the length of subnet contact as {} is too big", + subnet_contact_len + ); + return Err(PrecompileFailure::Error { + exit_status: ExitError::InvalidRange, + }); + } let mut contact_vec = vec![0; subnet_contact_len]; contact_vec.copy_from_slice(get_slice( From 998867ead0e6618fbccbd8bb3d69b0671c2fc02e Mon Sep 17 00:00:00 2001 From: open-junius Date: Fri, 31 Jan 2025 00:11:02 +0800 Subject: [PATCH 22/23] fix wrong origin --- runtime/src/precompiles/staking.rs | 81 ++++++++++++++++++++++-------- runtime/src/precompiles/subnet.rs | 20 +++++--- 2 files changed, 73 insertions(+), 28 deletions(-) diff --git a/runtime/src/precompiles/staking.rs b/runtime/src/precompiles/staking.rs index 3c3ee7ad9..eefde3a37 100644 --- a/runtime/src/precompiles/staking.rs +++ b/runtime/src/precompiles/staking.rs @@ -26,16 +26,17 @@ // 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::{ProxyType, Runtime, RuntimeCall}; +use frame_system::RawOrigin; use pallet_evm::{ - BalanceConverter, ExitError, ExitSucceed, PrecompileFailure, PrecompileHandle, - PrecompileOutput, PrecompileResult, + AddressMapping, BalanceConverter, ExitError, ExitSucceed, HashedAddressMapping, + PrecompileFailure, PrecompileHandle, PrecompileOutput, PrecompileResult, }; use sp_core::U256; -use sp_runtime::traits::{StaticLookup, UniqueSaturatedInto}; +use sp_runtime::traits::{BlakeTwo256, Dispatchable, StaticLookup, UniqueSaturatedInto}; +use sp_runtime::AccountId32; use sp_std::vec; pub const STAKING_PRECOMPILE_INDEX: u64 = 2049; @@ -75,27 +76,37 @@ impl StakingPrecompile { } fn add_stake(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { + let account_id = + as AddressMapping>::into_account_id( + handle.context().caller, + ); + let (hotkey, _) = get_pubkey(data)?; let amount: U256 = handle.context().apparent_value; let netuid = Self::parse_netuid(data, 0x3E)?; + if !amount.is_zero() { + Self::transfer_back_to_caller(&account_id, amount)?; + } + let amount_sub = ::BalanceConverter::into_substrate_balance(amount) .ok_or(ExitError::OutOfFund)?; - // let (account_id_src, _) = get_pubkey(&CONTRACT_ADDRESS_SS58)?; - // Create the add_stake call let call = RuntimeCall::SubtensorModule(pallet_subtensor::Call::::add_stake { hotkey, netuid, amount_staked: amount_sub.unique_saturated_into(), }); - // let origin = RawOrigin::Signed(account_id_src); - // Dispatch the add_stake call - try_dispatch_runtime_call(handle, call, contract_to_origin(&CONTRACT_ADDRESS_SS58)?) + + try_dispatch_runtime_call(handle, call, RawOrigin::Signed(account_id)) } fn remove_stake(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { + let account_id = + as AddressMapping>::into_account_id( + handle.context().caller, + ); let (hotkey, _) = get_pubkey(data)?; let netuid = Self::parse_netuid(data, 0x5E)?; @@ -115,10 +126,14 @@ impl StakingPrecompile { netuid, amount_unstaked: amount_sub.unique_saturated_into(), }); - try_dispatch_runtime_call(handle, call, contract_to_origin(&CONTRACT_ADDRESS_SS58)?) + try_dispatch_runtime_call(handle, call, RawOrigin::Signed(account_id)) } fn add_proxy(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { + let account_id = + as AddressMapping>::into_account_id( + handle.context().caller, + ); let (delegate, _) = get_pubkey(data)?; let delegate = ::Lookup::unlookup(delegate); let call = RuntimeCall::Proxy(pallet_proxy::Call::::add_proxy { @@ -127,10 +142,14 @@ impl StakingPrecompile { delay: 0, }); - try_dispatch_runtime_call(handle, call, contract_to_origin(&CONTRACT_ADDRESS_SS58)?) + try_dispatch_runtime_call(handle, call, RawOrigin::Signed(account_id)) } fn remove_proxy(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult { + let account_id = + as AddressMapping>::into_account_id( + handle.context().caller, + ); let (delegate, _) = get_pubkey(data)?; let delegate = ::Lookup::unlookup(delegate); let call = RuntimeCall::Proxy(pallet_proxy::Call::::remove_proxy { @@ -139,7 +158,7 @@ impl StakingPrecompile { delay: 0, }); - try_dispatch_runtime_call(handle, call, contract_to_origin(&CONTRACT_ADDRESS_SS58)?) + try_dispatch_runtime_call(handle, call, RawOrigin::Signed(account_id)) } fn get_stake(data: &[u8]) -> PrecompileResult { @@ -167,17 +186,37 @@ impl StakingPrecompile { }) } - fn parse_netuid(data: &[u8], offset: usize) -> Result { - if data.len() < offset + 2 { + fn transfer_back_to_caller( + account_id: &AccountId32, + amount: U256, + ) -> Result<(), PrecompileFailure> { + let smart_contract_account_id: AccountId32 = CONTRACT_ADDRESS_SS58.into(); + + let amount_sub = + ::BalanceConverter::into_substrate_balance(amount) + .ok_or(ExitError::OutOfFund)?; + + // Create a transfer call from the smart contract to the caller + let transfer_call = + RuntimeCall::Balances(pallet_balances::Call::::transfer_allow_death { + dest: account_id.clone().into(), + value: amount_sub.unique_saturated_into(), + }); + + // Execute the transfer + let transfer_result = + transfer_call.dispatch(RawOrigin::Signed(smart_contract_account_id).into()); + + if let Err(dispatch_error) = transfer_result { + log::error!( + "Transfer back to caller failed. Error: {:?}", + dispatch_error + ); return Err(PrecompileFailure::Error { - exit_status: ExitError::InvalidRange, + exit_status: ExitError::Other("Transfer back to caller failed".into()), }); } - let mut netuid_bytes = [0u8; 2]; - netuid_bytes.copy_from_slice(get_slice(data, offset, offset + 2)?); - let netuid: u16 = netuid_bytes[1] as u16 | ((netuid_bytes[0] as u16) << 8u16); - - Ok(netuid) + Ok(()) } } diff --git a/runtime/src/precompiles/subnet.rs b/runtime/src/precompiles/subnet.rs index 36f861d48..2701c67d3 100644 --- a/runtime/src/precompiles/subnet.rs +++ b/runtime/src/precompiles/subnet.rs @@ -1,11 +1,13 @@ -use crate::precompiles::{ - contract_to_origin, get_method_id, get_pubkey, get_slice, try_dispatch_runtime_call, -}; +use crate::precompiles::{get_method_id, get_pubkey, get_slice, try_dispatch_runtime_call}; use crate::{Runtime, RuntimeCall}; -use pallet_evm::{ExitError, PrecompileFailure, PrecompileHandle, PrecompileResult}; +use frame_system::RawOrigin; +use pallet_evm::{ + AddressMapping, ExitError, HashedAddressMapping, PrecompileFailure, PrecompileHandle, + PrecompileResult, +}; +use sp_runtime::traits::BlakeTwo256; use sp_runtime::AccountId32; use sp_std::vec; - pub const SUBNET_PRECOMPILE_INDEX: u64 = 2051; // bytes with max lenght 1K pub const MAX_SINGLE_PARAMETER_SIZE: usize = 1024; @@ -51,7 +53,6 @@ impl SubnetPrecompile { let call = match data.len() { 32 => { let (hotkey, _) = get_pubkey(data)?; - RuntimeCall::SubtensorModule( pallet_subtensor::Call::::register_network_with_identity { hotkey, @@ -85,8 +86,13 @@ impl SubnetPrecompile { } }; + let account_id = + as AddressMapping>::into_account_id( + handle.context().caller, + ); + // Dispatch the register_network call - try_dispatch_runtime_call(handle, call, contract_to_origin(&CONTRACT_ADDRESS_SS58)?) + try_dispatch_runtime_call(handle, call, RawOrigin::Signed(account_id)) } fn parse_register_network_parameters( From cdfffaf05a816a9cfd84af2b15e76fe32388ad5d Mon Sep 17 00:00:00 2001 From: open-junius Date: Fri, 31 Jan 2025 00:22:16 +0800 Subject: [PATCH 23/23] fix clippy --- runtime/src/precompiles/staking.rs | 4 ++-- runtime/src/precompiles/subnet.rs | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/runtime/src/precompiles/staking.rs b/runtime/src/precompiles/staking.rs index eefde3a37..8cc1c879a 100644 --- a/runtime/src/precompiles/staking.rs +++ b/runtime/src/precompiles/staking.rs @@ -83,7 +83,7 @@ impl StakingPrecompile { let (hotkey, _) = get_pubkey(data)?; let amount: U256 = handle.context().apparent_value; - let netuid = Self::parse_netuid(data, 0x3E)?; + let netuid = parse_netuid(data, 0x3E)?; if !amount.is_zero() { Self::transfer_back_to_caller(&account_id, amount)?; @@ -108,7 +108,7 @@ impl StakingPrecompile { handle.context().caller, ); let (hotkey, _) = get_pubkey(data)?; - let netuid = Self::parse_netuid(data, 0x5E)?; + let netuid = parse_netuid(data, 0x5E)?; // We have to treat this as uint256 (because of Solidity ABI encoding rules, it pads uint64), // but this will never exceed 8 bytes, se we will ignore higher bytes and will only use lower diff --git a/runtime/src/precompiles/subnet.rs b/runtime/src/precompiles/subnet.rs index 2701c67d3..9944572c5 100644 --- a/runtime/src/precompiles/subnet.rs +++ b/runtime/src/precompiles/subnet.rs @@ -16,6 +16,7 @@ pub const MAX_PARAMETER_SIZE: usize = 3 * MAX_SINGLE_PARAMETER_SIZE; // 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] = [ 0x3a, 0x86, 0x18, 0xfb, 0xbb, 0x1b, 0xbc, 0x47, 0x86, 0x64, 0xff, 0x53, 0x46, 0x18, 0x0c, 0x35, 0xd0, 0x9f, 0xac, 0x26, 0xf2, 0x02, 0x70, 0x85, 0xb3, 0x1c, 0x56, 0xc1, 0x06, 0x3c, 0x1c, 0xd3,