referrerpolicy=no-referrer-when-downgrade

pallet_revive/evm/
fees.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! Contains the fee types that need to be configured for `pallet-transaction-payment`.
19
20use crate::{
21	evm::{
22		runtime::{EthExtra, SetWeightLimit},
23		OnChargeTransactionBalanceOf,
24	},
25	BalanceOf, CallOf, Config, DispatchErrorWithPostInfo, DispatchResultWithPostInfo, Error,
26	PostDispatchInfo, LOG_TARGET,
27};
28use codec::Encode;
29use core::marker::PhantomData;
30use frame_support::{
31	dispatch::{DispatchClass, DispatchInfo, GetDispatchInfo},
32	pallet_prelude::Weight,
33	traits::{fungible::Credit, Get, SuppressedDrop},
34	weights::WeightToFee,
35};
36use frame_system::Config as SysConfig;
37use num_traits::{One, Zero};
38use pallet_transaction_payment::{
39	Config as TxConfig, MultiplierUpdate, NextFeeMultiplier, Pallet as TxPallet, TxCreditHold,
40};
41use sp_arithmetic::{FixedPointOperand, SignedRounding};
42use sp_runtime::{
43	generic::UncheckedExtrinsic,
44	traits::{
45		Block as BlockT, Dispatchable, ExtensionPostDispatchWeightHandler, TransactionExtension,
46	},
47	FixedPointNumber, FixedU128, SaturatedConversion, Saturating,
48};
49
50type CreditOf<T> = Credit<<T as frame_system::Config>::AccountId, <T as Config>::Currency>;
51
52/// The only [`WeightToFee`] implementation that is supported by this pallet.
53///
54/// `P,Q`: Rational number that defines the ref_time to fee mapping.
55///
56/// This enforces a ratio of ref_time and proof_time that is proportional
57/// to their distribution in the block limits. We enforce the usage of this fee
58/// structure because our gas mapping depends on it.
59///
60/// # Panics
61///
62/// If either `P` or `Q` is zero.
63pub struct BlockRatioFee<const P: u128, const Q: u128, T: Config>(PhantomData<T>);
64
65/// The only [`InfoT`] implementation valid for [`Config::FeeInfo`].
66///
67/// The reason for this type is to avoid coupling the rest of pallet_revive to
68/// pallet_transaction_payment. This way we bundle all the trait bounds in once place.
69pub struct Info<Address, Signature, Extra>(PhantomData<(Address, Signature, Extra)>);
70
71/// A trait that exposes all the transaction payment details to `pallet_revive`.
72///
73/// This trait is sealed. Use [`Info`].
74pub trait InfoT<T: Config>: seal::Sealed {
75	/// Check that the fee configuration of the chain is valid.
76	///
77	/// This is being called by the pallets `integrity_check`.
78	fn integrity_test() {}
79
80	/// Exposes the current fee multiplier of the chain.
81	fn next_fee_multiplier() -> FixedU128 {
82		FixedU128::from_rational(1, 1)
83	}
84
85	/// The reciprocal of the next fee multiplier.
86	///
87	/// Needed when dividing a fee by the multiplier before presenting
88	/// it to the eth wallet as gas. Needed because the wallet will multiply
89	/// it with the gas_price which includes this multiplicator.
90	fn next_fee_multiplier_reciprocal() -> FixedU128 {
91		Self::next_fee_multiplier()
92			.reciprocal()
93			.expect("The minimum multiplier is not 0. We check that in `integrity_test`; qed")
94	}
95
96	/// Calculate the fee of a transaction including the next fee multiplier adjustment.
97	fn tx_fee(_len: u32, _call: &CallOf<T>) -> BalanceOf<T> {
98		Zero::zero()
99	}
100
101	/// Calculate the fee using the weight instead of a dispatch info.
102	fn tx_fee_from_weight(_encoded_len: u32, _weight: &Weight) -> BalanceOf<T> {
103		Zero::zero()
104	}
105
106	/// The base extrinsic and len fee.
107	fn fixed_fee(_encoded_len: u32) -> BalanceOf<T> {
108		Zero::zero()
109	}
110
111	/// Makes sure that not too much storage deposit was withdrawn.
112	fn ensure_not_overdrawn(
113		_fee: BalanceOf<T>,
114		result: DispatchResultWithPostInfo,
115	) -> DispatchResultWithPostInfo {
116		result
117	}
118
119	/// Get the dispatch info of a call with the proper extension weight set.
120	fn dispatch_info(_call: &CallOf<T>) -> DispatchInfo {
121		Default::default()
122	}
123
124	/// The dispatch info with the weight argument set to `0`.
125	fn base_dispatch_info(_call: &mut CallOf<T>) -> DispatchInfo {
126		Default::default()
127	}
128
129	/// Calculate the encoded length of a call.
130	fn encoded_len(_eth_transact_call: CallOf<T>) -> u32 {
131		0
132	}
133
134	/// Convert a weight to an unadjusted fee.
135	fn weight_to_fee(_weight: &Weight) -> BalanceOf<T> {
136		Zero::zero()
137	}
138
139	/// Convert a weight to an unadjusted fee using an average instead of maximum.
140	fn weight_to_fee_average(_weight: &Weight) -> BalanceOf<T> {
141		Zero::zero()
142	}
143
144	/// Convert an unadjusted fee back to a weight.
145	fn fee_to_weight(_fee: BalanceOf<T>) -> Weight {
146		Zero::zero()
147	}
148
149	/// Convert the length of a transaction to an unadjusted weight.
150	fn length_to_fee(_len: u32) -> BalanceOf<T> {
151		Zero::zero()
152	}
153
154	/// Add some additional fee to the `pallet_transaction_payment` credit.
155	fn deposit_txfee(_credit: CreditOf<T>) {}
156
157	/// Withdraw some fee to pay for storage deposits.
158	fn withdraw_txfee(_amount: BalanceOf<T>) -> Option<CreditOf<T>> {
159		Default::default()
160	}
161
162	/// Return the remaining transaction fee.
163	fn remaining_txfee() -> BalanceOf<T> {
164		Default::default()
165	}
166
167	/// Compute the actual post_dispatch fee
168	fn compute_actual_fee(
169		_encoded_len: u32,
170		_info: &DispatchInfo,
171		_result: &DispatchResultWithPostInfo,
172	) -> BalanceOf<T> {
173		Default::default()
174	}
175}
176
177impl<const P: u128, const Q: u128, T: Config> BlockRatioFee<P, Q, T> {
178	const REF_TIME_TO_FEE: FixedU128 = {
179		assert!(P > 0 && Q > 0);
180		FixedU128::from_rational(P, Q)
181	};
182
183	/// The proof_size to fee coefficient.
184	fn proof_size_to_fee() -> FixedU128 {
185		let max_weight = <T as frame_system::Config>::BlockWeights::get().max_block;
186		let ratio =
187			FixedU128::from_rational(max_weight.ref_time().into(), max_weight.proof_size().into());
188		Self::REF_TIME_TO_FEE.saturating_mul(ratio)
189	}
190}
191
192impl<const P: u128, const Q: u128, T: Config> WeightToFee for BlockRatioFee<P, Q, T> {
193	type Balance = BalanceOf<T>;
194
195	fn weight_to_fee(weight: &Weight) -> Self::Balance {
196		let ref_time_fee = Self::REF_TIME_TO_FEE
197			.saturating_mul_int(BalanceOf::<T>::saturated_from(weight.ref_time()));
198		let proof_size_fee = Self::proof_size_to_fee()
199			.saturating_mul_int(BalanceOf::<T>::saturated_from(weight.proof_size()));
200		ref_time_fee.max(proof_size_fee)
201	}
202}
203
204impl<const P: u128, const Q: u128, Address, Signature, E: EthExtra> InfoT<E::Config>
205	for Info<Address, Signature, E>
206where
207	E::Config: TxConfig<WeightToFee = BlockRatioFee<P, Q, E::Config>>,
208	BalanceOf<E::Config>: From<OnChargeTransactionBalanceOf<E::Config>>,
209	<E::Config as frame_system::Config>::RuntimeCall:
210		Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
211	CallOf<E::Config>: SetWeightLimit,
212	<<E::Config as SysConfig>::Block as BlockT>::Extrinsic:
213		From<UncheckedExtrinsic<Address, CallOf<E::Config>, Signature, E::Extension>>,
214	<<E::Config as TxConfig>::OnChargeTransaction as TxCreditHold<E::Config>>::Credit:
215		SuppressedDrop<Inner = CreditOf<E::Config>>,
216{
217	fn integrity_test() {
218		let min_multiplier = <E::Config as TxConfig>::FeeMultiplierUpdate::min();
219		assert!(!min_multiplier.is_zero(), "The multiplier is never allowed to be zero.");
220		assert!(
221			min_multiplier.saturating_mul_int(<E::Config as Config>::NativeToEthRatio::get()) > 0,
222			"The gas price needs to be greater zero."
223		);
224		assert!(
225			!<E::Config as TxConfig>::WeightToFee::REF_TIME_TO_FEE.is_zero(),
226			"ref_time to fee is not allowed to be zero."
227		);
228		assert!(
229			!<E::Config as TxConfig>::WeightToFee::proof_size_to_fee().is_zero(),
230			"proof_size to fee is not allowed to be zero."
231		);
232	}
233
234	fn next_fee_multiplier() -> FixedU128 {
235		<NextFeeMultiplier<E::Config>>::get()
236	}
237
238	fn tx_fee(len: u32, call: &CallOf<E::Config>) -> BalanceOf<E::Config> {
239		let dispatch_info = Self::dispatch_info(call);
240		TxPallet::<E::Config>::compute_fee(len, &dispatch_info, 0u32.into()).into()
241	}
242
243	/// Calculate the fee using the weight instead of a dispatch info.
244	fn tx_fee_from_weight(encoded_len: u32, weight: &Weight) -> BalanceOf<E::Config> {
245		let fixed_fee = Self::fixed_fee(encoded_len);
246		let weight_fee =
247			Self::next_fee_multiplier().saturating_mul_int(Self::weight_to_fee(weight));
248		fixed_fee.saturating_add(weight_fee)
249	}
250
251	fn fixed_fee(encoded_len: u32) -> BalanceOf<E::Config> {
252		Self::weight_to_fee(
253			&<E::Config as frame_system::Config>::BlockWeights::get()
254				.get(DispatchClass::Normal)
255				.base_extrinsic,
256		)
257		.saturating_add(Self::length_to_fee(encoded_len))
258	}
259
260	fn ensure_not_overdrawn(
261		fee: BalanceOf<E::Config>,
262		result: DispatchResultWithPostInfo,
263	) -> DispatchResultWithPostInfo {
264		// if tx is already failing we can ignore
265		// as it will be rolled back anyways
266		let Ok(post_info) = result else {
267			return result;
268		};
269
270		let available = Self::remaining_txfee();
271		if fee > available {
272			log::debug!(target: LOG_TARGET, "Drew too much from the txhold. \
273				fee={fee:?} \
274				available={available:?} \
275				overdrawn_by={:?}",
276				fee.saturating_sub(available),
277			);
278			Err(DispatchErrorWithPostInfo {
279				post_info,
280				error: <Error<E::Config>>::TxFeeOverdraw.into(),
281			})
282		} else {
283			log::trace!(target: LOG_TARGET, "Enough left in the txhold. \
284				fee={fee:?} \
285				available={available:?} \
286				refund={:?}",
287				available.saturating_sub(fee),
288			);
289			result
290		}
291	}
292
293	fn dispatch_info(call: &CallOf<E::Config>) -> DispatchInfo {
294		let mut dispatch_info = call.get_dispatch_info();
295		dispatch_info.extension_weight =
296			E::get_eth_extension(0u32.into(), 0u32.into()).weight(call);
297		dispatch_info
298	}
299
300	fn base_dispatch_info(call: &mut CallOf<E::Config>) -> DispatchInfo {
301		let pre_weight = call.set_weight_limit(Zero::zero());
302		let info = Self::dispatch_info(call);
303		call.set_weight_limit(pre_weight);
304		info
305	}
306
307	fn encoded_len(eth_transact_call: CallOf<E::Config>) -> u32 {
308		let uxt: <<E::Config as SysConfig>::Block as BlockT>::Extrinsic =
309			UncheckedExtrinsic::new_bare(eth_transact_call).into();
310		uxt.encoded_size() as u32
311	}
312
313	fn weight_to_fee(weight: &Weight) -> BalanceOf<E::Config> {
314		<E::Config as TxConfig>::WeightToFee::weight_to_fee(weight)
315	}
316
317	// Convert a weight to an unadjusted fee using an average instead of maximum.
318	fn weight_to_fee_average(weight: &Weight) -> BalanceOf<E::Config> {
319		let ref_time_part = <E::Config as TxConfig>::WeightToFee::REF_TIME_TO_FEE
320			.saturating_mul_int(weight.ref_time());
321		let proof_size_part = <E::Config as TxConfig>::WeightToFee::proof_size_to_fee()
322			.saturating_mul_int(weight.proof_size());
323
324		// saturated addition not required here but better to be defensive
325		((ref_time_part / 2).saturating_add(proof_size_part / 2)).saturated_into()
326	}
327
328	/// Convert an unadjusted fee back to a weight.
329	fn fee_to_weight(fee: BalanceOf<E::Config>) -> Weight {
330		let ref_time_to_fee = <E::Config as TxConfig>::WeightToFee::REF_TIME_TO_FEE;
331		let proof_size_to_fee = <E::Config as TxConfig>::WeightToFee::proof_size_to_fee();
332
333		let (ref_time, proof_size) =
334			compute_max_integer_pair_quotient((ref_time_to_fee, proof_size_to_fee), fee);
335
336		Weight::from_parts(ref_time.saturated_into(), proof_size.saturated_into())
337	}
338
339	fn length_to_fee(len: u32) -> BalanceOf<E::Config> {
340		TxPallet::<E::Config>::length_to_fee(len).into()
341	}
342
343	fn deposit_txfee(credit: CreditOf<E::Config>) {
344		TxPallet::<E::Config>::deposit_txfee(credit)
345	}
346
347	fn withdraw_txfee(amount: BalanceOf<E::Config>) -> Option<CreditOf<E::Config>> {
348		TxPallet::<E::Config>::withdraw_txfee(amount)
349	}
350
351	fn remaining_txfee() -> BalanceOf<E::Config> {
352		TxPallet::<E::Config>::remaining_txfee()
353	}
354
355	fn compute_actual_fee(
356		encoded_len: u32,
357		info: &DispatchInfo,
358		result: &DispatchResultWithPostInfo,
359	) -> BalanceOf<E::Config> {
360		let mut post_info = *match result {
361			Ok(post_info) => post_info,
362			Err(err) => &err.post_info,
363		};
364
365		post_info.set_extension_weight(info);
366		<TxPallet<E::Config>>::compute_actual_fee(encoded_len, info, &post_info, Zero::zero())
367			.into()
368	}
369}
370
371impl<T: Config> InfoT<T> for () {}
372
373mod seal {
374	pub trait Sealed {}
375	impl<Address, Signature, E: super::EthExtra> Sealed for super::Info<Address, Signature, E> {}
376	impl Sealed for () {}
377}
378
379/// Determine the maximal integer `n` so that `multiplier.saturating_mul_int(n) <= product`
380///
381/// See the tests `compute_max_quotient_works` below for an example why simple division does not
382/// give the correct result. This level of pedantry is required because otherwise we observed actual
383/// cases where limits where calculated incorrectly and the transaction ran out of gas although it
384/// used the correct gas estimate.
385///
386/// FixedU128 wraps a 128 bit unsigned integer `self.0` and it is interpreted to represent the real
387/// number self.0 / FixedU128::DIV, where FixedU128::DIV is 1_000_000_000_000_000_000.
388///
389/// Given an integer `n`, the operation `multiplier.saturating_mul_int(n)` is defined as
390///      `div_round_down(multiplier.0 * n, FixedU128::DIV)`
391/// where `div_round_down` is integer division where the result is rounded down.
392///
393/// To determine the maximal integer `n` so that `multiplier.saturating_mul_int(n) <= product` is
394/// therefore equivalent to determining the maximal `n` such that
395///      `div_round_down(multiplier.0 * n, FixedU128::DIV) <= product`
396/// This is equivalent to the condition
397///      `multiplier.0 * n <= product * FixedU128::DIV + FixedU128::DIV - 1`
398/// This is equivalent to
399///      `multiplier.0 * n < (product + 1) * FixedU128::DIV`
400/// This is equivalent to
401///      `n < div_round_up((product + 1) * FixedU128::DIV, multiplier.0)`
402/// where `div_round_up` is integer division where the result is rounded up.
403/// Since we look for a maximal `n` with this condition, the result is
404///      `n = div_round_up((product + 1) * FixedU128::DIV, multiplier.0) - 1`.
405///
406/// We can take advantage of the function `FixedU128::checked_rounding_div`, which, given two fixed
407/// point numbers `a` and `b`, just computes `a.0 * FixedU128::DIV / b.0`. It also allows to specify
408/// the rounding mode `SignedRounding::Major`, which means that the result of the division is
409/// rounded up.
410pub fn compute_max_integer_quotient<F: FixedPointOperand + One>(
411	multiplier: FixedU128,
412	product: F,
413) -> F {
414	let one = F::one();
415	let product_plus_one = FixedU128::from_inner(product.saturating_add(one).saturated_into());
416
417	product_plus_one
418		.checked_rounding_div(multiplier, SignedRounding::Major)
419		.map(|f| f.into_inner().saturated_into::<F>().saturating_sub(one))
420		.unwrap_or(F::max_value())
421}
422
423/// same as compute_max_integer_quotient but applied to a pair
424pub fn compute_max_integer_pair_quotient<F: FixedPointOperand + One>(
425	multiplier: (FixedU128, FixedU128),
426	product: F,
427) -> (F, F) {
428	let one = F::one();
429	let product_plus_one = FixedU128::from_inner(product.saturating_add(one).saturated_into());
430
431	let result1 = product_plus_one
432		.checked_rounding_div(multiplier.0, SignedRounding::Major)
433		.map(|f| f.into_inner().saturated_into::<F>().saturating_sub(one))
434		.unwrap_or(F::max_value());
435
436	let result2 = product_plus_one
437		.checked_rounding_div(multiplier.1, SignedRounding::Major)
438		.map(|f| f.into_inner().saturated_into::<F>().saturating_sub(one))
439		.unwrap_or(F::max_value());
440
441	(result1, result2)
442}
443
444#[cfg(test)]
445mod tests {
446	use super::*;
447	use proptest::proptest;
448
449	#[test]
450	fn compute_max_quotient_works() {
451		let product1 = 8625031518u64;
452		let product2 = 2597808837u64;
453
454		let multiplier = FixedU128::from_rational(4_000_000_000_000, 10 * 1024 * 1024);
455
456		assert_eq!(compute_max_integer_quotient(multiplier, product1), 22610);
457		assert_eq!(compute_max_integer_quotient(multiplier, product2), 6810);
458
459		// This shows that just dividing by the multiplier does not give the correct result, neither
460		// when rounding up, nor when rounding down
461		assert_eq!(multiplier.reciprocal().unwrap().saturating_mul_int(product1), 22610);
462		assert_eq!(multiplier.reciprocal().unwrap().saturating_mul_int(product2), 6809);
463	}
464
465	#[test]
466	fn proptest_max_quotient_works() {
467		proptest!(|(numerator: u128, denominator: u128, product: u128)| {
468			let multiplier = FixedU128::from_rational(numerator.saturating_add(1), denominator.saturating_add(1));
469			let max_quotient = compute_max_integer_quotient(multiplier, product);
470
471			assert!(multiplier.saturating_mul_int(max_quotient) <= product);
472			if max_quotient < u128::MAX {
473				assert!(multiplier.saturating_mul_int(max_quotient + 1) > product);
474			}
475		});
476	}
477
478	#[test]
479	fn proptest_max_pair_quotient_works() {
480		proptest!(|(numerator1: u128, denominator1: u128, numerator2: u128, denominator2: u128, product: u128)| {
481			let multiplier1 = FixedU128::from_rational(numerator1.saturating_add(1), denominator1.saturating_add(1));
482			let multiplier2 = FixedU128::from_rational(numerator2.saturating_add(1), denominator2.saturating_add(1));
483			let (max_quotient1, max_quotient2) = compute_max_integer_pair_quotient((multiplier1, multiplier2), product);
484
485			assert!(multiplier1.saturating_mul_int(max_quotient1) <= product);
486			if max_quotient1 < u128::MAX {
487				assert!(multiplier1.saturating_mul_int(max_quotient1 + 1) > product);
488			}
489
490			assert!(multiplier2.saturating_mul_int(max_quotient2) <= product);
491			if max_quotient2 < u128::MAX {
492				assert!(multiplier2.saturating_mul_int(max_quotient2 + 1) > product);
493			}
494		});
495	}
496}