Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add subnet precompile contract #1082

Open
wants to merge 9 commits into
base: devnet-ready
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 102 additions & 2 deletions runtime/src/precompiles/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,37 @@ 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 metagraph;
mod staking;
mod subnet;

use balance_transfer::*;
use ed25519::*;
use metagraph::*;
use staking::*;
use subnet::*;

pub struct FrontierPrecompiles<R>(PhantomData<R>);

Expand All @@ -39,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),
Expand All @@ -51,6 +65,7 @@ where
hash(EDVERIFY_PRECOMPILE_INDEX),
hash(BALANCE_TRANSFER_INDEX),
hash(STAKING_PRECOMPILE_INDEX),
hash(SUBNET_PRECOMPILE_INDEX),
hash(METAGRAPH_PRECOMPILE_INDEX),
]
}
Expand All @@ -76,6 +91,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)),
a if a == hash(METAGRAPH_PRECOMPILE_INDEX) => {
Some(MetagraphPrecompile::execute(handle))
}
Expand Down Expand Up @@ -125,8 +141,92 @@ 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,
})
}
}

/// 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 =
<Runtime as pallet_evm::Config>::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::<Runtime>::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 =
<HashedAddressMapping<BlakeTwo256> as AddressMapping<AccountId32>>::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()),
}),
}
}
32 changes: 32 additions & 0 deletions runtime/src/precompiles/solidity/subnet.abi
Original file line number Diff line number Diff line change
@@ -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"
}
]
10 changes: 10 additions & 0 deletions runtime/src/precompiles/solidity/subnet.sol
Original file line number Diff line number Diff line change
@@ -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 memory subnetName, bytes memory githubRepo, bytes memory subnetContact) external payable;
}
93 changes: 8 additions & 85 deletions runtime/src/precompiles/staking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,18 @@
// - 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};
pub const STAKING_PRECOMPILE_INDEX: u64 = 2049;

// this is staking smart contract's(0x0000000000000000000000000000000000000801) sr25519 address
pub const STAKING_CONTRACT_ADDRESS: &str = "5CwnBK9Ack1mhznmCnwiibCNQc174pYQVktYW3ayRpLm4K2X";
pub struct StakingPrecompile;

impl StakingPrecompile {
Expand Down Expand Up @@ -78,7 +73,7 @@ impl StakingPrecompile {
amount_staked: amount_sub.unique_saturated_into(),
});
// Dispatch the add_stake call
Self::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();
Expand All @@ -98,7 +93,7 @@ impl StakingPrecompile {
hotkey,
amount_unstaked: amount_sub.unique_saturated_into(),
});
Self::dispatch(handle, call)
dispatch(handle, call, STAKING_CONTRACT_ADDRESS)
}

fn parse_hotkey(data: &[u8]) -> Result<[u8; 32], PrecompileFailure> {
Expand All @@ -111,76 +106,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 =
<HashedAddressMapping<BlakeTwo256> as AddressMapping<AccountId32>>::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 =
<Runtime as pallet_evm::Config>::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::<Runtime>::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(())
}
}
Loading
Loading