Skip to content

Commit 126f780

Browse files
committed
Refactor staking and neuron precompile with precompile-utils
1 parent 58ed35d commit 126f780

File tree

5 files changed

+90
-124
lines changed

5 files changed

+90
-124
lines changed

runtime/src/precompiles/balance_transfer.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,6 @@ impl BalanceTransferPrecompile {
4848
value: amount_sub.unique_saturated_into(),
4949
};
5050

51-
try_dispatch_runtime_call(handle, call, contract_to_origin(&CONTRACT_ADDRESS_SS58)?)?;
52-
53-
Ok(())
51+
try_dispatch_runtime_call(handle, call, contract_to_origin(&CONTRACT_ADDRESS_SS58)?)
5452
}
5553
}

runtime/src/precompiles/mod.rs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ use pallet_evm::{
1212
use pallet_evm_precompile_modexp::Modexp;
1313
use pallet_evm_precompile_sha3fips::Sha3FIPS256;
1414
use pallet_evm_precompile_simple::{ECRecover, ECRecoverPublicKey, Identity, Ripemd160, Sha256};
15-
use sp_core::{hashing::keccak_256, H160};
15+
use precompile_utils::EvmResult;
16+
use sp_core::{hashing::keccak_256, H160, U256};
1617
use sp_runtime::{traits::Dispatchable, AccountId32};
1718
use sp_std::vec;
1819

@@ -151,6 +152,12 @@ pub fn get_pubkey(data: &[u8]) -> Result<(AccountId32, vec::Vec<u8>), Precompile
151152
))
152153
}
153154

155+
fn try_u16_from_u256(value: U256) -> Result<u16, PrecompileFailure> {
156+
u16::try_from(value.as_u32()).map_err(|_| PrecompileFailure::Error {
157+
exit_status: ExitError::Other("the value is outside of u16 bounds".into()),
158+
})
159+
}
160+
154161
fn parse_netuid(data: &[u8], offset: usize) -> Result<u16, PrecompileFailure> {
155162
if data.len() < offset + 2 {
156163
return Err(PrecompileFailure::Error {
@@ -175,7 +182,7 @@ fn try_dispatch_runtime_call(
175182
handle: &mut impl PrecompileHandle,
176183
call: impl Into<RuntimeCall>,
177184
origin: RawOrigin<AccountId32>,
178-
) -> PrecompileResult {
185+
) -> EvmResult<()> {
179186
let call = Into::<RuntimeCall>::into(call);
180187
let info = call.get_dispatch_info();
181188

@@ -220,10 +227,7 @@ fn try_dispatch_runtime_call(
220227

221228
log::info!("Dispatch succeeded. Post info: {:?}", post_info);
222229

223-
Ok(PrecompileOutput {
224-
exit_status: ExitSucceed::Returned,
225-
output: Default::default(),
226-
})
230+
Ok(())
227231
}
228232
Err(e) => {
229233
log::error!("Dispatch failed. Error: {:?}", e);

runtime/src/precompiles/neuron.rs

Lines changed: 22 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,39 @@
1-
use pallet_evm::{ExitError, PrecompileFailure, PrecompileHandle, PrecompileResult};
2-
3-
use crate::precompiles::{
4-
contract_to_origin, get_method_id, get_pubkey, get_slice, parse_netuid,
5-
try_dispatch_runtime_call,
6-
};
1+
use frame_system::RawOrigin;
2+
use pallet_evm::{AddressMapping, HashedAddressMapping, PrecompileHandle};
3+
use precompile_utils::EvmResult;
4+
use sp_core::H256;
5+
use sp_runtime::traits::BlakeTwo256;
76
use sp_runtime::AccountId32;
8-
use sp_std::vec;
97

8+
use crate::precompiles::{get_pubkey, try_dispatch_runtime_call};
109
use crate::{Runtime, RuntimeCall};
10+
1111
pub const NEURON_PRECOMPILE_INDEX: u64 = 2052;
1212

13-
// ss58 public key i.e., the contract sends funds it received to the destination address from the
14-
// method parameter.
15-
const CONTRACT_ADDRESS_SS58: [u8; 32] = [
16-
0xbc, 0x46, 0x35, 0x79, 0xbc, 0x99, 0xf9, 0xee, 0x7c, 0x59, 0xed, 0xee, 0x20, 0x61, 0xa3, 0x09,
17-
0xd2, 0x1e, 0x68, 0xd5, 0x39, 0xb6, 0x40, 0xec, 0x66, 0x46, 0x90, 0x30, 0xab, 0x74, 0xc1, 0xdb,
18-
];
1913
pub struct NeuronPrecompile;
2014

15+
#[precompile_utils::precompile]
2116
impl NeuronPrecompile {
22-
pub fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult {
23-
let txdata = handle.input();
24-
let method_id = get_slice(txdata, 0, 4)?;
25-
let method_input = txdata
26-
.get(4..)
27-
.map_or_else(vec::Vec::new, |slice| slice.to_vec()); // Avoiding borrowing conflicts
28-
29-
match method_id {
30-
id if id == get_method_id("burnedRegister(uint16,bytes32)") => {
31-
Self::burned_register(handle, &method_input)
32-
}
33-
34-
_ => Err(PrecompileFailure::Error {
35-
exit_status: ExitError::InvalidRange,
36-
}),
37-
}
38-
}
39-
40-
pub fn burned_register(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult {
41-
let (netuid, hotkey) = Self::parse_netuid_hotkey_parameter(data)?;
17+
#[precompile::public("burnedRegister(uint16,bytes32)")]
18+
#[precompile::payable]
19+
fn burned_register(
20+
handle: &mut impl PrecompileHandle,
21+
netuid: u16,
22+
hotkey: H256,
23+
) -> EvmResult<()> {
24+
let coldkey =
25+
<HashedAddressMapping<BlakeTwo256> as AddressMapping<AccountId32>>::into_account_id(
26+
handle.context().caller,
27+
);
28+
let (hotkey, _) = get_pubkey(hotkey.as_bytes())?;
4229
let call =
4330
RuntimeCall::SubtensorModule(pallet_subtensor::Call::<Runtime>::burned_register {
4431
netuid,
4532
hotkey,
4633
});
47-
try_dispatch_runtime_call(handle, call, contract_to_origin(&CONTRACT_ADDRESS_SS58)?)
48-
}
49-
50-
fn parse_netuid_hotkey_parameter(data: &[u8]) -> Result<(u16, AccountId32), PrecompileFailure> {
51-
if data.len() < 64 {
52-
return Err(PrecompileFailure::Error {
53-
exit_status: ExitError::InvalidRange,
54-
});
55-
}
56-
let netuid = parse_netuid(data, 30)?;
5734

58-
let (hotkey, _) = get_pubkey(get_slice(data, 32, 64)?)?;
35+
try_dispatch_runtime_call(handle, call, RawOrigin::Signed(coldkey))?;
5936

60-
Ok((netuid, hotkey))
37+
Ok(())
6138
}
6239
}

runtime/src/precompiles/staking.rs

Lines changed: 45 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,23 @@
2525
// - Precompile checks the result of do_remove_stake and, in case of a failure, reverts the transaction.
2626
//
2727

28-
use crate::precompiles::{
29-
get_method_id, get_pubkey, get_slice, parse_netuid, try_dispatch_runtime_call,
30-
};
31-
use crate::{ProxyType, Runtime, RuntimeCall};
3228
use frame_system::RawOrigin;
3329
use pallet_evm::{
3430
AddressMapping, BalanceConverter, ExitError, ExitSucceed, HashedAddressMapping,
3531
PrecompileFailure, PrecompileHandle, PrecompileOutput, PrecompileResult,
3632
};
37-
use sp_core::U256;
33+
use precompile_utils::EvmResult;
34+
use sp_core::{H256, U256};
3835
use sp_runtime::traits::{BlakeTwo256, Dispatchable, StaticLookup, UniqueSaturatedInto};
3936
use sp_runtime::AccountId32;
4037
use sp_std::vec;
4138

39+
use crate::precompiles::{
40+
get_method_id, get_pubkey, get_slice, parse_netuid, try_dispatch_runtime_call,
41+
try_u16_from_u256,
42+
};
43+
use crate::{ProxyType, Runtime, RuntimeCall};
44+
4245
pub const STAKING_PRECOMPILE_INDEX: u64 = 2049;
4346

4447
// ss58 public key i.e., the contract sends funds it received to the destination address from the
@@ -50,40 +53,17 @@ const CONTRACT_ADDRESS_SS58: [u8; 32] = [
5053

5154
pub struct StakingPrecompile;
5255

56+
#[precompile_utils::precompile]
5357
impl StakingPrecompile {
54-
pub fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult {
55-
let txdata = handle.input();
56-
let method_id = get_slice(txdata, 0, 4)?;
57-
let method_input = txdata
58-
.get(4..)
59-
.map_or_else(vec::Vec::new, |slice| slice.to_vec()); // Avoiding borrowing conflicts
60-
61-
if method_id == get_method_id("addStake(bytes32,uint256)") {
62-
Self::add_stake(handle, &method_input)
63-
} else if method_id == get_method_id("removeStake(bytes32,uint256,uint256)") {
64-
Self::remove_stake(handle, &method_input)
65-
} else if method_id == get_method_id("getStake(bytes32,bytes32,uint256)") {
66-
Self::get_stake(&method_input)
67-
} else if method_id == get_method_id("addProxy(bytes32)") {
68-
Self::add_proxy(handle, &method_input)
69-
} else if method_id == get_method_id("removeProxy(bytes32)") {
70-
Self::remove_proxy(handle, &method_input)
71-
} else {
72-
Err(PrecompileFailure::Error {
73-
exit_status: ExitError::InvalidRange,
74-
})
75-
}
76-
}
77-
78-
fn add_stake(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult {
58+
#[precompile::public("addStake(bytes32,uint256)")]
59+
#[precompile::payable]
60+
fn add_stake(handle: &mut impl PrecompileHandle, address: H256, netuid: U256) -> EvmResult<()> {
7961
let account_id =
8062
<HashedAddressMapping<BlakeTwo256> as AddressMapping<AccountId32>>::into_account_id(
8163
handle.context().caller,
8264
);
8365

84-
let (hotkey, _) = get_pubkey(data)?;
85-
let amount: U256 = handle.context().apparent_value;
86-
let netuid = parse_netuid(data, 0x3E)?;
66+
let amount = handle.context().apparent_value;
8767

8868
if !amount.is_zero() {
8969
Self::transfer_back_to_caller(&account_id, amount)?;
@@ -93,6 +73,8 @@ impl StakingPrecompile {
9373
<Runtime as pallet_evm::Config>::BalanceConverter::into_substrate_balance(amount)
9474
.ok_or(ExitError::OutOfFund)?;
9575

76+
let (hotkey, _) = get_pubkey(address.as_bytes())?;
77+
let netuid = try_u16_from_u256(netuid)?;
9678
let call = RuntimeCall::SubtensorModule(pallet_subtensor::Call::<Runtime>::add_stake {
9779
hotkey,
9880
netuid,
@@ -102,39 +84,40 @@ impl StakingPrecompile {
10284
try_dispatch_runtime_call(handle, call, RawOrigin::Signed(account_id))
10385
}
10486

105-
fn remove_stake(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult {
87+
#[precompile::public("removeStake(bytes32,uint256,uint256)")]
88+
fn remove_stake(
89+
handle: &mut impl PrecompileHandle,
90+
address: H256,
91+
amount: U256,
92+
netuid: U256,
93+
) -> EvmResult<()> {
10694
let account_id =
10795
<HashedAddressMapping<BlakeTwo256> as AddressMapping<AccountId32>>::into_account_id(
10896
handle.context().caller,
10997
);
110-
let (hotkey, _) = get_pubkey(data)?;
111-
let netuid = parse_netuid(data, 0x5E)?;
112-
113-
// We have to treat this as uint256 (because of Solidity ABI encoding rules, it pads uint64),
114-
// but this will never exceed 8 bytes, se we will ignore higher bytes and will only use lower
115-
// 8 bytes.
116-
let amount = data
117-
.get(56..64)
118-
.map(U256::from_big_endian)
119-
.ok_or(ExitError::OutOfFund)?;
98+
12099
let amount_sub =
121100
<Runtime as pallet_evm::Config>::BalanceConverter::into_substrate_balance(amount)
122101
.ok_or(ExitError::OutOfFund)?;
123102

103+
let (hotkey, _) = get_pubkey(address.as_bytes())?;
104+
let netuid = try_u16_from_u256(netuid)?;
124105
let call = RuntimeCall::SubtensorModule(pallet_subtensor::Call::<Runtime>::remove_stake {
125106
hotkey,
126107
netuid,
127108
amount_unstaked: amount_sub.unique_saturated_into(),
128109
});
110+
129111
try_dispatch_runtime_call(handle, call, RawOrigin::Signed(account_id))
130112
}
131113

132-
fn add_proxy(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult {
114+
#[precompile::public("addProxy(bytes32)")]
115+
fn add_proxy(handle: &mut impl PrecompileHandle, delegate: H256) -> EvmResult<()> {
133116
let account_id =
134117
<HashedAddressMapping<BlakeTwo256> as AddressMapping<AccountId32>>::into_account_id(
135118
handle.context().caller,
136119
);
137-
let (delegate, _) = get_pubkey(data)?;
120+
let (delegate, _) = get_pubkey(delegate.as_bytes())?;
138121
let delegate = <Runtime as frame_system::Config>::Lookup::unlookup(delegate);
139122
let call = RuntimeCall::Proxy(pallet_proxy::Call::<Runtime>::add_proxy {
140123
delegate,
@@ -145,12 +128,13 @@ impl StakingPrecompile {
145128
try_dispatch_runtime_call(handle, call, RawOrigin::Signed(account_id))
146129
}
147130

148-
fn remove_proxy(handle: &mut impl PrecompileHandle, data: &[u8]) -> PrecompileResult {
131+
#[precompile::public("removeProxy(bytes32)")]
132+
fn remove_proxy(handle: &mut impl PrecompileHandle, delegate: H256) -> EvmResult<()> {
149133
let account_id =
150134
<HashedAddressMapping<BlakeTwo256> as AddressMapping<AccountId32>>::into_account_id(
151135
handle.context().caller,
152136
);
153-
let (delegate, _) = get_pubkey(data)?;
137+
let (delegate, _) = get_pubkey(delegate.as_bytes())?;
154138
let delegate = <Runtime as frame_system::Config>::Lookup::unlookup(delegate);
155139
let call = RuntimeCall::Proxy(pallet_proxy::Call::<Runtime>::remove_proxy {
156140
delegate,
@@ -161,29 +145,25 @@ impl StakingPrecompile {
161145
try_dispatch_runtime_call(handle, call, RawOrigin::Signed(account_id))
162146
}
163147

164-
fn get_stake(data: &[u8]) -> PrecompileResult {
165-
let (hotkey, left_data) = get_pubkey(data)?;
166-
let (coldkey, _) = get_pubkey(&left_data)?;
167-
let netuid = parse_netuid(data, 0x5E)?;
148+
#[precompile::public("getStake(bytes32,bytes32,uint256)")]
149+
#[precompile::view]
150+
fn get_stake(
151+
_: &mut impl PrecompileHandle,
152+
hotkey: H256,
153+
coldkey: H256,
154+
netuid: U256,
155+
) -> EvmResult<U256> {
156+
let (hotkey, _) = get_pubkey(hotkey.as_bytes())?;
157+
let (coldkey, _) = get_pubkey(coldkey.as_bytes())?;
158+
let netuid = try_u16_from_u256(netuid)?;
168159

169160
let stake = pallet_subtensor::Pallet::<Runtime>::get_stake_for_hotkey_and_coldkey_on_subnet(
170161
&hotkey, &coldkey, netuid,
171162
);
172163

173164
// Convert to EVM decimals
174-
let stake_u256 = U256::from(stake);
175-
let stake_eth =
176-
<Runtime as pallet_evm::Config>::BalanceConverter::into_evm_balance(stake_u256)
177-
.ok_or(ExitError::InvalidRange)?;
178-
179-
// Format output
180-
let mut result = [0_u8; 32];
181-
U256::to_big_endian(&stake_eth, &mut result);
182-
183-
Ok(PrecompileOutput {
184-
exit_status: ExitSucceed::Returned,
185-
output: result.into(),
186-
})
165+
<Runtime as pallet_evm::Config>::BalanceConverter::into_evm_balance(stake.into())
166+
.ok_or(ExitError::InvalidRange.into())
187167
}
188168

189169
fn transfer_back_to_caller(

runtime/src/precompiles/subnet.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
1-
use crate::precompiles::{get_method_id, get_pubkey, get_slice, try_dispatch_runtime_call};
2-
use crate::{Runtime, RuntimeCall};
31
use frame_system::RawOrigin;
42
use pallet_evm::{
5-
AddressMapping, ExitError, HashedAddressMapping, PrecompileFailure, PrecompileHandle,
6-
PrecompileResult,
3+
AddressMapping, ExitError, ExitSucceed, HashedAddressMapping, PrecompileFailure,
4+
PrecompileHandle, PrecompileOutput, PrecompileResult,
75
};
86
use sp_runtime::traits::BlakeTwo256;
97
use sp_runtime::AccountId32;
108
use sp_std::vec;
9+
10+
use crate::precompiles::{get_method_id, get_pubkey, get_slice, try_dispatch_runtime_call};
11+
use crate::{Runtime, RuntimeCall};
12+
1113
pub const SUBNET_PRECOMPILE_INDEX: u64 = 2051;
1214
// bytes with max lenght 1K
1315
pub const MAX_SINGLE_PARAMETER_SIZE: usize = 1024;
@@ -21,6 +23,7 @@ const CONTRACT_ADDRESS_SS58: [u8; 32] = [
2123
0x3a, 0x86, 0x18, 0xfb, 0xbb, 0x1b, 0xbc, 0x47, 0x86, 0x64, 0xff, 0x53, 0x46, 0x18, 0x0c, 0x35,
2224
0xd0, 0x9f, 0xac, 0x26, 0xf2, 0x02, 0x70, 0x85, 0xb3, 0x1c, 0x56, 0xc1, 0x06, 0x3c, 0x1c, 0xd3,
2325
];
26+
2427
pub struct SubnetPrecompile;
2528

2629
impl SubnetPrecompile {
@@ -93,7 +96,11 @@ impl SubnetPrecompile {
9396
);
9497

9598
// Dispatch the register_network call
96-
try_dispatch_runtime_call(handle, call, RawOrigin::Signed(account_id))
99+
try_dispatch_runtime_call(handle, call, RawOrigin::Signed(account_id))?;
100+
Ok(PrecompileOutput {
101+
exit_status: ExitSucceed::Returned,
102+
output: Default::default(),
103+
})
97104
}
98105

99106
fn parse_register_network_parameters(

0 commit comments

Comments
 (0)