29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121 | @pytest.mark.valid_from("Shanghai")
@pytest.mark.parametrize(
"use_sufficient_gas",
[True, False],
ids=["sufficient_gas", "insufficient_gas"],
)
@pytest.mark.parametrize(
"opcode,call_opcode",
[
("call", Op.CALL),
("callcode", Op.CALLCODE),
("delegatecall", Op.DELEGATECALL),
("staticcall", Op.STATICCALL),
],
ids=["CALL", "CALLCODE", "DELEGATECALL", "STATICCALL"],
)
def test_warm_coinbase_call_out_of_gas(
state_test: StateTestFiller,
env: Environment,
pre: Alloc,
post: Alloc,
sender: Address,
fork: Fork,
opcode: str,
call_opcode: Op,
use_sufficient_gas: bool,
) -> None:
"""
Test that the coinbase is warm by accessing the COINBASE with each
of the following opcodes.
- CALL
- CALLCODE
- DELEGATECALL
- STATICCALL
"""
# Build contract code: POP(xCALL(0, COINBASE, 0, ...))
if call_opcode in (Op.CALL, Op.CALLCODE):
contract_under_test_code = Op.POP(
call_opcode(0, Op.COINBASE, 0, 0, 0, 0, 0)
)
else:
contract_under_test_code = Op.POP(
call_opcode(0, Op.COINBASE, 0, 0, 0, 0)
)
contract_under_test_address = pre.deploy_contract(contract_under_test_code)
# Compute exact gas: warm call cost + overhead
# (COINBASE, PUSHes, DUPs, POP)
warm_call_cost = call_opcode(address_warm=True).gas_cost(fork)
# Overhead = total cost (with cold default) minus the cold call cost
cold_call_cost = call_opcode(address_warm=False).gas_cost(fork)
total_with_cold = contract_under_test_code.gas_cost(fork)
call_gas_exact = warm_call_cost + (total_with_cold - cold_call_cost)
if not use_sufficient_gas:
call_gas_exact -= 1
caller_code = Op.SSTORE(
0,
Op.CALL(call_gas_exact, contract_under_test_address, 0, 0, 0, 0, 0),
)
caller_address = pre.deploy_contract(caller_code)
tx = Transaction(
to=caller_address,
sender=sender,
)
if use_sufficient_gas and fork >= Shanghai:
post[caller_address] = Account(
storage={
# On shanghai and beyond, calls with only 100 gas to
# coinbase will succeed.
0: 1,
}
)
else:
post[caller_address] = Account(
storage={
# Before shanghai, calls with only 100 gas to
# coinbase will fail.
0: 0,
}
)
state_test(
env=env,
pre=pre,
post=post,
tx=tx,
)
|