Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Fix issues with Operational transactions validity and prioritization. #6435

Merged
merged 3 commits into from
Jun 21, 2020
Merged
Changes from all commits
Commits
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
71 changes: 65 additions & 6 deletions frame/system/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1394,8 +1394,10 @@ impl<T: Trait + Send + Sync> CheckWeight<T> where
info: &DispatchInfoOf<T::Call>,
) -> Result<(), TransactionValidityError> {
match info.class {
// Mandatory and Operational transactions does not
DispatchClass::Mandatory | DispatchClass::Operational => Ok(()),
// Mandatory transactions are included in a block unconditionally, so
// we don't verify weight.
DispatchClass::Mandatory => Ok(()),
// Normal transactions must not exceed `MaximumExtrinsicWeight`.
DispatchClass::Normal => {
let maximum_weight = T::MaximumExtrinsicWeight::get();
let extrinsic_weight = info.weight.saturating_add(T::ExtrinsicBaseWeight::get());
Expand All @@ -1404,7 +1406,22 @@ impl<T: Trait + Send + Sync> CheckWeight<T> where
} else {
Ok(())
}
}
},
// For operational transactions we make sure it doesn't exceed
// the space alloted for `Operational` class.
DispatchClass::Operational => {
let maximum_weight = T::MaximumBlockWeight::get();
let operational_limit =
Self::get_dispatch_limit_ratio(DispatchClass::Operational) * maximum_weight;
let operational_limit =
operational_limit.saturating_sub(T::BlockExecutionWeight::get());
let extrinsic_weight = info.weight.saturating_add(T::ExtrinsicBaseWeight::get());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This calculation is missing BaseBlockWeight if we want to be entirely accurate.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if a transaction satisfies this but can actually never be included because check_block_weight always fails ? Is that an issue like the transaction will stay forever in the queue ?
Because in the moment I guess it also doesn't include the minimum cost of OnInitialize.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that an issue like the transaction will stay forever in the queue ?

Yes, if it's immortal.

Because in the moment I guess it also doesn't include the minimum cost of OnInitialize.

Good point. With Normal dispatchables, we have a separate parameter that allows to control the max size when runtime is created. I guess it might make sense to lift it up for Operational as well and define as like 90% of max block weight or sth.

if extrinsic_weight > operational_limit {
Err(InvalidTransaction::ExhaustsResources.into())
} else {
Ok(())
}
},
}
}

Expand Down Expand Up @@ -1483,9 +1500,11 @@ impl<T: Trait + Send + Sync> CheckWeight<T> where
fn get_priority(info: &DispatchInfoOf<T::Call>) -> TransactionPriority {
match info.class {
DispatchClass::Normal => info.weight.into(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just tagging the related issue to this funny line: #5672

Probably not a problem that needs to be solved now

DispatchClass::Operational => Bounded::max_value(),
// Don't use up the whole priority space, to allow things like `tip`
// to be taken into account as well.
DispatchClass::Operational => TransactionPriority::max_value() / 2,
gui1117 marked this conversation as resolved.
Show resolved Hide resolved
// Mandatory extrinsics are only for inherents; never transactions.
DispatchClass::Mandatory => Bounded::min_value(),
DispatchClass::Mandatory => TransactionPriority::min_value(),
}
}

Expand Down Expand Up @@ -2451,6 +2470,42 @@ pub(crate) mod tests {
});
}

#[test]
fn operational_extrinsic_limited_by_operational_space_limit() {
new_test_ext().execute_with(|| {
let operational_limit = CheckWeight::<Test>::get_dispatch_limit_ratio(
DispatchClass::Operational
) * <Test as Trait>::MaximumBlockWeight::get();
let base_weight = <Test as Trait>::ExtrinsicBaseWeight::get();
let block_base = <Test as Trait>::BlockExecutionWeight::get();

let weight = operational_limit - base_weight - block_base;
let okay = DispatchInfo {
weight,
class: DispatchClass::Operational,
..Default::default()
};
let max = DispatchInfo {
weight: weight + 1,
class: DispatchClass::Operational,
..Default::default()
};
let len = 0_usize;

assert_eq!(
CheckWeight::<Test>::do_validate(&okay, len),
Ok(ValidTransaction {
priority: CheckWeight::<Test>::get_priority(&okay),
..Default::default()
})
);
assert_noop!(
CheckWeight::<Test>::do_validate(&max, len),
InvalidTransaction::ExhaustsResources
);
});
}

#[test]
fn register_extra_weight_unchecked_doesnt_care_about_limits() {
new_test_ext().execute_with(|| {
Expand Down Expand Up @@ -2478,6 +2533,8 @@ pub(crate) mod tests {
assert_ok!(CheckWeight::<Test>::do_pre_dispatch(&rest_operational, len));
assert_eq!(<Test as Trait>::MaximumBlockWeight::get(), 1024);
assert_eq!(System::block_weight().total(), <Test as Trait>::MaximumBlockWeight::get());
// Checking single extrinsic should not take current block weight into account.
assert_eq!(CheckWeight::<Test>::check_extrinsic_weight(&rest_operational), Ok(()));
});
}

Expand Down Expand Up @@ -2513,6 +2570,8 @@ pub(crate) mod tests {
assert_ok!(CheckWeight::<Test>::do_pre_dispatch(&dispatch_operational, len));
// Not too much though
assert_noop!(CheckWeight::<Test>::do_pre_dispatch(&dispatch_operational, len), InvalidTransaction::ExhaustsResources);
// Even with full block, validity of single transaction should be correct.
assert_eq!(CheckWeight::<Test>::check_extrinsic_weight(&dispatch_operational), Ok(()));
});
}

Expand Down Expand Up @@ -2558,7 +2617,7 @@ pub(crate) mod tests {
.validate(&1, CALL, &op, len)
.unwrap()
.priority;
assert_eq!(priority, u64::max_value());
assert_eq!(priority, u64::max_value() / 2);
})
}

Expand Down