Skip to content

add subnets precompile #1110

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

Merged
merged 31 commits into from
Jan 30, 2025
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
bc8c514
add subnet precompile contract
open-junius Dec 10, 2024
157032b
remove commented code
open-junius Dec 10, 2024
5435ccd
refactor code
open-junius Dec 10, 2024
7169908
fix clippy
open-junius Dec 11, 2024
7155329
merge with target branch
open-junius Dec 11, 2024
cdda2f6
fix compile error
open-junius Dec 11, 2024
e565825
add memory storage
open-junius Dec 16, 2024
6a0f6f9
add subnets precompile
open-junius Dec 20, 2024
2141e00
Merge branch 'devnet-ready' into feat/subnet-precompile
open-junius Dec 20, 2024
2284e31
merge with subnet precompile
open-junius Dec 20, 2024
be5bc35
merge with target branch
open-junius Jan 16, 2025
1643238
fix compilation
open-junius Jan 16, 2025
5040e52
update sol
open-junius Jan 16, 2025
1341e7a
rename contract
open-junius Jan 17, 2025
23f9b87
fix e2e test
open-junius Jan 20, 2025
f3c6059
Merge branch 'devnet-ready' into neuron-precompile
open-junius Jan 20, 2025
9de1927
commit Cargo.lock
open-junius Jan 20, 2025
3d05f7b
cargo clippy
open-junius Jan 20, 2025
1a34d33
refactor helper
open-junius Jan 23, 2025
541518e
fix compilation error
open-junius Jan 23, 2025
d148c2b
refactor get pubkey method
open-junius Jan 23, 2025
96a1d08
cargo clippy
open-junius Jan 23, 2025
241d620
cargo fmt
open-junius Jan 23, 2025
a0819b4
remove byte_to_account_id
open-junius Jan 23, 2025
4a73af6
merge with devnet ready
open-junius Jan 24, 2025
682ac1a
Merge branch 'devnet-ready' into neuron-precompile
open-junius Jan 28, 2025
ae60405
merge with target branch
open-junius Jan 30, 2025
f644f26
refactor code
open-junius Jan 30, 2025
f25cabc
add len check
open-junius Jan 30, 2025
998867e
fix wrong origin
open-junius Jan 30, 2025
cdfffaf
fix clippy
open-junius Jan 30, 2025
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
110 changes: 106 additions & 4 deletions runtime/src/precompiles/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,40 @@ 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;
mod subnets;

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

use subnet::*;
use subnets::*;
pub struct FrontierPrecompiles<R>(PhantomData<R>);

impl<R> Default for FrontierPrecompiles<R>
where
R: pallet_evm::Config,
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; 13] {
[
hash(1),
hash(2),
Expand All @@ -51,7 +65,9 @@ where
hash(EDVERIFY_PRECOMPILE_INDEX),
hash(BALANCE_TRANSFER_INDEX),
hash(STAKING_PRECOMPILE_INDEX),
hash(SUBNET_PRECOMPILE_INDEX),
hash(METAGRAPH_PRECOMPILE_INDEX),
hash(SUBNETS_PRECOMPILE_INDEX),
]
}
}
Expand All @@ -76,9 +92,11 @@ 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))
}
a if a == hash(SUBNETS_PRECOMPILE_INDEX) => Some(SubnetsPrecompile::execute(handle)),

_ => None,
}
Expand Down Expand Up @@ -125,8 +143,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()),
}),
}
}
130 changes: 69 additions & 61 deletions runtime/src/precompiles/solidity/staking.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,69 +3,77 @@ 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 (uint256).
*
* Requirements:
* - `hotkey` must be a valid hotkey registered on the network, ensuring that the stake is
* correctly attributed.
*/
function addStake(bytes32 hotkey, uint256 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 (uint256).
*
* Requirements:
* - `hotkey` must be a valid hotkey registered on the network, ensuring that the stake is
* correctly attributed.
*/
function addStake(bytes32 hotkey, uint256 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,
* which effectively calls `remove_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 amount The amount to unstake in rao.
* @param netuid The subnet to stake to (uint256).
*
* Requirements:
* - `hotkey` must be a valid hotkey registered on the network, ensuring that the stake is
* correctly attributed.
* - The existing stake amount must be not lower than specified amount
*/
function removeStake(bytes32 hotkey, uint256 amount, uint256 netuid) external;
/**
* @dev Removes a subtensor stake `amount` from the specified `hotkey`.
*
* This function allows external accounts and contracts to unstake TAO from the subtensor pallet,
* which effectively calls `remove_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 amount The amount to unstake in rao.
* @param netuid The subnet to stake to (uint256).
*
* Requirements:
* - `hotkey` must be a valid hotkey registered on the network, ensuring that the stake is
* correctly attributed.
* - The existing stake amount must be not lower than specified amount
*/
function removeStake(
bytes32 hotkey,
uint256 amount,
uint256 netuid
) external;

/**
* @dev Delegates staking to a proxy account.
*
* @param delegate The public key (32 bytes) of the delegate.
*/
function addProxy(bytes32 delegate) external;
/**
* @dev Delegates staking to a proxy account.
*
* @param delegate The public key (32 bytes) of the delegate.
*/
function addProxy(bytes32 delegate) external;

/**
* @dev Removes staking proxy account.
*
* @param delegate The public key (32 bytes) of the delegate.
*/
function removeProxy(bytes32 delegate) external;
/**
* @dev Removes staking proxy account.
*
* @param delegate The public key (32 bytes) of the delegate.
*/
function removeProxy(bytes32 delegate) external;

/**
* @dev Returns the stake amount associated with the specified `hotkey` and `coldkey`.
*
* This function retrieves the current stake amount linked to a specific hotkey and coldkey pair.
* It is a view function, meaning it does not modify the state of the contract and is free to call.
*
* @param hotkey The hotkey public key (32 bytes).
* @param coldkey The coldkey public key (32 bytes).
* @param netuid The subnet the stake is on (uint256).
* @return The current stake amount in uint256 format.
*/
function getStake(bytes32 hotkey, bytes32 coldkey, uint256 netuid) external view returns (uint256);
/**
* @dev Returns the stake amount associated with the specified `hotkey` and `coldkey`.
*
* This function retrieves the current stake amount linked to a specific hotkey and coldkey pair.
* It is a view function, meaning it does not modify the state of the contract and is free to call.
*
* @param hotkey The hotkey public key (32 bytes).
* @param coldkey The coldkey public key (32 bytes).
* @param netuid The subnet the stake is on (uint256).
* @return The current stake amount in uint256 format.
*/
function getStake(
bytes32 hotkey,
bytes32 coldkey,
uint256 netuid
) external view returns (uint256);
}
43 changes: 43 additions & 0 deletions runtime/src/precompiles/solidity/subnet.abi
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
[
{
"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",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "githubRepo",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "subnetContact",
"type": "bytes"
}
],
"name": "registerNetwork",
"outputs": [],
"stateMutability": "payable",
"type": "function"
}
]
15 changes: 15 additions & 0 deletions runtime/src/precompiles/solidity/subnet.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
pragma solidity ^0.8.0;

address constant ISTAKING_ADDRESS = 0x0000000000000000000000000000000000000803;

interface ISubnet {
/// 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;
}
20 changes: 20 additions & 0 deletions runtime/src/precompiles/solidity/subnets.abi
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
[
{
"inputs": [
{
"internalType": "uint16",
"name": "netuid",
"type": "uint16"
},
{
"internalType": "bytes32",
"name": "hotkey",
"type": "bytes32"
}
],
"name": "burnedRegister",
"outputs": [],
"stateMutability": "payable",
"type": "function"
}
]
Loading
Loading