Skip to content

Commit d9316e3

Browse files
committed
Test and fix base fee adjustment
1. target is half of limit 2. gas_used_delta bug when parent_gas_used > parent_gas_target: was subtracting base fee instead of target
1 parent 7c53070 commit d9316e3

File tree

2 files changed

+92
-9
lines changed

2 files changed

+92
-9
lines changed

eth/vm/forks/london/headers.py

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,27 +42,30 @@ def calculate_expected_base_fee_per_gas(parent_header: BlockHeaderAPI) -> int:
4242
# Parent is a non-London header
4343
return INITIAL_BASE_FEE
4444

45-
parent_gas_target = parent_header.gas_limit
45+
# Parent *is* a London header
46+
parent_gas_target = parent_header.gas_limit // ELASTICITY_MULTIPLIER
4647
parent_gas_used = parent_header.gas_used
4748

4849
if parent_gas_used == parent_gas_target:
4950
return parent_base_fee_per_gas
5051

5152
elif parent_gas_used > parent_gas_target:
52-
gas_used_delta = parent_gas_used - parent_base_fee_per_gas
53+
gas_used_delta = parent_gas_used - parent_gas_target
54+
overburnt_wei = parent_base_fee_per_gas * gas_used_delta
5355
base_fee_per_gas_delta = max(
54-
(
55-
parent_base_fee_per_gas * gas_used_delta
56-
// parent_gas_target // BASE_FEE_MAX_CHANGE_DENOMINATOR
57-
),
58-
1
56+
overburnt_wei // parent_gas_target // BASE_FEE_MAX_CHANGE_DENOMINATOR,
57+
1,
5958
)
6059
return parent_base_fee_per_gas + base_fee_per_gas_delta
6160

6261
else:
6362
gas_used_delta = parent_gas_target - parent_gas_used
64-
base_fee_per_gas_delta = parent_base_fee_per_gas * gas_used_delta \
65-
// parent_gas_target // BASE_FEE_MAX_CHANGE_DENOMINATOR
63+
underburnt_wei = parent_base_fee_per_gas * gas_used_delta
64+
base_fee_per_gas_delta = (
65+
underburnt_wei
66+
// parent_gas_target
67+
// BASE_FEE_MAX_CHANGE_DENOMINATOR
68+
)
6669
return max(parent_base_fee_per_gas - base_fee_per_gas_delta, 0)
6770

6871

tests/core/vm/test_london.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import pytest
2+
3+
from eth import constants
4+
from eth.consensus.noproof import NoProofConsensus
5+
from eth.chains.base import MiningChain
6+
from eth.chains.mainnet import (
7+
MAINNET_VMS,
8+
)
9+
from eth.vm.forks import BerlinVM
10+
from eth.tools.factories.transaction import (
11+
new_transaction
12+
)
13+
14+
15+
# VMs starting at London
16+
@pytest.fixture(params=MAINNET_VMS[9:])
17+
def london_plus_miner(request, base_db, genesis_state):
18+
klass = MiningChain.configure(
19+
__name__='CrossoverTestChain',
20+
vm_configuration=(
21+
(
22+
constants.GENESIS_BLOCK_NUMBER,
23+
BerlinVM.configure(consensus_class=NoProofConsensus),
24+
),
25+
(
26+
constants.GENESIS_BLOCK_NUMBER + 1,
27+
request.param.configure(consensus_class=NoProofConsensus),
28+
),
29+
),
30+
chain_id=1337,
31+
)
32+
header_fields = dict(
33+
difficulty=1,
34+
gas_limit=21000 * 2, # block limit is hit with two transactions
35+
)
36+
# On the first London+ block, it will double the block limit so that the
37+
# block *target* is hit with 2 transactions
38+
return klass.from_genesis(base_db, header_fields, genesis_state)
39+
40+
41+
@pytest.mark.parametrize(
42+
'num_txns, expected_base_fee',
43+
(
44+
(0, 875000000),
45+
(1, 937500000),
46+
# base fee should stay stable at 1 gwei when block is exactly half full
47+
(2, 1000000000),
48+
(3, 1062500000),
49+
(4, 1125000000),
50+
),
51+
)
52+
def test_base_fee_evolution(
53+
london_plus_miner, funded_address, funded_address_private_key, num_txns, expected_base_fee):
54+
chain = london_plus_miner
55+
assert chain.header.gas_limit == 21000 * 4
56+
57+
vm = chain.get_vm()
58+
txns = [
59+
new_transaction(
60+
vm,
61+
funded_address,
62+
b'\x00' * 20,
63+
private_key=funded_address_private_key,
64+
gas=21000,
65+
nonce=nonce,
66+
)
67+
for nonce in range(num_txns)
68+
]
69+
block_import, _, _ = chain.mine_all(txns, gas_limit=21000 * 4)
70+
mined_header = block_import.imported_block.header
71+
assert mined_header.gas_limit == 21000 * 4
72+
assert mined_header.gas_used == 21000 * num_txns
73+
assert mined_header.base_fee_per_gas == 10 ** 9 # Initialize at 1 gwei
74+
75+
# Next block should have an unchanged gas fee because usage was equal to target (half of limit)
76+
block_import, _, _ = chain.mine_all([], gas_limit=21000 * 4)
77+
mined_header = block_import.imported_block.header
78+
assert mined_header.gas_limit == 21000 * 4
79+
assert mined_header.gas_used == 0
80+
assert mined_header.base_fee_per_gas == expected_base_fee

0 commit comments

Comments
 (0)