Skip to content

Commit a819d77

Browse files
authored
Merge pull request #1298 from opentensor/feat/validate-serve-axon
Add serve_axon extrinsic validation
2 parents 279e0e9 + f111118 commit a819d77

File tree

4 files changed

+180
-29
lines changed

4 files changed

+180
-29
lines changed

pallets/subtensor/src/lib.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1674,6 +1674,10 @@ pub enum CustomTransactionError {
16741674
InsufficientLiquidity,
16751675
SlippageTooHigh,
16761676
TransferDisallowed,
1677+
HotKeyNotRegisteredInNetwork,
1678+
InvalidIpAddress,
1679+
ServingRateLimitExceeded,
1680+
InvalidPort,
16771681
BadRequest,
16781682
}
16791683

@@ -1690,6 +1694,10 @@ impl From<CustomTransactionError> for u8 {
16901694
CustomTransactionError::InsufficientLiquidity => 7,
16911695
CustomTransactionError::SlippageTooHigh => 8,
16921696
CustomTransactionError::TransferDisallowed => 9,
1697+
CustomTransactionError::HotKeyNotRegisteredInNetwork => 10,
1698+
CustomTransactionError::InvalidIpAddress => 11,
1699+
CustomTransactionError::ServingRateLimitExceeded => 12,
1700+
CustomTransactionError::InvalidPort => 13,
16931701
CustomTransactionError::BadRequest => 255,
16941702
}
16951703
}
@@ -1773,6 +1781,22 @@ where
17731781
CustomTransactionError::TransferDisallowed.into(),
17741782
)
17751783
.into()),
1784+
Error::<T>::HotKeyNotRegisteredInNetwork => Err(InvalidTransaction::Custom(
1785+
CustomTransactionError::HotKeyNotRegisteredInNetwork.into(),
1786+
)
1787+
.into()),
1788+
Error::<T>::InvalidIpAddress => Err(InvalidTransaction::Custom(
1789+
CustomTransactionError::InvalidIpAddress.into(),
1790+
)
1791+
.into()),
1792+
Error::<T>::ServingRateLimitExceeded => Err(InvalidTransaction::Custom(
1793+
CustomTransactionError::ServingRateLimitExceeded.into(),
1794+
)
1795+
.into()),
1796+
Error::<T>::InvalidPort => Err(InvalidTransaction::Custom(
1797+
CustomTransactionError::InvalidPort.into(),
1798+
)
1799+
.into()),
17761800
_ => Err(
17771801
InvalidTransaction::Custom(CustomTransactionError::BadRequest.into()).into(),
17781802
),
@@ -2175,6 +2199,32 @@ where
21752199
})
21762200
}
21772201
}
2202+
Some(Call::serve_axon {
2203+
netuid,
2204+
version,
2205+
ip,
2206+
port,
2207+
ip_type,
2208+
protocol,
2209+
placeholder1,
2210+
placeholder2,
2211+
}) => {
2212+
// Fully validate the user input
2213+
Self::result_to_validity(
2214+
Pallet::<T>::validate_serve_axon(
2215+
who,
2216+
*netuid,
2217+
*version,
2218+
*ip,
2219+
*port,
2220+
*ip_type,
2221+
*protocol,
2222+
*placeholder1,
2223+
*placeholder2,
2224+
),
2225+
Self::get_priority_vanilla(),
2226+
)
2227+
}
21782228
_ => {
21792229
if let Some(
21802230
BalancesCall::transfer_keep_alive { .. }

pallets/subtensor/src/subnets/serving.rs

Lines changed: 71 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -69,35 +69,28 @@ impl<T: Config> Pallet<T> {
6969
// We check the callers (hotkey) signature.
7070
let hotkey_id = ensure_signed(origin)?;
7171

72-
// Ensure the hotkey is registered somewhere.
73-
ensure!(
74-
Self::is_hotkey_registered_on_any_network(&hotkey_id),
75-
Error::<T>::HotKeyNotRegisteredInNetwork
76-
);
77-
78-
// Check the ip signature validity.
79-
ensure!(Self::is_valid_ip_type(ip_type), Error::<T>::InvalidIpType);
80-
ensure!(
81-
Self::is_valid_ip_address(ip_type, ip),
82-
Error::<T>::InvalidIpAddress
83-
);
84-
85-
// Get the previous axon information.
86-
let mut prev_axon = Self::get_axon_info(netuid, &hotkey_id);
87-
let current_block: u64 = Self::get_current_block_as_u64();
88-
ensure!(
89-
Self::axon_passes_rate_limit(netuid, &prev_axon, current_block),
90-
Error::<T>::ServingRateLimitExceeded
91-
);
72+
// Validate user input
73+
Self::validate_serve_axon(
74+
&hotkey_id,
75+
netuid,
76+
version,
77+
ip,
78+
port,
79+
ip_type,
80+
protocol,
81+
placeholder1,
82+
placeholder2,
83+
)?;
9284

93-
// Check certificate
85+
// Check+insert certificate
9486
if let Some(certificate) = certificate {
9587
if let Ok(certificate) = NeuronCertificateOf::try_from(certificate) {
9688
NeuronCertificates::<T>::insert(netuid, hotkey_id.clone(), certificate)
9789
}
9890
}
9991

10092
// We insert the axon meta.
93+
let mut prev_axon = Self::get_axon_info(netuid, &hotkey_id);
10194
prev_axon.block = Self::get_current_block_as_u64();
10295
prev_axon.version = version;
10396
prev_axon.ip = ip;
@@ -176,19 +169,19 @@ impl<T: Config> Pallet<T> {
176169
// We check the callers (hotkey) signature.
177170
let hotkey_id = ensure_signed(origin)?;
178171

179-
// Ensure the hotkey is registered somewhere.
180-
ensure!(
181-
Self::is_hotkey_registered_on_any_network(&hotkey_id),
182-
Error::<T>::HotKeyNotRegisteredInNetwork
183-
);
184-
185172
// Check the ip signature validity.
186173
ensure!(Self::is_valid_ip_type(ip_type), Error::<T>::InvalidIpType);
187174
ensure!(
188175
Self::is_valid_ip_address(ip_type, ip),
189176
Error::<T>::InvalidIpAddress
190177
);
191178

179+
// Ensure the hotkey is registered somewhere.
180+
ensure!(
181+
Self::is_hotkey_registered_on_any_network(&hotkey_id),
182+
Error::<T>::HotKeyNotRegisteredInNetwork
183+
);
184+
192185
// We get the previous axon info assoicated with this ( netuid, uid )
193186
let mut prev_prometheus = Self::get_prometheus_info(netuid, &hotkey_id);
194187
let current_block: u64 = Self::get_current_block_as_u64();
@@ -332,4 +325,55 @@ impl<T: Config> Pallet<T> {
332325

333326
Ok(true)
334327
}
328+
329+
pub fn validate_serve_axon(
330+
hotkey_id: &T::AccountId,
331+
netuid: u16,
332+
version: u32,
333+
ip: u128,
334+
port: u16,
335+
ip_type: u8,
336+
protocol: u8,
337+
placeholder1: u8,
338+
placeholder2: u8,
339+
) -> Result<(), Error<T>> {
340+
// Ensure the hotkey is registered somewhere.
341+
ensure!(
342+
Self::is_hotkey_registered_on_any_network(hotkey_id),
343+
Error::<T>::HotKeyNotRegisteredInNetwork
344+
);
345+
346+
// Check the ip signature validity.
347+
ensure!(Self::is_valid_ip_type(ip_type), Error::<T>::InvalidIpType);
348+
ensure!(
349+
Self::is_valid_ip_address(ip_type, ip),
350+
Error::<T>::InvalidIpAddress
351+
);
352+
353+
// Get the previous axon information.
354+
let mut prev_axon = Self::get_axon_info(netuid, hotkey_id);
355+
let current_block: u64 = Self::get_current_block_as_u64();
356+
ensure!(
357+
Self::axon_passes_rate_limit(netuid, &prev_axon, current_block),
358+
Error::<T>::ServingRateLimitExceeded
359+
);
360+
361+
// Validate axon data with delegate func
362+
prev_axon.block = Self::get_current_block_as_u64();
363+
prev_axon.version = version;
364+
prev_axon.ip = ip;
365+
prev_axon.port = port;
366+
prev_axon.ip_type = ip_type;
367+
prev_axon.protocol = protocol;
368+
prev_axon.placeholder1 = placeholder1;
369+
prev_axon.placeholder2 = placeholder2;
370+
371+
let axon_validated = Self::validate_axon_data(&prev_axon);
372+
ensure!(
373+
axon_validated.is_ok(),
374+
axon_validated.err().unwrap_or(Error::<T>::InvalidPort)
375+
);
376+
377+
Ok(())
378+
}
335379
}

pallets/subtensor/src/tests/serving.rs

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ use super::mock::*;
22

33
use crate::Error;
44
use crate::*;
5-
use frame_support::assert_noop;
65
use frame_support::pallet_prelude::Weight;
6+
use frame_support::{assert_err, assert_noop};
77
use frame_support::{
88
assert_ok,
99
dispatch::{DispatchClass, DispatchInfo, GetDispatchInfo, Pays},
@@ -1257,3 +1257,60 @@ fn test_set_subnet_identity_dispatch_info_ok() {
12571257
assert_eq!(dispatch_info.pays_fee, Pays::Yes);
12581258
});
12591259
}
1260+
1261+
// cargo test --package pallet-subtensor --lib -- tests::serving::test_serve_axon_validate --exact --show-output
1262+
#[test]
1263+
fn test_serve_axon_validate() {
1264+
// Testing the signed extension validate function
1265+
// correctly filters the `serve_axon` transaction.
1266+
1267+
new_test_ext(0).execute_with(|| {
1268+
let hotkey = U256::from(1);
1269+
let netuid: u16 = 1;
1270+
let version: u32 = 2;
1271+
let ip: u128 = 1676056785;
1272+
let port: u16 = 128;
1273+
let ip_type: u8 = 4;
1274+
let protocol: u8 = 0;
1275+
let placeholder1: u8 = 0;
1276+
let placeholder2: u8 = 0;
1277+
1278+
// Serve axon bad call
1279+
let call = RuntimeCall::SubtensorModule(SubtensorCall::serve_axon {
1280+
netuid,
1281+
version,
1282+
ip,
1283+
port,
1284+
ip_type,
1285+
protocol,
1286+
placeholder1,
1287+
placeholder2,
1288+
});
1289+
1290+
let info: crate::DispatchInfo =
1291+
crate::DispatchInfoOf::<<Test as frame_system::Config>::RuntimeCall>::default();
1292+
1293+
let extension = crate::SubtensorSignedExtension::<Test>::new();
1294+
// Submit to the signed extension validate function
1295+
let result_bad = extension.validate(&hotkey, &call.clone(), &info, 10);
1296+
1297+
// Should fail due to insufficient stake
1298+
assert_err!(
1299+
result_bad,
1300+
crate::TransactionValidityError::Invalid(crate::InvalidTransaction::Custom(
1301+
CustomTransactionError::HotKeyNotRegisteredInNetwork.into()
1302+
))
1303+
);
1304+
1305+
// Register the hotkey in the subnet and try again
1306+
let coldkey = U256::from(1);
1307+
add_network(netuid, 13, 0);
1308+
register_ok_neuron(netuid, hotkey, coldkey, 0);
1309+
1310+
// Submit to the signed extension validate function
1311+
let result_ok = extension.validate(&hotkey, &call.clone(), &info, 10);
1312+
1313+
// Now the call passes
1314+
assert_ok!(result_ok);
1315+
});
1316+
}

runtime/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
229229
// `spec_version`, and `authoring_version` are the same between Wasm and native.
230230
// This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use
231231
// the compatible custom types.
232-
spec_version: 235,
232+
spec_version: 236,
233233
impl_version: 1,
234234
apis: RUNTIME_API_VERSIONS,
235235
transaction_version: 1,

0 commit comments

Comments
 (0)