Skip to content

Commit 133d8d9

Browse files
authored
Merge pull request #2264 from opentensor/feat/reject-incorrect-sudo-exts
Filter for incorrect sudo calls
2 parents 7b027b7 + 3ce8ec5 commit 133d8d9

File tree

5 files changed

+206
-1
lines changed

5 files changed

+206
-1
lines changed

node/src/benchmarking.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
use crate::client::FullClient;
66

77
use node_subtensor_runtime as runtime;
8-
use node_subtensor_runtime::pallet_subtensor;
98
use node_subtensor_runtime::{check_nonce, transaction_payment_wrapper};
9+
use node_subtensor_runtime::{pallet_subtensor, sudo_wrapper};
1010
use runtime::{BalancesCall, SystemCall};
1111
use sc_cli::Result;
1212
use sc_client_api::BlockBackend;
@@ -139,6 +139,7 @@ pub fn create_benchmark_extrinsic(
139139
transaction_payment_wrapper::ChargeTransactionPaymentWrapper::new(
140140
pallet_transaction_payment::ChargeTransactionPayment::<runtime::Runtime>::from(0),
141141
),
142+
sudo_wrapper::SudoTransactionExtension::<runtime::Runtime>::new(),
142143
pallet_subtensor::transaction_extension::SubtensorTransactionExtension::<
143144
runtime::Runtime,
144145
>::new(),
@@ -160,6 +161,7 @@ pub fn create_benchmark_extrinsic(
160161
(),
161162
(),
162163
(),
164+
(),
163165
None,
164166
),
165167
);

node/src/mev_shield/author.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,8 @@ where
391391
>::new(pallet_transaction_payment::ChargeTransactionPayment::<
392392
runtime::Runtime,
393393
>::from(0u64)),
394+
node_subtensor_runtime::sudo_wrapper::SudoTransactionExtension::<runtime::Runtime>::new(
395+
),
394396
pallet_subtensor::transaction_extension::SubtensorTransactionExtension::<
395397
runtime::Runtime,
396398
>::new(),
@@ -427,6 +429,7 @@ where
427429
(), // CheckNonce::Implicit = ()
428430
(), // CheckWeight::Implicit = ()
429431
(), // ChargeTransactionPaymentWrapper::Implicit = ()
432+
(), // SudoTransactionExtension::Implicit = ()
430433
(), // SubtensorTransactionExtension::Implicit = ()
431434
(), // DrandPriority::Implicit = ()
432435
None, // CheckMetadataHash::Implicit = Option<[u8; 32]>

runtime/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use core::num::NonZeroU64;
1212

1313
pub mod check_nonce;
1414
mod migrations;
15+
pub mod sudo_wrapper;
1516
pub mod transaction_payment_wrapper;
1617

1718
extern crate alloc;
@@ -173,6 +174,7 @@ impl frame_system::offchain::CreateSignedTransaction<pallet_drand::Call<Runtime>
173174
ChargeTransactionPaymentWrapper::new(
174175
pallet_transaction_payment::ChargeTransactionPayment::<Runtime>::from(0),
175176
),
177+
SudoTransactionExtension::<Runtime>::new(),
176178
pallet_subtensor::transaction_extension::SubtensorTransactionExtension::<Runtime>::new(
177179
),
178180
pallet_drand::drand_priority::DrandPriority::<Runtime>::new(),
@@ -1158,6 +1160,7 @@ impl pallet_subtensor_swap::Config for Runtime {
11581160
type WeightInfo = pallet_subtensor_swap::weights::DefaultWeight<Runtime>;
11591161
}
11601162

1163+
use crate::sudo_wrapper::SudoTransactionExtension;
11611164
use crate::transaction_payment_wrapper::ChargeTransactionPaymentWrapper;
11621165
use sp_runtime::BoundedVec;
11631166

@@ -1609,6 +1612,7 @@ pub type TransactionExtensions = (
16091612
check_nonce::CheckNonce<Runtime>,
16101613
frame_system::CheckWeight<Runtime>,
16111614
ChargeTransactionPaymentWrapper<Runtime>,
1615+
SudoTransactionExtension<Runtime>,
16121616
pallet_subtensor::transaction_extension::SubtensorTransactionExtension<Runtime>,
16131617
pallet_drand::drand_priority::DrandPriority<Runtime>,
16141618
frame_metadata_hash_extension::CheckMetadataHash<Runtime>,

runtime/src/sudo_wrapper.rs

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
use codec::{Decode, DecodeWithMemTracking, Encode};
2+
use frame_support::dispatch::{DispatchInfo, PostDispatchInfo};
3+
use frame_support::traits::IsSubType;
4+
use frame_system::Config;
5+
use pallet_sudo::Call as SudoCall;
6+
use scale_info::TypeInfo;
7+
use sp_runtime::impl_tx_ext_default;
8+
use sp_runtime::traits::{
9+
AsSystemOriginSigner, DispatchInfoOf, Dispatchable, Implication, TransactionExtension,
10+
ValidateResult,
11+
};
12+
use sp_runtime::transaction_validity::{InvalidTransaction, TransactionSource};
13+
use sp_std::marker::PhantomData;
14+
use subtensor_macros::freeze_struct;
15+
16+
#[freeze_struct("99dce71278b36b44")]
17+
#[derive(Default, Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, TypeInfo)]
18+
pub struct SudoTransactionExtension<T: Config + Send + Sync + TypeInfo>(pub PhantomData<T>);
19+
20+
impl<T: Config + Send + Sync + TypeInfo> sp_std::fmt::Debug for SudoTransactionExtension<T> {
21+
#[cfg(feature = "std")]
22+
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
23+
write!(f, "SudoTransactionExtension",)
24+
}
25+
#[cfg(not(feature = "std"))]
26+
fn fmt(&self, _: &mut core::fmt::Formatter) -> core::fmt::Result {
27+
Ok(())
28+
}
29+
}
30+
31+
impl<T: Config + Send + Sync + TypeInfo> SudoTransactionExtension<T> {
32+
pub fn new() -> Self {
33+
Self(Default::default())
34+
}
35+
}
36+
37+
impl<T: Config + Send + Sync + TypeInfo + pallet_sudo::Config>
38+
TransactionExtension<<T as Config>::RuntimeCall> for SudoTransactionExtension<T>
39+
where
40+
<T as Config>::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
41+
<T as Config>::RuntimeOrigin: AsSystemOriginSigner<T::AccountId> + Clone,
42+
<T as Config>::RuntimeCall: IsSubType<SudoCall<T>>,
43+
{
44+
const IDENTIFIER: &'static str = "SudoTransactionExtension";
45+
46+
type Implicit = ();
47+
type Val = ();
48+
type Pre = ();
49+
50+
impl_tx_ext_default!(<T as Config>::RuntimeCall; weight prepare);
51+
52+
fn validate(
53+
&self,
54+
origin: <T as Config>::RuntimeOrigin,
55+
call: &<T as Config>::RuntimeCall,
56+
_info: &DispatchInfoOf<<T as Config>::RuntimeCall>,
57+
_len: usize,
58+
_self_implicit: Self::Implicit,
59+
_inherited_implication: &impl Implication,
60+
_source: TransactionSource,
61+
) -> ValidateResult<Self::Val, <T as Config>::RuntimeCall> {
62+
// Ensure the transaction is signed, else we just skip the extension.
63+
let Some(who) = origin.as_system_origin_signer() else {
64+
return Ok((Default::default(), (), origin));
65+
};
66+
67+
// Check validity of the signer for sudo call
68+
if let Some(_sudo_call) = IsSubType::<pallet_sudo::Call<T>>::is_sub_type(call) {
69+
let sudo_key = pallet_sudo::pallet::Key::<T>::get();
70+
71+
// No sudo key configured → reject
72+
let Some(expected_who) = sudo_key else {
73+
return Err(InvalidTransaction::BadSigner.into());
74+
};
75+
76+
// Signer does not match the sudo key → reject
77+
if *who != expected_who {
78+
return Err(InvalidTransaction::BadSigner.into());
79+
}
80+
}
81+
82+
Ok((Default::default(), (), origin))
83+
}
84+
}

runtime/tests/sudo_wrapper.rs

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
#![allow(clippy::unwrap_used)]
2+
3+
use frame_support::assert_ok;
4+
use frame_support::dispatch::GetDispatchInfo;
5+
use node_subtensor_runtime::{
6+
BuildStorage, Runtime, RuntimeCall, RuntimeGenesisConfig, RuntimeOrigin, System, SystemCall,
7+
sudo_wrapper,
8+
};
9+
use sp_runtime::traits::{TransactionExtension, TxBaseImplication, ValidateResult};
10+
use sp_runtime::transaction_validity::{
11+
InvalidTransaction, TransactionSource, TransactionValidityError,
12+
};
13+
use subtensor_runtime_common::AccountId;
14+
15+
const SUDO_ACCOUNT: [u8; 32] = [1_u8; 32];
16+
const OTHER_ACCOUNT: [u8; 32] = [3_u8; 32];
17+
18+
fn new_test_ext() -> sp_io::TestExternalities {
19+
let mut ext: sp_io::TestExternalities = RuntimeGenesisConfig {
20+
sudo: pallet_sudo::GenesisConfig { key: None },
21+
..Default::default()
22+
}
23+
.build_storage()
24+
.unwrap()
25+
.into();
26+
ext.execute_with(|| System::set_block_number(1));
27+
ext
28+
}
29+
30+
fn call_remark() -> RuntimeCall {
31+
let remark = vec![1, 2, 3];
32+
RuntimeCall::System(SystemCall::remark { remark })
33+
}
34+
35+
fn sudo_extrinsic(inner: RuntimeCall) -> RuntimeCall {
36+
RuntimeCall::Sudo(pallet_sudo::Call::sudo {
37+
call: Box::new(inner),
38+
})
39+
}
40+
41+
fn validate_ext(origin: RuntimeOrigin, call: &RuntimeCall) -> ValidateResult<(), RuntimeCall> {
42+
let ext = sudo_wrapper::SudoTransactionExtension::<Runtime>::new();
43+
44+
ext.validate(
45+
origin,
46+
call,
47+
&call.get_dispatch_info(),
48+
0,
49+
(),
50+
&TxBaseImplication(()),
51+
TransactionSource::External,
52+
)
53+
}
54+
#[test]
55+
fn sudo_signed_by_correct_key_is_valid() {
56+
new_test_ext().execute_with(|| {
57+
let sudo_key = AccountId::from(SUDO_ACCOUNT);
58+
pallet_sudo::Key::<Runtime>::put(sudo_key.clone());
59+
let sudo_call = sudo_extrinsic(call_remark());
60+
61+
// Signed origin with correct sudo key
62+
let origin = RuntimeOrigin::signed(sudo_key);
63+
let res = validate_ext(origin, &sudo_call);
64+
assert_ok!(res);
65+
});
66+
}
67+
68+
#[test]
69+
fn sudo_signed_by_wrong_account_is_rejected() {
70+
new_test_ext().execute_with(|| {
71+
let sudo_key = AccountId::from(SUDO_ACCOUNT);
72+
// Set sudo key in storage
73+
pallet_sudo::Key::<Runtime>::put(sudo_key.clone());
74+
let sudo_call = sudo_extrinsic(call_remark());
75+
// Wrong signer
76+
let origin = RuntimeOrigin::signed(AccountId::from(OTHER_ACCOUNT));
77+
let res = validate_ext(origin, &sudo_call);
78+
assert!(matches!(
79+
res,
80+
Err(TransactionValidityError::Invalid(
81+
InvalidTransaction::BadSigner
82+
))
83+
));
84+
});
85+
}
86+
87+
#[test]
88+
fn sudo_when_no_sudo_key_configured_is_rejected() {
89+
new_test_ext().execute_with(|| {
90+
// Remove sudo key
91+
pallet_sudo::Key::<Runtime>::kill();
92+
let sudo_call = sudo_extrinsic(call_remark());
93+
let origin = RuntimeOrigin::signed(AccountId::from(SUDO_ACCOUNT));
94+
let res = validate_ext(origin, &sudo_call);
95+
assert!(matches!(
96+
res,
97+
Err(TransactionValidityError::Invalid(
98+
InvalidTransaction::BadSigner
99+
))
100+
));
101+
});
102+
}
103+
104+
#[test]
105+
fn non_sudo_extrinsic_does_not_trigger_filter() {
106+
new_test_ext().execute_with(|| {
107+
let origin = RuntimeOrigin::signed(AccountId::from(OTHER_ACCOUNT));
108+
let call = call_remark();
109+
let res = validate_ext(origin, &call);
110+
assert!(res.is_ok());
111+
});
112+
}

0 commit comments

Comments
 (0)