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	BalanceOf, CallOf, Config, DispatchErrorWithPostInfo, DispatchResultWithPostInfo, Error,
22	LOG_TARGET, PostDispatchInfo,
23	evm::{
24		OnChargeTransactionBalanceOf,
25		runtime::{EthExtra, SetWeightLimit},
26	},
27};
28use codec::Encode;
29use core::marker::PhantomData;
30use frame_support::{
31	dispatch::{DispatchClass, DispatchInfo, GetDispatchInfo},
32	pallet_prelude::Weight,
33	traits::{Get, SuppressedDrop, fungible::Credit, tokens::Balance},
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	FixedPointNumber, FixedU128, SaturatedConversion, Saturating,
44	generic::UncheckedExtrinsic,
45	traits::{
46		Block as BlockT, Dispatchable, ExtensionPostDispatchWeightHandler, TransactionExtension,
47	},
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, B>(PhantomData<(T, B)>);
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: SysConfig, B> BlockRatioFee<P, Q, T, B> {
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 SysConfig>::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, B> WeightToFee for BlockRatioFee<P, Q, T, B>
193where
194	T: SysConfig,
195	B: Balance,
196{
197	type Balance = B;
198
199	fn weight_to_fee(weight: &Weight) -> Self::Balance {
200		let ref_time_fee = Self::REF_TIME_TO_FEE
201			.saturating_mul_int(Self::Balance::saturated_from(weight.ref_time()));
202		let proof_size_fee = Self::proof_size_to_fee()
203			.saturating_mul_int(Self::Balance::saturated_from(weight.proof_size()));
204		ref_time_fee.max(proof_size_fee)
205	}
206}
207
208impl<const P: u128, const Q: u128, Address, Signature, E: EthExtra> InfoT<E::Config>
209	for Info<Address, Signature, E>
210where
211	E::Config: TxConfig<WeightToFee = BlockRatioFee<P, Q, E::Config, BalanceOf<E::Config>>>,
212	BalanceOf<E::Config>: From<OnChargeTransactionBalanceOf<E::Config>>,
213	<E::Config as frame_system::Config>::RuntimeCall:
214		Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
215	CallOf<E::Config>: SetWeightLimit,
216	<<E::Config as SysConfig>::Block as BlockT>::Extrinsic: From<
217		UncheckedExtrinsic<
218			Address,
219			CallOf<E::Config>,
220			Signature,
221			E::ExtensionV0,
222			E::ExtensionOtherVersions,
223		>,
224	>,
225	<<E::Config as TxConfig>::OnChargeTransaction as TxCreditHold<E::Config>>::Credit:
226		SuppressedDrop<Inner = CreditOf<E::Config>>,
227{
228	fn integrity_test() {
229		let min_multiplier = <E::Config as TxConfig>::FeeMultiplierUpdate::min();
230		assert!(!min_multiplier.is_zero(), "The multiplier is never allowed to be zero.");
231		assert!(
232			min_multiplier.saturating_mul_int(<E::Config as Config>::NativeToEthRatio::get()) > 0,
233			"The gas price needs to be greater zero."
234		);
235		assert!(
236			!<E::Config as TxConfig>::WeightToFee::REF_TIME_TO_FEE.is_zero(),
237			"ref_time to fee is not allowed to be zero."
238		);
239		assert!(
240			!<E::Config as TxConfig>::WeightToFee::proof_size_to_fee().is_zero(),
241			"proof_size to fee is not allowed to be zero."
242		);
243	}
244
245	fn next_fee_multiplier() -> FixedU128 {
246		<NextFeeMultiplier<E::Config>>::get()
247	}
248
249	fn tx_fee(len: u32, call: &CallOf<E::Config>) -> BalanceOf<E::Config> {
250		let dispatch_info = Self::dispatch_info(call);
251		TxPallet::<E::Config>::compute_fee(len, &dispatch_info, 0u32.into()).into()
252	}
253
254	/// Calculate the fee using the weight instead of a dispatch info.
255	fn tx_fee_from_weight(encoded_len: u32, weight: &Weight) -> BalanceOf<E::Config> {
256		let fixed_fee = Self::fixed_fee(encoded_len);
257		let weight_fee =
258			Self::next_fee_multiplier().saturating_mul_int(Self::weight_to_fee(weight));
259		fixed_fee.saturating_add(weight_fee)
260	}
261
262	fn fixed_fee(encoded_len: u32) -> BalanceOf<E::Config> {
263		Self::weight_to_fee(
264			&<E::Config as frame_system::Config>::BlockWeights::get()
265				.get(DispatchClass::Normal)
266				.base_extrinsic,
267		)
268		.saturating_add(Self::length_to_fee(encoded_len))
269	}
270
271	fn ensure_not_overdrawn(
272		fee: BalanceOf<E::Config>,
273		result: DispatchResultWithPostInfo,
274	) -> DispatchResultWithPostInfo {
275		// if tx is already failing we can ignore
276		// as it will be rolled back anyways
277		let Ok(post_info) = result else {
278			return result;
279		};
280
281		let available = Self::remaining_txfee();
282		if fee > available {
283			log::debug!(target: LOG_TARGET, "Drew too much from the txhold. \
284				fee={fee:?} \
285				available={available:?} \
286				overdrawn_by={:?}",
287				fee.saturating_sub(available),
288			);
289			Err(DispatchErrorWithPostInfo {
290				post_info,
291				error: <Error<E::Config>>::TxFeeOverdraw.into(),
292			})
293		} else {
294			log::trace!(target: LOG_TARGET, "Enough left in the txhold. \
295				fee={fee:?} \
296				available={available:?} \
297				refund={:?}",
298				available.saturating_sub(fee),
299			);
300			result
301		}
302	}
303
304	fn dispatch_info(call: &CallOf<E::Config>) -> DispatchInfo {
305		let mut dispatch_info = call.get_dispatch_info();
306		dispatch_info.extension_weight =
307			E::get_eth_extension(0u32.into(), 0u32.into()).weight(call);
308		dispatch_info
309	}
310
311	fn base_dispatch_info(call: &mut CallOf<E::Config>) -> DispatchInfo {
312		let pre_weight = call.set_weight_limit(Zero::zero());
313		let info = Self::dispatch_info(call);
314		call.set_weight_limit(pre_weight);
315		info
316	}
317
318	fn encoded_len(eth_transact_call: CallOf<E::Config>) -> u32 {
319		let uxt: <<E::Config as SysConfig>::Block as BlockT>::Extrinsic =
320			UncheckedExtrinsic::new_bare(eth_transact_call).into();
321		uxt.encoded_size() as u32
322	}
323
324	fn weight_to_fee(weight: &Weight) -> BalanceOf<E::Config> {
325		<E::Config as TxConfig>::WeightToFee::weight_to_fee(weight)
326	}
327
328	// Convert a weight to an unadjusted fee using an average instead of maximum.
329	fn weight_to_fee_average(weight: &Weight) -> BalanceOf<E::Config> {
330		let ref_time_part = <E::Config as TxConfig>::WeightToFee::REF_TIME_TO_FEE
331			.saturating_mul_int(weight.ref_time());
332		let proof_size_part = <E::Config as TxConfig>::WeightToFee::proof_size_to_fee()
333			.saturating_mul_int(weight.proof_size());
334
335		// saturated addition not required here but better to be defensive
336		((ref_time_part / 2).saturating_add(proof_size_part / 2)).saturated_into()
337	}
338
339	/// Convert an unadjusted fee back to a weight.
340	fn fee_to_weight(fee: BalanceOf<E::Config>) -> Weight {
341		let ref_time_to_fee = <E::Config as TxConfig>::WeightToFee::REF_TIME_TO_FEE;
342		let proof_size_to_fee = <E::Config as TxConfig>::WeightToFee::proof_size_to_fee();
343
344		let (ref_time, proof_size) =
345			compute_max_integer_pair_quotient((ref_time_to_fee, proof_size_to_fee), fee);
346
347		Weight::from_parts(ref_time.saturated_into(), proof_size.saturated_into())
348	}
349
350	fn length_to_fee(len: u32) -> BalanceOf<E::Config> {
351		TxPallet::<E::Config>::length_to_fee(len).into()
352	}
353
354	fn deposit_txfee(credit: CreditOf<E::Config>) {
355		TxPallet::<E::Config>::deposit_txfee(credit)
356	}
357
358	fn withdraw_txfee(amount: BalanceOf<E::Config>) -> Option<CreditOf<E::Config>> {
359		TxPallet::<E::Config>::withdraw_txfee(amount)
360	}
361
362	fn remaining_txfee() -> BalanceOf<E::Config> {
363		TxPallet::<E::Config>::remaining_txfee()
364	}
365
366	fn compute_actual_fee(
367		encoded_len: u32,
368		info: &DispatchInfo,
369		result: &DispatchResultWithPostInfo,
370	) -> BalanceOf<E::Config> {
371		let mut post_info = *match result {
372			Ok(post_info) => post_info,
373			Err(err) => &err.post_info,
374		};
375
376		post_info.set_extension_weight(info);
377		<TxPallet<E::Config>>::compute_actual_fee(encoded_len, info, &post_info, Zero::zero())
378			.into()
379	}
380}
381
382impl<T: Config> InfoT<T> for () {}
383
384mod seal {
385	pub trait Sealed {}
386	impl<Address, Signature, E: super::EthExtra> Sealed for super::Info<Address, Signature, E> {}
387	impl Sealed for () {}
388}
389
390/// Determine the maximal integer `n` so that `multiplier.saturating_mul_int(n) <= product`
391///
392/// See the tests `compute_max_quotient_works` below for an example why simple division does not
393/// give the correct result. This level of pedantry is required because otherwise we observed actual
394/// cases where limits where calculated incorrectly and the transaction ran out of gas although it
395/// used the correct gas estimate.
396///
397/// FixedU128 wraps a 128 bit unsigned integer `self.0` and it is interpreted to represent the real
398/// number self.0 / FixedU128::DIV, where FixedU128::DIV is 1_000_000_000_000_000_000.
399///
400/// Given an integer `n`, the operation `multiplier.saturating_mul_int(n)` is defined as
401///      `div_round_down(multiplier.0 * n, FixedU128::DIV)`
402/// where `div_round_down` is integer division where the result is rounded down.
403///
404/// To determine the maximal integer `n` so that `multiplier.saturating_mul_int(n) <= product` is
405/// therefore equivalent to determining the maximal `n` such that
406///      `div_round_down(multiplier.0 * n, FixedU128::DIV) <= product`
407/// This is equivalent to the condition
408///      `multiplier.0 * n <= product * FixedU128::DIV + FixedU128::DIV - 1`
409/// This is equivalent to
410///      `multiplier.0 * n < (product + 1) * FixedU128::DIV`
411/// This is equivalent to
412///      `n < div_round_up((product + 1) * FixedU128::DIV, multiplier.0)`
413/// where `div_round_up` is integer division where the result is rounded up.
414/// Since we look for a maximal `n` with this condition, the result is
415///      `n = div_round_up((product + 1) * FixedU128::DIV, multiplier.0) - 1`.
416///
417/// We can take advantage of the function `FixedU128::checked_rounding_div`, which, given two fixed
418/// point numbers `a` and `b`, just computes `a.0 * FixedU128::DIV / b.0`. It also allows to specify
419/// the rounding mode `SignedRounding::Major`, which means that the result of the division is
420/// rounded up.
421pub fn compute_max_integer_quotient<F: FixedPointOperand + One>(
422	multiplier: FixedU128,
423	product: F,
424) -> F {
425	let one = F::one();
426	let product_plus_one = FixedU128::from_inner(product.saturating_add(one).saturated_into());
427
428	product_plus_one
429		.checked_rounding_div(multiplier, SignedRounding::Major)
430		.map(|f| f.into_inner().saturated_into::<F>().saturating_sub(one))
431		.unwrap_or(F::max_value())
432}
433
434/// same as compute_max_integer_quotient but applied to a pair
435pub fn compute_max_integer_pair_quotient<F: FixedPointOperand + One>(
436	multiplier: (FixedU128, FixedU128),
437	product: F,
438) -> (F, F) {
439	let one = F::one();
440	let product_plus_one = FixedU128::from_inner(product.saturating_add(one).saturated_into());
441
442	let result1 = product_plus_one
443		.checked_rounding_div(multiplier.0, SignedRounding::Major)
444		.map(|f| f.into_inner().saturated_into::<F>().saturating_sub(one))
445		.unwrap_or(F::max_value());
446
447	let result2 = product_plus_one
448		.checked_rounding_div(multiplier.1, SignedRounding::Major)
449		.map(|f| f.into_inner().saturated_into::<F>().saturating_sub(one))
450		.unwrap_or(F::max_value());
451
452	(result1, result2)
453}
454
455#[cfg(test)]
456mod tests {
457	use super::*;
458	use proptest::proptest;
459
460	#[test]
461	fn compute_max_quotient_works() {
462		let product1 = 8625031518u64;
463		let product2 = 2597808837u64;
464
465		let multiplier = FixedU128::from_rational(4_000_000_000_000, 10 * 1024 * 1024);
466
467		assert_eq!(compute_max_integer_quotient(multiplier, product1), 22610);
468		assert_eq!(compute_max_integer_quotient(multiplier, product2), 6810);
469
470		// This shows that just dividing by the multiplier does not give the correct result, neither
471		// when rounding up, nor when rounding down
472		assert_eq!(multiplier.reciprocal().unwrap().saturating_mul_int(product1), 22610);
473		assert_eq!(multiplier.reciprocal().unwrap().saturating_mul_int(product2), 6809);
474	}
475
476	#[test]
477	fn proptest_max_quotient_works() {
478		proptest!(|(numerator: u128, denominator: u128, product: u128)| {
479			let multiplier = FixedU128::from_rational(numerator.saturating_add(1), denominator.saturating_add(1));
480			let max_quotient = compute_max_integer_quotient(multiplier, product);
481
482			assert!(multiplier.saturating_mul_int(max_quotient) <= product);
483			if max_quotient < u128::MAX {
484				assert!(multiplier.saturating_mul_int(max_quotient + 1) > product);
485			}
486		});
487	}
488
489	#[test]
490	fn proptest_max_pair_quotient_works() {
491		proptest!(|(numerator1: u128, denominator1: u128, numerator2: u128, denominator2: u128, product: u128)| {
492			let multiplier1 = FixedU128::from_rational(numerator1.saturating_add(1), denominator1.saturating_add(1));
493			let multiplier2 = FixedU128::from_rational(numerator2.saturating_add(1), denominator2.saturating_add(1));
494			let (max_quotient1, max_quotient2) = compute_max_integer_pair_quotient((multiplier1, multiplier2), product);
495
496			assert!(multiplier1.saturating_mul_int(max_quotient1) <= product);
497			if max_quotient1 < u128::MAX {
498				assert!(multiplier1.saturating_mul_int(max_quotient1 + 1) > product);
499			}
500
501			assert!(multiplier2.saturating_mul_int(max_quotient2) <= product);
502			if max_quotient2 < u128::MAX {
503				assert!(multiplier2.saturating_mul_int(max_quotient2 + 1) > product);
504			}
505		});
506	}
507}