Skip to content

Commit 32f82a5

Browse files
bkchrKiChjang
authored andcommitted
frame-executive: Reject invalid inherents in the executive (paritytech#12365)
* frame-executive: Reject invalid inherents in the executive We already had support for making a block fail if an inherent returned, but it was part of the signed extension `CheckWeight`. Rejecting blocks with invalid inherents should happen on the `frame-executive` level without requiring any special signed extension. This is crucial to prevent any kind of spamming of the network that could may happen with blocks that include failing inherents. * FMT * Update frame/executive/src/lib.rs Co-authored-by: Keith Yeung <kungfukeith11@gmail.com> * Update primitives/runtime/src/transaction_validity.rs Co-authored-by: Keith Yeung <kungfukeith11@gmail.com> Co-authored-by: parity-processbot <> Co-authored-by: Keith Yeung <kungfukeith11@gmail.com>
1 parent 7283bb9 commit 32f82a5

File tree

4 files changed

+61
-26
lines changed

4 files changed

+61
-26
lines changed

frame/executive/src/lib.rs

+49-2
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@
119119
use codec::{Codec, Encode};
120120
use frame_support::{
121121
dispatch::{DispatchClass, DispatchInfo, GetDispatchInfo, PostDispatchInfo},
122+
pallet_prelude::InvalidTransaction,
122123
traits::{
123124
EnsureInherentsAreFirst, ExecuteBlock, OffchainWorker, OnFinalize, OnIdle, OnInitialize,
124125
OnRuntimeUpgrade,
@@ -497,6 +498,14 @@ where
497498
let dispatch_info = xt.get_dispatch_info();
498499
let r = Applyable::apply::<UnsignedValidator>(xt, &dispatch_info, encoded_len)?;
499500

501+
// Mandatory(inherents) are not allowed to fail.
502+
//
503+
// The entire block should be discarded if an inherent fails to apply. Otherwise
504+
// it may open an attack vector.
505+
if r.is_err() && dispatch_info.class == DispatchClass::Mandatory {
506+
return Err(InvalidTransaction::BadMandatory.into())
507+
}
508+
500509
<frame_system::Pallet<System>>::note_applied_extrinsic(&r, dispatch_info);
501510

502511
Ok(r.map(|_| ()).map_err(|e| e.error))
@@ -563,6 +572,10 @@ where
563572
xt.get_dispatch_info()
564573
};
565574

575+
if dispatch_info.class == DispatchClass::Mandatory {
576+
return Err(InvalidTransaction::MandatoryValidation.into())
577+
}
578+
566579
within_span! {
567580
sp_tracing::Level::TRACE, "validate";
568581
xt.validate::<UnsignedValidator>(source, &dispatch_info, encoded_len)
@@ -692,9 +705,9 @@ mod tests {
692705
Ok(())
693706
}
694707

695-
#[pallet::weight(0)]
708+
#[pallet::weight((0, DispatchClass::Mandatory))]
696709
pub fn inherent_call(origin: OriginFor<T>) -> DispatchResult {
697-
let _ = frame_system::ensure_none(origin)?;
710+
frame_system::ensure_none(origin)?;
698711
Ok(())
699712
}
700713

@@ -1533,4 +1546,38 @@ mod tests {
15331546
Executive::execute_block(Block::new(header, vec![xt1, xt2]));
15341547
});
15351548
}
1549+
1550+
#[test]
1551+
#[should_panic(expected = "A call was labelled as mandatory, but resulted in an Error.")]
1552+
fn invalid_inherents_fail_block_execution() {
1553+
let xt1 =
1554+
TestXt::new(RuntimeCall::Custom(custom::Call::inherent_call {}), sign_extra(1, 0, 0));
1555+
1556+
new_test_ext(1).execute_with(|| {
1557+
Executive::execute_block(Block::new(
1558+
Header::new(
1559+
1,
1560+
H256::default(),
1561+
H256::default(),
1562+
[69u8; 32].into(),
1563+
Digest::default(),
1564+
),
1565+
vec![xt1],
1566+
));
1567+
});
1568+
}
1569+
1570+
// Inherents are created by the runtime and don't need to be validated.
1571+
#[test]
1572+
fn inherents_fail_validate_block() {
1573+
let xt1 = TestXt::new(RuntimeCall::Custom(custom::Call::inherent_call {}), None);
1574+
1575+
new_test_ext(1).execute_with(|| {
1576+
assert_eq!(
1577+
Executive::validate_transaction(TransactionSource::External, xt1, H256::random())
1578+
.unwrap_err(),
1579+
InvalidTransaction::MandatoryValidation.into()
1580+
);
1581+
})
1582+
}
15361583
}

frame/support/src/dispatch.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -359,13 +359,15 @@ where
359359

360360
/// Implementation for test extrinsic.
361361
#[cfg(feature = "std")]
362-
impl<Call: Encode, Extra: Encode> GetDispatchInfo for sp_runtime::testing::TestXt<Call, Extra> {
362+
impl<Call: Encode + GetDispatchInfo, Extra: Encode> GetDispatchInfo
363+
for sp_runtime::testing::TestXt<Call, Extra>
364+
{
363365
fn get_dispatch_info(&self) -> DispatchInfo {
364366
// for testing: weight == size.
365367
DispatchInfo {
366368
weight: Weight::from_ref_time(self.encode().len() as _),
367369
pays_fee: Pays::Yes,
368-
..Default::default()
370+
class: self.call.get_dispatch_info().class,
369371
}
370372
}
371373
}

frame/system/src/extensions/check_weight.rs

+3-17
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
use crate::{limits::BlockWeights, Config, Pallet};
1919
use codec::{Decode, Encode};
2020
use frame_support::{
21-
dispatch::{DispatchClass, DispatchInfo, PostDispatchInfo},
21+
dispatch::{DispatchInfo, PostDispatchInfo},
2222
traits::Get,
2323
};
2424
use scale_info::TypeInfo;
@@ -190,9 +190,6 @@ where
190190
info: &DispatchInfoOf<Self::Call>,
191191
len: usize,
192192
) -> Result<(), TransactionValidityError> {
193-
if info.class == DispatchClass::Mandatory {
194-
return Err(InvalidTransaction::MandatoryDispatch.into())
195-
}
196193
Self::do_pre_dispatch(info, len)
197194
}
198195

@@ -203,9 +200,6 @@ where
203200
info: &DispatchInfoOf<Self::Call>,
204201
len: usize,
205202
) -> TransactionValidity {
206-
if info.class == DispatchClass::Mandatory {
207-
return Err(InvalidTransaction::MandatoryDispatch.into())
208-
}
209203
Self::do_validate(info, len)
210204
}
211205

@@ -230,16 +224,8 @@ where
230224
info: &DispatchInfoOf<Self::Call>,
231225
post_info: &PostDispatchInfoOf<Self::Call>,
232226
_len: usize,
233-
result: &DispatchResult,
227+
_result: &DispatchResult,
234228
) -> Result<(), TransactionValidityError> {
235-
// Since mandatory dispatched do not get validated for being overweight, we are sensitive
236-
// to them actually being useful. Block producers are thus not allowed to include mandatory
237-
// extrinsics that result in error.
238-
if let (DispatchClass::Mandatory, Err(e)) = (info.class, result) {
239-
log::error!(target: "runtime::system", "Bad mandatory: {:?}", e);
240-
return Err(InvalidTransaction::BadMandatory.into())
241-
}
242-
243229
let unspent = post_info.calc_unspent(info);
244230
if unspent.any_gt(Weight::zero()) {
245231
crate::BlockWeight::<T>::mutate(|current_weight| {
@@ -268,7 +254,7 @@ mod tests {
268254
use super::*;
269255
use crate::{
270256
mock::{new_test_ext, System, Test, CALL},
271-
AllExtrinsicsLen, BlockWeight,
257+
AllExtrinsicsLen, BlockWeight, DispatchClass,
272258
};
273259
use frame_support::{assert_err, assert_ok, dispatch::Pays, weights::Weight};
274260
use sp_std::marker::PhantomData;

primitives/runtime/src/transaction_validity.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,9 @@ pub enum InvalidTransaction {
7777
/// malicious validator or a buggy `provide_inherent`. In any case, it can result in
7878
/// dangerously overweight blocks and therefore if found, invalidates the block.
7979
BadMandatory,
80-
/// A transaction with a mandatory dispatch. This is invalid; only inherent extrinsics are
81-
/// allowed to have mandatory dispatches.
82-
MandatoryDispatch,
80+
/// An extrinsic with a mandatory dispatch tried to be validated.
81+
/// This is invalid; only inherent extrinsics are allowed to have mandatory dispatches.
82+
MandatoryValidation,
8383
/// The sending address is disabled or known to be invalid.
8484
BadSigner,
8585
}
@@ -109,8 +109,8 @@ impl From<InvalidTransaction> for &'static str {
109109
"Inability to pay some fees (e.g. account balance too low)",
110110
InvalidTransaction::BadMandatory =>
111111
"A call was labelled as mandatory, but resulted in an Error.",
112-
InvalidTransaction::MandatoryDispatch =>
113-
"Transaction dispatch is mandatory; transactions may not have mandatory dispatches.",
112+
InvalidTransaction::MandatoryValidation =>
113+
"Transaction dispatch is mandatory; transactions must not be validated.",
114114
InvalidTransaction::Custom(_) => "InvalidTransaction custom error",
115115
InvalidTransaction::BadSigner => "Invalid signing address",
116116
}

0 commit comments

Comments
 (0)