Skip to content

Commit 804dfbd

Browse files
committed
Validate London txn gas costs, plus tests
- Test London validation of sender sufficient funds
1 parent ba7cf8d commit 804dfbd

File tree

4 files changed

+132
-3
lines changed

4 files changed

+132
-3
lines changed

eth/tools/factories/transaction.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,42 @@ def new_access_list_transaction(
7777
)
7878

7979
return tx.as_signed_transaction(private_key)
80+
81+
82+
@curry
83+
def new_fee_burn_transaction(
84+
vm,
85+
from_,
86+
to,
87+
private_key,
88+
amount=0,
89+
max_priority_fee_per_gas=1,
90+
max_fee_per_gas=10**10,
91+
gas=100000,
92+
data=b'',
93+
nonce=None,
94+
chain_id=1,
95+
access_list=None):
96+
"""
97+
Create and return a transaction sending amount from <from_> to <to>.
98+
99+
The transaction will be signed with the given private key.
100+
"""
101+
if nonce is None:
102+
nonce = vm.state.get_nonce(from_)
103+
if access_list is None:
104+
access_list = []
105+
106+
tx = vm.get_transaction_builder().new_unsigned_fee_burn_transaction(
107+
chain_id=chain_id,
108+
nonce=nonce,
109+
max_priority_fee_per_gas=max_priority_fee_per_gas,
110+
max_fee_per_gas=max_fee_per_gas,
111+
gas=gas,
112+
to=to,
113+
value=amount,
114+
data=data,
115+
access_list=access_list,
116+
)
117+
118+
return tx.as_signed_transaction(private_key)

eth/vm/forks/london/transactions.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ class LondonTransactionBuilder(BerlinTransactionBuilder):
224224
legacy_unsigned = LondonUnsignedLegacyTransaction
225225

226226
@classmethod
227-
def new_unsigned_base_gas_price_transaction(
227+
def new_unsigned_fee_burn_transaction(
228228
cls,
229229
chain_id: int,
230230
nonce: int,
@@ -249,7 +249,7 @@ def new_unsigned_base_gas_price_transaction(
249249
return transaction
250250

251251
@classmethod
252-
def new_base_gas_price_transaction(
252+
def new_fee_burn_transaction(
253253
cls,
254254
chain_id: int,
255255
nonce: int,

eth/vm/forks/london/validation.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def validate_london_normalized_transaction(
3838
)
3939

4040
effective_gas_price = priority_fee_per_gas + base_fee_per_gas
41-
total_transaction_cost = transaction.value + effective_gas_price
41+
total_transaction_cost = transaction.value + effective_gas_price * transaction.gas
4242

4343
if sender_balance < total_transaction_cost:
4444
raise ValidationError(
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
from eth_utils import ValidationError
2+
import pytest
3+
4+
from eth._utils.address import force_bytes_to_address
5+
from eth.chains.base import MiningChain
6+
from eth.constants import GAS_TX
7+
from eth.tools.factories.transaction import new_fee_burn_transaction
8+
from eth.vm.forks import LondonVM
9+
10+
11+
@pytest.fixture
12+
def london_plus_miner(chain_without_block_validation):
13+
if not isinstance(chain_without_block_validation, MiningChain):
14+
pytest.skip("This test is only meant to run with mining capability")
15+
return
16+
17+
valid_vms = (
18+
LondonVM,
19+
)
20+
vm = chain_without_block_validation.get_vm()
21+
if isinstance(vm, valid_vms):
22+
return chain_without_block_validation
23+
else:
24+
pytest.skip("This test is not meant to run on pre-London VMs")
25+
26+
27+
ADDRESS_A = force_bytes_to_address(b'\x10\x10')
28+
29+
30+
def test_transaction_cost_valid(london_plus_miner, funded_address, funded_address_private_key):
31+
chain = london_plus_miner
32+
vm = chain.get_vm()
33+
base_fee_per_gas = vm.get_header().base_fee_per_gas
34+
# Make sure we're testing an interesting case
35+
assert base_fee_per_gas > 0
36+
37+
account_balance = vm.state.get_balance(funded_address)
38+
39+
tx = new_fee_burn_transaction(
40+
vm,
41+
from_=funded_address,
42+
to=ADDRESS_A,
43+
private_key=funded_address_private_key,
44+
gas=GAS_TX,
45+
amount=account_balance - base_fee_per_gas * GAS_TX,
46+
max_priority_fee_per_gas=1,
47+
max_fee_per_gas=base_fee_per_gas,
48+
)
49+
50+
# sanity check
51+
assert vm.get_header().gas_used == 0
52+
53+
# There should be no validation failure when applying the transaction
54+
chain.apply_transaction(tx)
55+
56+
# sanity check: make sure the transaction actually got applied
57+
assert chain.get_vm().get_header().gas_used > 0
58+
59+
60+
def test_transaction_cost_invalid(london_plus_miner, funded_address, funded_address_private_key):
61+
chain = london_plus_miner
62+
vm = chain.get_vm()
63+
base_fee_per_gas = vm.get_header().base_fee_per_gas
64+
# Make sure we're testing an interesting case
65+
assert base_fee_per_gas > 0
66+
67+
account_balance = vm.state.get_balance(funded_address)
68+
69+
tx = new_fee_burn_transaction(
70+
vm,
71+
from_=funded_address,
72+
to=ADDRESS_A,
73+
private_key=funded_address_private_key,
74+
gas=GAS_TX,
75+
amount=account_balance - base_fee_per_gas * GAS_TX + 1,
76+
max_priority_fee_per_gas=1,
77+
max_fee_per_gas=base_fee_per_gas,
78+
)
79+
80+
# sanity check
81+
assert vm.get_header().gas_used == 0
82+
83+
# The *validation* step should catch that the sender does not have enough funds. If validation
84+
# misses the problem, then we might see an InsufficientFunds, because the VM will think the
85+
# transaction is fine, then attempt to execute it, then then run out of funds.
86+
with pytest.raises(ValidationError):
87+
chain.apply_transaction(tx)
88+
89+
# sanity check: make sure the transaction does not get applied
90+
assert chain.get_vm().get_header().gas_used == 0

0 commit comments

Comments
 (0)