Skip to content

Commit e0ae1ee

Browse files
committed
refactor(benchmark): optimize SLOAD/SSTORE benchmarks per review feedback
Address review comments by optimizing loop efficiency: 1. Move function selector MSTORE outside loops (Comment #2) - BALANCEOF_SELECTOR and APPROVE_SELECTOR now stored once per contract - Saves 3 gas (G_VERY_LOW) per iteration - Total savings: ~6,471 gas for 50-50 ratio with 10M budget and 3 contracts 2. Remove unused return data from CALL operations (Comment #1) - Changed ret_offset=96/128, ret_size=32 to ret_offset=0, ret_size=0 - Eliminates unnecessary memory expansion - Minor gas savings, cleaner implementation Skipped Comment #3 (use Op.GAS for addresses): - Would lose determinism (GAS varies per iteration) - Adds complexity for minimal benefit - Counter still needed for loop control Changes applied to: - test_sload_empty_erc20_balanceof - test_sstore_erc20_approve - test_mixed_sload_sstore (both SLOAD and SSTORE loops)
1 parent 665a8c0 commit e0ae1ee

File tree

2 files changed

+29
-31
lines changed

2 files changed

+29
-31
lines changed

tests/benchmark/stateful/bloatnet/test_multi_opcode.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -454,26 +454,26 @@ def test_mixed_sload_sstore(
454454
for erc20_address in erc20_addresses:
455455
# For each contract, execute SLOAD operations (balanceOf)
456456
attack_code += (
457+
# Store function selector at memory[32] once (outside loop)
458+
Op.MSTORE(offset=32, value=BALANCEOF_SELECTOR)
457459
# Initialize counter in memory[0] = number of balanceOf calls
458-
Op.MSTORE(offset=0, value=sload_calls_per_contract)
460+
+ Op.MSTORE(offset=0, value=sload_calls_per_contract)
459461
# Loop for balanceOf calls
460462
+ While(
461463
condition=Op.MLOAD(0) + Op.ISZERO + Op.ISZERO,
462464
body=(
463-
# Store function selector at memory[32]
464-
Op.MSTORE(offset=32, value=BALANCEOF_SELECTOR)
465465
# Store address at memory[64] (use counter as address)
466-
+ Op.MSTORE(offset=64, value=Op.MLOAD(0))
466+
Op.MSTORE(offset=64, value=Op.MLOAD(0))
467467
# Call balanceOf(address) on ERC20 contract
468468
+ Op.CALL(
469469
address=erc20_address,
470470
value=0,
471471
args_offset=32,
472472
args_size=36,
473-
ret_offset=96,
474-
ret_size=32,
473+
ret_offset=0,
474+
ret_size=0,
475475
)
476-
+ Op.POP # Discard result
476+
+ Op.POP # Discard CALL success status
477477
# Decrement counter
478478
+ Op.MSTORE(offset=0, value=Op.SUB(Op.MLOAD(0), 1))
479479
),
@@ -482,16 +482,16 @@ def test_mixed_sload_sstore(
482482

483483
# For each contract, execute SSTORE operations (approve)
484484
attack_code += (
485+
# Store function selector at memory[32] once (outside loop)
486+
Op.MSTORE(offset=32, value=APPROVE_SELECTOR)
485487
# Initialize counter in memory[0] = number of approve calls
486-
Op.MSTORE(offset=0, value=sstore_calls_per_contract)
488+
+ Op.MSTORE(offset=0, value=sstore_calls_per_contract)
487489
# Loop for approve calls
488490
+ While(
489491
condition=Op.MLOAD(0) + Op.ISZERO + Op.ISZERO,
490492
body=(
491-
# Store function selector at memory[32]
492-
Op.MSTORE(offset=32, value=APPROVE_SELECTOR)
493493
# Store spender address at memory[64] (use counter)
494-
+ Op.MSTORE(offset=64, value=Op.MLOAD(0))
494+
Op.MSTORE(offset=64, value=Op.MLOAD(0))
495495
# Store amount at memory[96] (use counter as amount)
496496
+ Op.MSTORE(offset=96, value=Op.MLOAD(0))
497497
# Call approve(spender, amount) on ERC20 contract
@@ -500,10 +500,10 @@ def test_mixed_sload_sstore(
500500
value=0,
501501
args_offset=32,
502502
args_size=68,
503-
ret_offset=128,
504-
ret_size=32,
503+
ret_offset=0,
504+
ret_size=0,
505505
)
506-
+ Op.POP # Discard result
506+
+ Op.POP # Discard CALL success status
507507
# Decrement counter
508508
+ Op.MSTORE(offset=0, value=Op.SUB(Op.MLOAD(0), 1))
509509
),

tests/benchmark/stateful/bloatnet/test_single_opcode.py

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -128,27 +128,26 @@ def test_sload_empty_erc20_balanceof(
128128
for erc20_address in erc20_addresses:
129129
# For each contract, initialize counter and loop
130130
attack_code += (
131+
# Store function selector at memory[32] once (outside loop)
132+
Op.MSTORE(offset=32, value=BALANCEOF_SELECTOR)
131133
# Initialize counter in memory[0] = number of calls
132-
Op.MSTORE(offset=0, value=calls_per_contract)
134+
+ Op.MSTORE(offset=0, value=calls_per_contract)
133135
# Loop for this specific contract
134136
+ While(
135137
condition=Op.MLOAD(0) + Op.ISZERO + Op.ISZERO, # Continue while counter > 0
136138
body=(
137-
# Use counter directly as address (cheapest option)
138-
# Store function selector at memory[32]
139-
Op.MSTORE(offset=32, value=BALANCEOF_SELECTOR)
140-
# Store address at memory[64] (just use counter as address)
141-
+ Op.MSTORE(offset=64, value=Op.MLOAD(0))
139+
# Store address at memory[64] (use counter as address)
140+
Op.MSTORE(offset=64, value=Op.MLOAD(0))
142141
# Call balanceOf(address) on ERC20 contract
143142
+ Op.CALL(
144143
address=erc20_address,
145144
value=0,
146145
args_offset=32,
147146
args_size=36,
148-
ret_offset=96,
149-
ret_size=32,
147+
ret_offset=0,
148+
ret_size=0,
150149
)
151-
+ Op.POP # Discard result
150+
+ Op.POP # Discard CALL success status
152151
# Decrement counter: counter - 1
153152
+ Op.MSTORE(offset=0, value=Op.SUB(Op.MLOAD(0), 1))
154153
),
@@ -283,17 +282,16 @@ def test_sstore_erc20_approve(
283282
for erc20_address in erc20_addresses:
284283
# For each contract, initialize counter and loop
285284
attack_code += (
285+
# Store function selector at memory[32] once (outside loop)
286+
Op.MSTORE(offset=32, value=APPROVE_SELECTOR)
286287
# Initialize counter in memory[0] = number of calls
287-
Op.MSTORE(offset=0, value=calls_per_contract)
288+
+ Op.MSTORE(offset=0, value=calls_per_contract)
288289
# Loop for this specific contract
289290
+ While(
290291
condition=Op.MLOAD(0) + Op.ISZERO + Op.ISZERO, # Continue while counter > 0
291292
body=(
292-
# Use counter directly as spender address (cheapest option)
293-
# Store function selector at memory[32]
294-
Op.MSTORE(offset=32, value=APPROVE_SELECTOR)
295293
# Store spender address at memory[64] (use counter)
296-
+ Op.MSTORE(offset=64, value=Op.MLOAD(0))
294+
Op.MSTORE(offset=64, value=Op.MLOAD(0))
297295
# Store amount at memory[96] (use counter as amount)
298296
+ Op.MSTORE(offset=96, value=Op.MLOAD(0))
299297
# Call approve(spender, amount) on ERC20 contract
@@ -302,10 +300,10 @@ def test_sstore_erc20_approve(
302300
value=0,
303301
args_offset=32,
304302
args_size=68, # 4 bytes selector + 32 bytes spender + 32 bytes amount
305-
ret_offset=128,
306-
ret_size=32,
303+
ret_offset=0,
304+
ret_size=0,
307305
)
308-
+ Op.POP # Discard result
306+
+ Op.POP # Discard CALL success status
309307
# Decrement counter: counter - 1
310308
+ Op.MSTORE(offset=0, value=Op.SUB(Op.MLOAD(0), 1))
311309
),

0 commit comments

Comments
 (0)