19
19
from ethereum_test_tools .vm .opcode import Opcodes as Op
20
20
21
21
22
- @pytest .mark .valid_from ("Osaka " )
23
- def test_bloatnet_sstore_cold (
22
+ @pytest .mark .valid_from ("Prague " )
23
+ def test_bloatnet_sstore_0_to_1 (
24
24
blockchain_test : BlockchainTestFiller , pre : Alloc , fork : Fork , gas_benchmark_value : int
25
25
):
26
26
"""
27
- Benchmark test that maximizes cold SSTORE operations (0 -> 1) by filling
27
+ Benchmark test that maximizes SSTORE operations (0 -> 1) by filling
28
28
a block with multiple transactions, with each one containing a contract
29
- that performs a set of cold SSTOREs.
29
+ that performs a set of SSTOREs.
30
30
31
31
The test iteratively creates new transactions until the cumulative gas used
32
32
reaches the block's gas benchmark value. Each transaction deploys a contract
@@ -58,39 +58,25 @@ def test_bloatnet_sstore_cold(
58
58
59
59
opcode_gas_budget = tx_gas_limit - intrinsic_gas
60
60
61
+ # Setup code to load value from calldata
61
62
tx_contract_code = Op .PUSH0 + Op .CALLDATALOAD
62
- tx_opcode_gas = (
63
- gas_costs .G_BASE # PUSH0
64
- + gas_costs .G_VERY_LOW # CALLDATALOAD
65
- )
66
- tx_sstores_count = 0
67
-
68
- current_slot = total_sstores
69
-
70
- pop_gas = gas_costs .G_BASE
71
-
72
- while True :
73
- sstore_per_op_cost = (
74
- gas_costs .G_VERY_LOW * 2 # PUSH + DUP1
75
- + gas_costs .G_COLD_SLOAD
76
- + gas_costs .G_STORAGE_SET # SSTORE
77
- )
63
+ tx_opcode_gas = gas_costs .G_BASE + gas_costs .G_VERY_LOW # PUSH0 + CALLDATALOAD
78
64
79
- if tx_opcode_gas + sstore_per_op_cost + pop_gas > opcode_gas_budget :
80
- break
65
+ sstore_per_op_cost = (
66
+ gas_costs .G_VERY_LOW * 2 # PUSH + DUP1
67
+ + gas_costs .G_COLD_SLOAD
68
+ + gas_costs .G_STORAGE_SET # SSTORE
69
+ )
81
70
82
- tx_opcode_gas += sstore_per_op_cost
83
- tx_contract_code += Op .SSTORE (current_slot , Op .DUP1 )
84
- tx_sstores_count += 1
85
- current_slot += 1
71
+ tx_sstores_count = (opcode_gas_budget - tx_opcode_gas ) // sstore_per_op_cost
86
72
87
73
# If no SSTOREs could be added, we've filled the block
88
74
if tx_sstores_count == 0 :
89
75
break
90
76
91
- # Add a POP to clean up the stack at the end
92
- tx_contract_code += Op . POP
93
- tx_opcode_gas += pop_gas
77
+ tx_opcode_gas += sstore_per_op_cost * tx_sstores_count
78
+ for slot in range ( total_sstores , total_sstores + tx_sstores_count ):
79
+ tx_contract_code += Op . SSTORE ( slot , Op . DUP1 )
94
80
95
81
contract_address = pre .deploy_contract (code = tx_contract_code )
96
82
tx = Transaction (
@@ -111,18 +97,18 @@ def test_bloatnet_sstore_cold(
111
97
tx_gas_used = actual_intrinsic_consumed + tx_opcode_gas
112
98
total_block_gas_used += tx_gas_used
113
99
114
- total_sstores += tx_sstores_count
115
-
116
100
# update expected storage state for each contract
117
101
expected_storage_state [contract_address ] = Account (
118
102
storage = Storage (
119
103
{
120
104
HashInt (slot ): HashInt (storage_value )
121
- for slot in range (current_slot - tx_sstores_count , current_slot )
105
+ for slot in range (total_sstores , total_sstores + tx_sstores_count )
122
106
}
123
107
)
124
108
)
125
109
110
+ total_sstores += tx_sstores_count
111
+
126
112
blockchain_test (
127
113
pre = pre ,
128
114
blocks = [Block (txs = all_txs )],
@@ -131,16 +117,15 @@ def test_bloatnet_sstore_cold(
131
117
)
132
118
133
119
134
- @pytest .mark .valid_from ("Osaka " )
135
- def test_bloatnet_sstore_warm (
120
+ @pytest .mark .valid_from ("Prague " )
121
+ def test_bloatnet_sstore_1_to_2 (
136
122
blockchain_test : BlockchainTestFiller , pre : Alloc , fork : Fork , gas_benchmark_value : int
137
123
):
138
124
"""
139
- Benchmark test that maximizes warm SSTORE operations (1 -> 2).
125
+ Benchmark test that maximizes SSTORE operations (1 -> 2).
140
126
141
127
This test pre-fills storage slots with value=1, then overwrites them with value=2.
142
128
This represents the case of changing a non-zero value to a different non-zero value,
143
- which is cheaper than cold SSTORE but still significant.
144
129
"""
145
130
gas_costs = fork .gas_costs ()
146
131
intrinsic_gas_calc = fork .transaction_intrinsic_cost_calculator ()
@@ -165,36 +150,24 @@ def test_bloatnet_sstore_warm(
165
150
166
151
opcode_gas_budget = tx_gas_limit - intrinsic_gas
167
152
153
+ # Setup code to load value from calldata
168
154
tx_contract_code = Op .PUSH0 + Op .CALLDATALOAD
169
- tx_opcode_gas = (
170
- gas_costs .G_BASE # PUSH0
171
- + gas_costs .G_VERY_LOW # CALLDATALOAD
172
- )
173
- tx_sstores_count = 0
174
-
175
- current_slot = total_sstores
176
- pop_gas = gas_costs .G_BASE
177
-
178
- warm_sstore_cost = gas_costs .G_COLD_SLOAD + gas_costs .G_STORAGE_RESET
179
- while True :
180
- sstore_per_op_cost = (
181
- gas_costs .G_VERY_LOW * 2 # PUSH + DUP1
182
- + warm_sstore_cost # SSTORE
183
- )
155
+ tx_opcode_gas = gas_costs .G_BASE + gas_costs .G_VERY_LOW # PUSH0 + CALLDATALOAD
184
156
185
- if tx_opcode_gas + sstore_per_op_cost + pop_gas > opcode_gas_budget :
186
- break
157
+ sstore_per_op_cost = (
158
+ gas_costs .G_VERY_LOW * 2 # PUSH + DUP1
159
+ + gas_costs .G_COLD_SLOAD
160
+ + gas_costs .G_STORAGE_RESET # SSTORE
161
+ )
187
162
188
- tx_opcode_gas += sstore_per_op_cost
189
- tx_contract_code += Op .SSTORE (current_slot , Op .DUP1 )
190
- tx_sstores_count += 1
191
- current_slot += 1
163
+ tx_sstores_count = (opcode_gas_budget - tx_opcode_gas ) // sstore_per_op_cost
192
164
193
165
if tx_sstores_count == 0 :
194
166
break
195
167
196
- tx_contract_code += Op .POP
197
- tx_opcode_gas += pop_gas
168
+ tx_opcode_gas += sstore_per_op_cost * tx_sstores_count
169
+ for slot in range (total_sstores , total_sstores + tx_sstores_count ):
170
+ tx_contract_code += Op .SSTORE (slot , Op .DUP1 )
198
171
199
172
# Pre-fill storage with initial values
200
173
initial_storage = {
@@ -219,17 +192,18 @@ def test_bloatnet_sstore_warm(
219
192
220
193
tx_gas_used = actual_intrinsic_consumed + tx_opcode_gas
221
194
total_block_gas_used += tx_gas_used
222
- total_sstores += tx_sstores_count
223
195
224
196
expected_storage_state [contract_address ] = Account (
225
197
storage = Storage (
226
198
{
227
199
HashInt (slot ): HashInt (new_value )
228
- for slot in range (current_slot - tx_sstores_count , current_slot )
200
+ for slot in range (total_sstores , total_sstores + tx_sstores_count )
229
201
}
230
202
)
231
203
)
232
204
205
+ total_sstores += tx_sstores_count
206
+
233
207
blockchain_test (
234
208
pre = pre ,
235
209
blocks = [Block (txs = all_txs )],
0 commit comments