Skip to content

Commit ea81e0b

Browse files
Tighten length estimation during dry running (#10540)
The length of the RLP-encoded Ethereum transaction will have an effect on the transaction cost (as pallet-transaction-payment charges a length fee) and therefore the required Ethereum gas. During dry running we need to estimate the length of the actual RLP-encoded Ethereum transaction that will submitted later. Some of the parameters that determine the length will usually not be provided at the dry running stage yet: `gas`, `gas_price` and `max_priority_fee_per_gas`. If we underestimate the actual lengths of these parameters, then the gas estimate might be too low and transaction execution will run out of gas. If we over estimate, then the pre-dispatch weight will be unreasonably large and we risk that a transaction that might still fit into a block, won't be put into the block anymore, which leads to lower block utilization. ## Current Approach The current approach is to just assume that maximal possible length for these fields, which results when they have the maximum possible value, `U256::MAX`, due to how RLP encoding works. This is a gross over estimation. ## New Approach In practice there won't be gas requirements and gas estimates that are more than `u64::MAX` and therefore we assume this as the maximal value for `gas`. For `gas_price` and `max_priority_fee_per_gas` we assume that the caller will use the current base fee and will scale it be some small amount so that the RLP encoding is at most one byte longer than the RLP encoding of the base fee. We achieve that by determining the RLP encoding of the base fee multiplied by 256. --------- Co-authored-by: cmd[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent 1547c30 commit ea81e0b

File tree

2 files changed

+24
-3
lines changed

2 files changed

+24
-3
lines changed

prdoc/pr_10540.prdoc

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
title: Tighten length estimation during dry running
2+
doc:
3+
- audience: Runtime Dev
4+
description: |-
5+
The length of the RLP-encoded Ethereum transaction will have an effect on the transaction cost (as pallet-transaction-payment charges a length fee) and therefore the required Ethereum gas.
6+
7+
During dry running we need to estimate the length of the actual RLP-encoded Ethereum transaction that will submitted later. Some of the parameters that determine the length will usually not be provided at the dry running stage yet: `gas`, `gas_price` and `max_priority_fee_per_gas`.
8+
9+
If we underestimate the actual lengths of these parameters, then the gas estimate might be too low and transaction execution will run out of gas. If we over estimate, then the pre-dispatch weight will be unreasonably large and we risk that a transaction that might still fit into a block, won't be put into the block anymore, which leads to lower block utilization.
10+
11+
## Current Approach
12+
The current approach is to just assume that maximal possible length for these fields, which results when they have the maximum possible value, `U256::MAX`, due to how RLP encoding works. This is a gross over estimation.
13+
14+
## New Approach
15+
In practice there won't be gas requirements and gas estimates that are more than `u64::MAX` and therefore we assume this as the maximal value for `gas`.
16+
17+
For `gas_price` and `max_priority_fee_per_gas` we assume that the caller will use the current base fee and will scale it be some small amount so that the RLP encoding is at most one byte longer than the RLP encoding of the base fee. We achieve that by determining the RLP encoding of the base fee multiplied by 256.
18+
crates:
19+
- name: pallet-revive
20+
bump: patch

substrate/frame/revive/src/evm/call.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,10 @@ impl GenericTransaction {
104104
// For dry runs, we need to ensure that the RLP encoding length is at least the
105105
// length of the encoding of the actual transaction submitted later
106106
let mut maximized_tx = self.clone();
107-
maximized_tx.gas = Some(U256::MAX);
108-
maximized_tx.gas_price = Some(U256::MAX);
109-
maximized_tx.max_priority_fee_per_gas = Some(U256::MAX);
107+
let maximized_base_fee = base_fee.saturating_mul(256.into());
108+
maximized_tx.gas = Some(u64::MAX.into());
109+
maximized_tx.gas_price = Some(maximized_base_fee);
110+
maximized_tx.max_priority_fee_per_gas = Some(maximized_base_fee);
110111

111112
let unsigned_tx = maximized_tx.try_into_unsigned().map_err(|_| {
112113
log::debug!(target: LOG_TARGET, "Invalid transaction type.");

0 commit comments

Comments
 (0)