From a1f2153b8df3bbfd9bb78d7b937edd57ccb0effb Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Thu, 14 Aug 2025 13:00:54 +0200 Subject: [PATCH 01/31] Add BloatNet tests Signed-off-by: Guillaume Ballet <3272758+gballet@users.noreply.github.com> --- .../prague/eip8047_bloatnet/test_bloatnet.py | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 tests/prague/eip8047_bloatnet/test_bloatnet.py diff --git a/tests/prague/eip8047_bloatnet/test_bloatnet.py b/tests/prague/eip8047_bloatnet/test_bloatnet.py new file mode 100644 index 00000000000..0bf2d3fb192 --- /dev/null +++ b/tests/prague/eip8047_bloatnet/test_bloatnet.py @@ -0,0 +1,59 @@ +""" +abstract: Tests [EIP-8047 BloatNet](https://eips.ethereum.org/EIPS/eip-8047) + Test cases for [EIP-8047 BloatNet](https://eips.ethereum.org/EIPS/eip-8047)]. +""" + +import pytest + +from ethereum_test_tools import Account, Alloc, Block, BlockchainTestFiller, Transaction +from ethereum_test_tools.vm.opcode import Opcodes as Op + +REFERENCE_SPEC_GIT_PATH = "DUMMY/eip-DUMMY.md" +REFERENCE_SPEC_VERSION = "DUMMY_VERSION" + + +@pytest.mark.valid_from("Prague") +def test_bloatnet(blockchain_test: BlockchainTestFiller, pre: Alloc): + """ + A test that calls a contract with many SSTOREs + + The first block will have many SSTORES that go from 0 -> 1 + and the 2nd block will have many SSTORES that go from 1 -> 2 + """ + # One gotcha is ensuring that the transaction `gas_limit` is set high + # enough to cover the gas cost of the contract execution. + + storage_slot: int = 1 + + sstore_code = Op.PUSH0 + for _ in range(100000): + sstore_code = sstore_code + Op.SSTORE(storage_slot, 1) # NOTE: this will probably push some value on the stack, but I want to increase it to reduce the amount of gas and the size of the bytecode + storage_slot += 1 + sstore_code = Op.POP + + sender = pre.fund_eoa() + print(sender) + contract_address = pre.deploy_contract( + code=sstore_code, + storage={}, + ) + + tx_0_1 = Transaction( + to=contract_address, + gas_limit=30000000, + data=b"", + value=0, + sender=sender, + ) + tx_1_2 = Transaction( + to=contract_address, + gas_limit=30000000, + data=b"", + value=0, + sender=sender, + ) + + # TODO: Modify post-state allocations here. + post = {contract_address: Account(storage={storage_slot: 0x2})} + + blockchain_test(pre=pre, blocks=[Block(txs=[tx_0_1]), Block(txs=[tx_1_2])], post=post) From 02d65b460eded007e0e03e7ace8aad2762cf7537 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Thu, 14 Aug 2025 15:33:24 +0200 Subject: [PATCH 02/31] try building the contract Signed-off-by: Guillaume Ballet <3272758+gballet@users.noreply.github.com> --- .../prague/eip8047_bloatnet/test_bloatnet.py | 50 ++++++++++++------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/tests/prague/eip8047_bloatnet/test_bloatnet.py b/tests/prague/eip8047_bloatnet/test_bloatnet.py index 0bf2d3fb192..26cf7289a14 100644 --- a/tests/prague/eip8047_bloatnet/test_bloatnet.py +++ b/tests/prague/eip8047_bloatnet/test_bloatnet.py @@ -7,35 +7,51 @@ from ethereum_test_tools import Account, Alloc, Block, BlockchainTestFiller, Transaction from ethereum_test_tools.vm.opcode import Opcodes as Op +from ethereum_test_forks import Fork REFERENCE_SPEC_GIT_PATH = "DUMMY/eip-DUMMY.md" REFERENCE_SPEC_VERSION = "DUMMY_VERSION" @pytest.mark.valid_from("Prague") -def test_bloatnet(blockchain_test: BlockchainTestFiller, pre: Alloc): +def test_bloatnet(blockchain_test: BlockchainTestFiller, pre: Alloc, fork: Fork): """ A test that calls a contract with many SSTOREs The first block will have many SSTORES that go from 0 -> 1 and the 2nd block will have many SSTORES that go from 1 -> 2 """ - # One gotcha is ensuring that the transaction `gas_limit` is set high - # enough to cover the gas cost of the contract execution. + # Get gas costs for the current fork + gas_costs = fork.gas_costs() storage_slot: int = 1 + storage = {} + + totalgas = gas_costs.G_BASE * 2 # Initial gas for PUSH0 + POP + gas_increment = gas_costs.G_VERY_LOW * 2 + gas_costs.G_STORAGE_SET + gas_costs.G_COLD_SLOAD sstore_code = Op.PUSH0 - for _ in range(100000): - sstore_code = sstore_code + Op.SSTORE(storage_slot, 1) # NOTE: this will probably push some value on the stack, but I want to increase it to reduce the amount of gas and the size of the bytecode + i = 0 + while totalgas + gas_increment < 30_000_000: + totalgas += gas_increment + print(f"increment={gas_increment} < totalgas={totalgas} i={i}") + if i < 256: + sstore_code = sstore_code + Op.PUSH1(i) + else: + sstore_code = sstore_code + Op.PUSH2(i) + + sstore_code = sstore_code + Op.PUSH1(1) + Op.SSTORE(unchecked=True) + + storage[storage_slot] = 0x1 storage_slot += 1 - sstore_code = Op.POP + i += 1 + sstore_code = sstore_code + Op.POP sender = pre.fund_eoa() print(sender) contract_address = pre.deploy_contract( code=sstore_code, - storage={}, + storage=storage, ) tx_0_1 = Transaction( @@ -45,15 +61,13 @@ def test_bloatnet(blockchain_test: BlockchainTestFiller, pre: Alloc): value=0, sender=sender, ) - tx_1_2 = Transaction( - to=contract_address, - gas_limit=30000000, - data=b"", - value=0, - sender=sender, - ) - - # TODO: Modify post-state allocations here. - post = {contract_address: Account(storage={storage_slot: 0x2})} + # tx_1_2 = Transaction( + # to=contract_address, + # gas_limit=30000000, + # data=b"", + # value=0, + # sender=sender, + # ) - blockchain_test(pre=pre, blocks=[Block(txs=[tx_0_1]), Block(txs=[tx_1_2])], post=post) + post = {contract_address: Account(storage=storage)} + blockchain_test(pre=pre, blocks=[Block(txs=[tx_0_1])], post=post) From e721cc635af591fa129a9027db4fb85042c1ded7 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Thu, 14 Aug 2025 16:17:10 +0200 Subject: [PATCH 03/31] fix: SSTORE 0 -> 1 match all values in the state Signed-off-by: Guillaume Ballet <3272758+gballet@users.noreply.github.com> --- tests/prague/eip8047_bloatnet/test_bloatnet.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/prague/eip8047_bloatnet/test_bloatnet.py b/tests/prague/eip8047_bloatnet/test_bloatnet.py index 26cf7289a14..276aafb9b8e 100644 --- a/tests/prague/eip8047_bloatnet/test_bloatnet.py +++ b/tests/prague/eip8047_bloatnet/test_bloatnet.py @@ -24,23 +24,25 @@ def test_bloatnet(blockchain_test: BlockchainTestFiller, pre: Alloc, fork: Fork) # Get gas costs for the current fork gas_costs = fork.gas_costs() - storage_slot: int = 1 + storage_slot: int = 0 storage = {} + GasLimit = 30_000_000 # Default gas limit seems to be >90M in this env totalgas = gas_costs.G_BASE * 2 # Initial gas for PUSH0 + POP gas_increment = gas_costs.G_VERY_LOW * 2 + gas_costs.G_STORAGE_SET + gas_costs.G_COLD_SLOAD sstore_code = Op.PUSH0 i = 0 - while totalgas + gas_increment < 30_000_000: + while totalgas + gas_increment < GasLimit: totalgas += gas_increment print(f"increment={gas_increment} < totalgas={totalgas} i={i}") + sstore_code = sstore_code + Op.PUSH1(1) if i < 256: sstore_code = sstore_code + Op.PUSH1(i) else: sstore_code = sstore_code + Op.PUSH2(i) - sstore_code = sstore_code + Op.PUSH1(1) + Op.SSTORE(unchecked=True) + sstore_code = sstore_code + Op.SSTORE(unchecked=True) storage[storage_slot] = 0x1 storage_slot += 1 @@ -56,7 +58,7 @@ def test_bloatnet(blockchain_test: BlockchainTestFiller, pre: Alloc, fork: Fork) tx_0_1 = Transaction( to=contract_address, - gas_limit=30000000, + gas_limit=GasLimit, data=b"", value=0, sender=sender, From d1cad258b6f58499014402371a7b4468ab8e4f7f Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Thu, 14 Aug 2025 16:43:20 +0200 Subject: [PATCH 04/31] add the tx for 0 -> 1 and 1 -> 2 Signed-off-by: Guillaume Ballet <3272758+gballet@users.noreply.github.com> --- .../prague/eip8047_bloatnet/test_bloatnet.py | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/tests/prague/eip8047_bloatnet/test_bloatnet.py b/tests/prague/eip8047_bloatnet/test_bloatnet.py index 276aafb9b8e..9e0c4357057 100644 --- a/tests/prague/eip8047_bloatnet/test_bloatnet.py +++ b/tests/prague/eip8047_bloatnet/test_bloatnet.py @@ -29,14 +29,14 @@ def test_bloatnet(blockchain_test: BlockchainTestFiller, pre: Alloc, fork: Fork) storage = {} GasLimit = 30_000_000 # Default gas limit seems to be >90M in this env - totalgas = gas_costs.G_BASE * 2 # Initial gas for PUSH0 + POP + totalgas = gas_costs.G_BASE * 2 # Initial gas for PUSH0 + CALLDATALOAD gas_increment = gas_costs.G_VERY_LOW * 2 + gas_costs.G_STORAGE_SET + gas_costs.G_COLD_SLOAD - sstore_code = Op.PUSH0 + sstore_code = Op.PUSH0 + Op.CALLDATALOAD + Op.DUP1 i = 0 while totalgas + gas_increment < GasLimit: totalgas += gas_increment - print(f"increment={gas_increment} < totalgas={totalgas} i={i}") - sstore_code = sstore_code + Op.PUSH1(1) + # print(f"increment={gas_increment} < totalgas={totalgas} i={i}") + sstore_code = sstore_code + Op.DUP1 if i < 256: sstore_code = sstore_code + Op.PUSH1(i) else: @@ -44,10 +44,10 @@ def test_bloatnet(blockchain_test: BlockchainTestFiller, pre: Alloc, fork: Fork) sstore_code = sstore_code + Op.SSTORE(unchecked=True) - storage[storage_slot] = 0x1 + storage[storage_slot] = 0x02 << 248 storage_slot += 1 i += 1 - sstore_code = sstore_code + Op.POP + sstore_code = sstore_code + Op.POP # Drop last value on the stack sender = pre.fund_eoa() print(sender) @@ -59,17 +59,18 @@ def test_bloatnet(blockchain_test: BlockchainTestFiller, pre: Alloc, fork: Fork) tx_0_1 = Transaction( to=contract_address, gas_limit=GasLimit, - data=b"", + data=b'\x01', # Single byte 0x01 + value=0, + sender=sender, + ) + tx_1_2 = Transaction( + to=contract_address, + gas_limit=30000000, + data=b'\x02', # Single byte 0x02, turns into 0x2000000000000000000000000000000000000000000000000000000000000000 value=0, sender=sender, ) - # tx_1_2 = Transaction( - # to=contract_address, - # gas_limit=30000000, - # data=b"", - # value=0, - # sender=sender, - # ) post = {contract_address: Account(storage=storage)} - blockchain_test(pre=pre, blocks=[Block(txs=[tx_0_1])], post=post) + + blockchain_test(pre=pre, blocks=[Block(txs=[tx_0_1, tx_1_2])], post=post) From 16f6d3017d8f3dae3aff9a1dd3adaa7ab2e1a59a Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Thu, 14 Aug 2025 17:03:07 +0200 Subject: [PATCH 05/31] fix: linter issues Signed-off-by: Guillaume Ballet <3272758+gballet@users.noreply.github.com> --- tests/prague/eip8047_bloatnet/test_bloatnet.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/tests/prague/eip8047_bloatnet/test_bloatnet.py b/tests/prague/eip8047_bloatnet/test_bloatnet.py index 9e0c4357057..259fa0146a1 100644 --- a/tests/prague/eip8047_bloatnet/test_bloatnet.py +++ b/tests/prague/eip8047_bloatnet/test_bloatnet.py @@ -5,18 +5,18 @@ import pytest +from ethereum_test_forks import Fork from ethereum_test_tools import Account, Alloc, Block, BlockchainTestFiller, Transaction from ethereum_test_tools.vm.opcode import Opcodes as Op -from ethereum_test_forks import Fork REFERENCE_SPEC_GIT_PATH = "DUMMY/eip-DUMMY.md" -REFERENCE_SPEC_VERSION = "DUMMY_VERSION" - +REFERENCE_SPEC_VERSION = "0.1" +GAS_LIMIT = 30_000_000 # Default gas limit seems to be >90M in this env @pytest.mark.valid_from("Prague") def test_bloatnet(blockchain_test: BlockchainTestFiller, pre: Alloc, fork: Fork): """ - A test that calls a contract with many SSTOREs + A test that calls a contract with many SSTOREs. The first block will have many SSTORES that go from 0 -> 1 and the 2nd block will have many SSTORES that go from 1 -> 2 @@ -27,13 +27,12 @@ def test_bloatnet(blockchain_test: BlockchainTestFiller, pre: Alloc, fork: Fork) storage_slot: int = 0 storage = {} - GasLimit = 30_000_000 # Default gas limit seems to be >90M in this env totalgas = gas_costs.G_BASE * 2 # Initial gas for PUSH0 + CALLDATALOAD gas_increment = gas_costs.G_VERY_LOW * 2 + gas_costs.G_STORAGE_SET + gas_costs.G_COLD_SLOAD sstore_code = Op.PUSH0 + Op.CALLDATALOAD + Op.DUP1 i = 0 - while totalgas + gas_increment < GasLimit: + while totalgas + gas_increment < GAS_LIMIT: totalgas += gas_increment # print(f"increment={gas_increment} < totalgas={totalgas} i={i}") sstore_code = sstore_code + Op.DUP1 @@ -58,15 +57,15 @@ def test_bloatnet(blockchain_test: BlockchainTestFiller, pre: Alloc, fork: Fork) tx_0_1 = Transaction( to=contract_address, - gas_limit=GasLimit, + gas_limit=GAS_LIMIT, data=b'\x01', # Single byte 0x01 value=0, sender=sender, ) tx_1_2 = Transaction( to=contract_address, - gas_limit=30000000, - data=b'\x02', # Single byte 0x02, turns into 0x2000000000000000000000000000000000000000000000000000000000000000 + gas_limit=GAS_LIMIT, + data=b'\x02', # Single byte 0x02, turns into 0x02 << 248 value=0, sender=sender, ) From 374e08a4e68e4b36f20775f4fbcd864ffac8983a Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Thu, 14 Aug 2025 21:05:27 +0200 Subject: [PATCH 06/31] remove more whitespaces Signed-off-by: Guillaume Ballet <3272758+gballet@users.noreply.github.com> remove leftover single whitespace :| --- tests/prague/eip8047_bloatnet/test_bloatnet.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/prague/eip8047_bloatnet/test_bloatnet.py b/tests/prague/eip8047_bloatnet/test_bloatnet.py index 259fa0146a1..797973ea7d2 100644 --- a/tests/prague/eip8047_bloatnet/test_bloatnet.py +++ b/tests/prague/eip8047_bloatnet/test_bloatnet.py @@ -40,9 +40,9 @@ def test_bloatnet(blockchain_test: BlockchainTestFiller, pre: Alloc, fork: Fork) sstore_code = sstore_code + Op.PUSH1(i) else: sstore_code = sstore_code + Op.PUSH2(i) - + sstore_code = sstore_code + Op.SSTORE(unchecked=True) - + storage[storage_slot] = 0x02 << 248 storage_slot += 1 i += 1 From 333c8769d79ebe76d747eabde7e1fc37e1969765 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Fri, 15 Aug 2025 14:01:58 +0200 Subject: [PATCH 07/31] fix formatting --- tests/prague/eip8047_bloatnet/test_bloatnet.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/prague/eip8047_bloatnet/test_bloatnet.py b/tests/prague/eip8047_bloatnet/test_bloatnet.py index 797973ea7d2..0fa28ce7fcf 100644 --- a/tests/prague/eip8047_bloatnet/test_bloatnet.py +++ b/tests/prague/eip8047_bloatnet/test_bloatnet.py @@ -11,7 +11,8 @@ REFERENCE_SPEC_GIT_PATH = "DUMMY/eip-DUMMY.md" REFERENCE_SPEC_VERSION = "0.1" -GAS_LIMIT = 30_000_000 # Default gas limit seems to be >90M in this env +GAS_LIMIT = 30_000_000 # Default gas limit seems to be >90M in this env + @pytest.mark.valid_from("Prague") def test_bloatnet(blockchain_test: BlockchainTestFiller, pre: Alloc, fork: Fork): @@ -29,7 +30,7 @@ def test_bloatnet(blockchain_test: BlockchainTestFiller, pre: Alloc, fork: Fork) storage = {} totalgas = gas_costs.G_BASE * 2 # Initial gas for PUSH0 + CALLDATALOAD - gas_increment = gas_costs.G_VERY_LOW * 2 + gas_costs.G_STORAGE_SET + gas_costs.G_COLD_SLOAD + gas_increment = gas_costs.G_VERY_LOW * 2 + gas_costs.G_STORAGE_SET + gas_costs.G_COLD_SLOAD sstore_code = Op.PUSH0 + Op.CALLDATALOAD + Op.DUP1 i = 0 while totalgas + gas_increment < GAS_LIMIT: @@ -46,7 +47,7 @@ def test_bloatnet(blockchain_test: BlockchainTestFiller, pre: Alloc, fork: Fork) storage[storage_slot] = 0x02 << 248 storage_slot += 1 i += 1 - sstore_code = sstore_code + Op.POP # Drop last value on the stack + sstore_code = sstore_code + Op.POP # Drop last value on the stack sender = pre.fund_eoa() print(sender) @@ -58,14 +59,14 @@ def test_bloatnet(blockchain_test: BlockchainTestFiller, pre: Alloc, fork: Fork) tx_0_1 = Transaction( to=contract_address, gas_limit=GAS_LIMIT, - data=b'\x01', # Single byte 0x01 + data=b"\x01", # Single byte 0x01 value=0, sender=sender, ) tx_1_2 = Transaction( to=contract_address, gas_limit=GAS_LIMIT, - data=b'\x02', # Single byte 0x02, turns into 0x02 << 248 + data=b"\x02", # Single byte 0x02, turns into 0x02 << 248 value=0, sender=sender, ) From 79a95b827746ac2d1601e00d1bc064a714d5759d Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Thu, 21 Aug 2025 17:33:33 +0200 Subject: [PATCH 08/31] move to benchmarks Signed-off-by: Guillaume Ballet <3272758+gballet@users.noreply.github.com> --- tests/{prague/eip8047_bloatnet => benchmark}/test_bloatnet.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/{prague/eip8047_bloatnet => benchmark}/test_bloatnet.py (100%) diff --git a/tests/prague/eip8047_bloatnet/test_bloatnet.py b/tests/benchmark/test_bloatnet.py similarity index 100% rename from tests/prague/eip8047_bloatnet/test_bloatnet.py rename to tests/benchmark/test_bloatnet.py From 8131e980ed041f605b83bb0e04700e050fa70227 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Fri, 22 Aug 2025 13:17:14 +0200 Subject: [PATCH 09/31] fix linter value --- tests/benchmark/test_bloatnet.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/benchmark/test_bloatnet.py b/tests/benchmark/test_bloatnet.py index 0fa28ce7fcf..fdf23605a1f 100644 --- a/tests/benchmark/test_bloatnet.py +++ b/tests/benchmark/test_bloatnet.py @@ -6,7 +6,7 @@ import pytest from ethereum_test_forks import Fork -from ethereum_test_tools import Account, Alloc, Block, BlockchainTestFiller, Transaction +from ethereum_test_tools import Account, Alloc, Block, BlockchainTestFiller, Storage, Transaction from ethereum_test_tools.vm.opcode import Opcodes as Op REFERENCE_SPEC_GIT_PATH = "DUMMY/eip-DUMMY.md" @@ -27,7 +27,7 @@ def test_bloatnet(blockchain_test: BlockchainTestFiller, pre: Alloc, fork: Fork) storage_slot: int = 0 - storage = {} + storage = Storage() totalgas = gas_costs.G_BASE * 2 # Initial gas for PUSH0 + CALLDATALOAD gas_increment = gas_costs.G_VERY_LOW * 2 + gas_costs.G_STORAGE_SET + gas_costs.G_COLD_SLOAD From 5f805fd36bc137cb27876eeb8eb38c2fa5463da6 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Fri, 22 Aug 2025 17:07:21 +0200 Subject: [PATCH 10/31] use the gas limit from the environment --- tests/benchmark/test_bloatnet.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/tests/benchmark/test_bloatnet.py b/tests/benchmark/test_bloatnet.py index fdf23605a1f..0b01a223d9c 100644 --- a/tests/benchmark/test_bloatnet.py +++ b/tests/benchmark/test_bloatnet.py @@ -6,13 +6,11 @@ import pytest from ethereum_test_forks import Fork -from ethereum_test_tools import Account, Alloc, Block, BlockchainTestFiller, Storage, Transaction +from ethereum_test_tools import Account, Alloc, Block, BlockchainTestFiller, Environment, Storage, Transaction from ethereum_test_tools.vm.opcode import Opcodes as Op REFERENCE_SPEC_GIT_PATH = "DUMMY/eip-DUMMY.md" REFERENCE_SPEC_VERSION = "0.1" -GAS_LIMIT = 30_000_000 # Default gas limit seems to be >90M in this env - @pytest.mark.valid_from("Prague") def test_bloatnet(blockchain_test: BlockchainTestFiller, pre: Alloc, fork: Fork): @@ -33,7 +31,7 @@ def test_bloatnet(blockchain_test: BlockchainTestFiller, pre: Alloc, fork: Fork) gas_increment = gas_costs.G_VERY_LOW * 2 + gas_costs.G_STORAGE_SET + gas_costs.G_COLD_SLOAD sstore_code = Op.PUSH0 + Op.CALLDATALOAD + Op.DUP1 i = 0 - while totalgas + gas_increment < GAS_LIMIT: + while totalgas + gas_increment < Environment().gas_limit: totalgas += gas_increment # print(f"increment={gas_increment} < totalgas={totalgas} i={i}") sstore_code = sstore_code + Op.DUP1 @@ -58,14 +56,14 @@ def test_bloatnet(blockchain_test: BlockchainTestFiller, pre: Alloc, fork: Fork) tx_0_1 = Transaction( to=contract_address, - gas_limit=GAS_LIMIT, + gas_limit=Environment().gas_limit, data=b"\x01", # Single byte 0x01 value=0, sender=sender, ) tx_1_2 = Transaction( to=contract_address, - gas_limit=GAS_LIMIT, + gas_limit=Environment().gas_limit, data=b"\x02", # Single byte 0x02, turns into 0x02 << 248 value=0, sender=sender, From 090a400178a6e0a3c83b2cd2b5a4c8afd5df2719 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Tue, 26 Aug 2025 06:31:28 -0400 Subject: [PATCH 11/31] parameterize the written value in SSTORE --- tests/benchmark/test_bloatnet.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/benchmark/test_bloatnet.py b/tests/benchmark/test_bloatnet.py index 0b01a223d9c..26492539c8d 100644 --- a/tests/benchmark/test_bloatnet.py +++ b/tests/benchmark/test_bloatnet.py @@ -13,7 +13,8 @@ REFERENCE_SPEC_VERSION = "0.1" @pytest.mark.valid_from("Prague") -def test_bloatnet(blockchain_test: BlockchainTestFiller, pre: Alloc, fork: Fork): +@pytest.mark.parametrize("final_storage_value", [0x02 << 248, 0x02]) +def test_bloatnet(blockchain_test: BlockchainTestFiller, pre: Alloc, fork: Fork, final_storage_value: int): """ A test that calls a contract with many SSTOREs. @@ -42,7 +43,7 @@ def test_bloatnet(blockchain_test: BlockchainTestFiller, pre: Alloc, fork: Fork) sstore_code = sstore_code + Op.SSTORE(unchecked=True) - storage[storage_slot] = 0x02 << 248 + storage[storage_slot] = final_storage_value storage_slot += 1 i += 1 sstore_code = sstore_code + Op.POP # Drop last value on the stack @@ -57,14 +58,14 @@ def test_bloatnet(blockchain_test: BlockchainTestFiller, pre: Alloc, fork: Fork) tx_0_1 = Transaction( to=contract_address, gas_limit=Environment().gas_limit, - data=b"\x01", # Single byte 0x01 + data=(final_storage_value//2).to_bytes(32, 'big').rstrip(b'\x00'), value=0, sender=sender, ) tx_1_2 = Transaction( to=contract_address, gas_limit=Environment().gas_limit, - data=b"\x02", # Single byte 0x02, turns into 0x02 << 248 + data=final_storage_value.to_bytes(32, 'big').rstrip(b'\x00'), value=0, sender=sender, ) From cd02a02163f3f5f5453cdd16f72e13c085486c93 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Tue, 26 Aug 2025 08:36:08 -0400 Subject: [PATCH 12/31] fix linter issues --- tests/benchmark/test_bloatnet.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tests/benchmark/test_bloatnet.py b/tests/benchmark/test_bloatnet.py index 26492539c8d..6559bdb416f 100644 --- a/tests/benchmark/test_bloatnet.py +++ b/tests/benchmark/test_bloatnet.py @@ -6,7 +6,15 @@ import pytest from ethereum_test_forks import Fork -from ethereum_test_tools import Account, Alloc, Block, BlockchainTestFiller, Environment, Storage, Transaction +from ethereum_test_tools import ( + Account, + Alloc, + Block, + BlockchainTestFiller, + Environment, + Storage, + Transaction, +) from ethereum_test_tools.vm.opcode import Opcodes as Op REFERENCE_SPEC_GIT_PATH = "DUMMY/eip-DUMMY.md" @@ -14,7 +22,9 @@ @pytest.mark.valid_from("Prague") @pytest.mark.parametrize("final_storage_value", [0x02 << 248, 0x02]) -def test_bloatnet(blockchain_test: BlockchainTestFiller, pre: Alloc, fork: Fork, final_storage_value: int): +def test_bloatnet( + blockchain_test: BlockchainTestFiller, pre: Alloc, fork: Fork, final_storage_value: int +): """ A test that calls a contract with many SSTOREs. From 1f3c381de3758182280d7781d32406734aae12bd Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Tue, 26 Aug 2025 08:43:50 -0400 Subject: [PATCH 13/31] update CHANGELOG.md --- docs/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 1d419042a88..8716c3b978b 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -131,6 +131,7 @@ Users can select any of the artifacts depending on their testing needs for their ### ๐Ÿงช Test Cases +- โœจ [BloatNet](bloatnet.info)/Multidimensional Metering: Add benchmarks to be used as part of the BloatNet project and also for Multidimensional Metering. - โœจ [EIP-7951](https://eips.ethereum.org/EIPS/eip-7951): Add additional test cases for modular comparison. - ๐Ÿ”€ Refactored `BLOBHASH` opcode context tests to use the `pre_alloc` plugin in order to avoid contract and EOA address collisions ([#1637](https://github.com/ethereum/execution-spec-tests/pull/1637)). - ๐Ÿ”€ Refactored `SELFDESTRUCT` opcode collision tests to use the `pre_alloc` plugin in order to avoid contract and EOA address collisions ([#1643](https://github.com/ethereum/execution-spec-tests/pull/1643)). From f6def7e3ab2739da990bce2dcb16318a3cb5c985 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Tue, 26 Aug 2025 11:39:45 -0400 Subject: [PATCH 14/31] fix format --- tests/benchmark/test_bloatnet.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/benchmark/test_bloatnet.py b/tests/benchmark/test_bloatnet.py index 6559bdb416f..c683aaf0367 100644 --- a/tests/benchmark/test_bloatnet.py +++ b/tests/benchmark/test_bloatnet.py @@ -20,6 +20,7 @@ REFERENCE_SPEC_GIT_PATH = "DUMMY/eip-DUMMY.md" REFERENCE_SPEC_VERSION = "0.1" + @pytest.mark.valid_from("Prague") @pytest.mark.parametrize("final_storage_value", [0x02 << 248, 0x02]) def test_bloatnet( @@ -68,14 +69,14 @@ def test_bloatnet( tx_0_1 = Transaction( to=contract_address, gas_limit=Environment().gas_limit, - data=(final_storage_value//2).to_bytes(32, 'big').rstrip(b'\x00'), + data=(final_storage_value // 2).to_bytes(32, "big").rstrip(b"\x00"), value=0, sender=sender, ) tx_1_2 = Transaction( to=contract_address, gas_limit=Environment().gas_limit, - data=final_storage_value.to_bytes(32, 'big').rstrip(b'\x00'), + data=final_storage_value.to_bytes(32, "big").rstrip(b"\x00"), value=0, sender=sender, ) From 7e20a501e92f1960cccbba48bcfd06a1f52e76c2 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Tue, 26 Aug 2025 12:02:51 -0400 Subject: [PATCH 15/31] simplify syntax --- tests/benchmark/test_bloatnet.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/tests/benchmark/test_bloatnet.py b/tests/benchmark/test_bloatnet.py index c683aaf0367..a69f0a4f69c 100644 --- a/tests/benchmark/test_bloatnet.py +++ b/tests/benchmark/test_bloatnet.py @@ -39,20 +39,13 @@ def test_bloatnet( storage = Storage() - totalgas = gas_costs.G_BASE * 2 # Initial gas for PUSH0 + CALLDATALOAD + totalgas = gas_costs.G_BASE * 3 + gas_costs.G_VERY_LOW # Initial gas for PUSH0 + CALLDATALOAD + DUP1 + POP (at the end) gas_increment = gas_costs.G_VERY_LOW * 2 + gas_costs.G_STORAGE_SET + gas_costs.G_COLD_SLOAD sstore_code = Op.PUSH0 + Op.CALLDATALOAD + Op.DUP1 i = 0 while totalgas + gas_increment < Environment().gas_limit: totalgas += gas_increment - # print(f"increment={gas_increment} < totalgas={totalgas} i={i}") - sstore_code = sstore_code + Op.DUP1 - if i < 256: - sstore_code = sstore_code + Op.PUSH1(i) - else: - sstore_code = sstore_code + Op.PUSH2(i) - - sstore_code = sstore_code + Op.SSTORE(unchecked=True) + sstore_code = sstore_code + Op.SSTORE(i, Op.DUP4) storage[storage_slot] = final_storage_value storage_slot += 1 From c24ad35a780b0de8efc95e229e35e19df0ed5ae8 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Tue, 26 Aug 2025 14:32:01 -0400 Subject: [PATCH 16/31] fix: start with an empty contract storage --- tests/benchmark/test_bloatnet.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/benchmark/test_bloatnet.py b/tests/benchmark/test_bloatnet.py index a69f0a4f69c..82b3d65d2bc 100644 --- a/tests/benchmark/test_bloatnet.py +++ b/tests/benchmark/test_bloatnet.py @@ -56,7 +56,7 @@ def test_bloatnet( print(sender) contract_address = pre.deploy_contract( code=sstore_code, - storage=storage, + storage=Storage(), ) tx_0_1 = Transaction( From fc27e53f0cf3ceeb8249b897f5a283ad18cd323d Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Tue, 26 Aug 2025 15:04:37 -0400 Subject: [PATCH 17/31] more fixes, but the result is still incorrect --- tests/benchmark/test_bloatnet.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/tests/benchmark/test_bloatnet.py b/tests/benchmark/test_bloatnet.py index 82b3d65d2bc..bd03369afc3 100644 --- a/tests/benchmark/test_bloatnet.py +++ b/tests/benchmark/test_bloatnet.py @@ -35,21 +35,19 @@ def test_bloatnet( # Get gas costs for the current fork gas_costs = fork.gas_costs() - storage_slot: int = 0 storage = Storage() - totalgas = gas_costs.G_BASE * 3 + gas_costs.G_VERY_LOW # Initial gas for PUSH0 + CALLDATALOAD + DUP1 + POP (at the end) + totalgas = gas_costs.G_BASE * 2 + gas_costs.G_VERY_LOW # Initial gas for PUSH0 + CALLDATALOAD + POP (at the end) gas_increment = gas_costs.G_VERY_LOW * 2 + gas_costs.G_STORAGE_SET + gas_costs.G_COLD_SLOAD - sstore_code = Op.PUSH0 + Op.CALLDATALOAD + Op.DUP1 - i = 0 + sstore_code = Op.PUSH0 + Op.CALLDATALOAD + storage_slot: int = 0 while totalgas + gas_increment < Environment().gas_limit: totalgas += gas_increment - sstore_code = sstore_code + Op.SSTORE(i, Op.DUP4) - + sstore_code = sstore_code + Op.SSTORE(Op.DUP2, storage_slot) storage[storage_slot] = final_storage_value storage_slot += 1 - i += 1 + sstore_code = sstore_code + Op.POP # Drop last value on the stack sender = pre.fund_eoa() From 7d872624aa1104b50faf70ede6d83cbc79926e9c Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Tue, 26 Aug 2025 15:22:00 -0400 Subject: [PATCH 18/31] fix: finally fix the tests --- tests/benchmark/test_bloatnet.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/benchmark/test_bloatnet.py b/tests/benchmark/test_bloatnet.py index bd03369afc3..633f6f0df07 100644 --- a/tests/benchmark/test_bloatnet.py +++ b/tests/benchmark/test_bloatnet.py @@ -35,16 +35,19 @@ def test_bloatnet( # Get gas costs for the current fork gas_costs = fork.gas_costs() + # this is only used for computing the intinsic gas + data = final_storage_value.to_bytes(32, "big").rstrip(b"\x00") storage = Storage() totalgas = gas_costs.G_BASE * 2 + gas_costs.G_VERY_LOW # Initial gas for PUSH0 + CALLDATALOAD + POP (at the end) + totalgas = totalgas + fork.transaction_intrinsic_cost_calculator()(calldata=data); gas_increment = gas_costs.G_VERY_LOW * 2 + gas_costs.G_STORAGE_SET + gas_costs.G_COLD_SLOAD sstore_code = Op.PUSH0 + Op.CALLDATALOAD storage_slot: int = 0 while totalgas + gas_increment < Environment().gas_limit: totalgas += gas_increment - sstore_code = sstore_code + Op.SSTORE(Op.DUP2, storage_slot) + sstore_code = sstore_code + Op.SSTORE(storage_slot, Op.DUP1) storage[storage_slot] = final_storage_value storage_slot += 1 From 8556014c1d630431fd35345d0afb40ca3328cf2a Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Wed, 27 Aug 2025 09:05:47 +0200 Subject: [PATCH 19/31] linter fix --- tests/benchmark/test_bloatnet.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/benchmark/test_bloatnet.py b/tests/benchmark/test_bloatnet.py index 633f6f0df07..64c7a2022ae 100644 --- a/tests/benchmark/test_bloatnet.py +++ b/tests/benchmark/test_bloatnet.py @@ -40,8 +40,9 @@ def test_bloatnet( storage = Storage() - totalgas = gas_costs.G_BASE * 2 + gas_costs.G_VERY_LOW # Initial gas for PUSH0 + CALLDATALOAD + POP (at the end) - totalgas = totalgas + fork.transaction_intrinsic_cost_calculator()(calldata=data); + # Initial gas for PUSH0 + CALLDATALOAD + POP (at the end) + totalgas = gas_costs.G_BASE * 2 + gas_costs.G_VERY_LOW + totalgas = totalgas + fork.transaction_intrinsic_cost_calculator()(calldata=data) gas_increment = gas_costs.G_VERY_LOW * 2 + gas_costs.G_STORAGE_SET + gas_costs.G_COLD_SLOAD sstore_code = Op.PUSH0 + Op.CALLDATALOAD storage_slot: int = 0 From 326915ea5c05735d2d9de3959657c318d57dccae Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Wed, 27 Aug 2025 11:03:17 +0200 Subject: [PATCH 20/31] add SLOAD tests --- tests/benchmark/test_bloatnet.py | 91 +++++++++++++++++++++++++++++++- 1 file changed, 89 insertions(+), 2 deletions(-) diff --git a/tests/benchmark/test_bloatnet.py b/tests/benchmark/test_bloatnet.py index 64c7a2022ae..7e468e2a461 100644 --- a/tests/benchmark/test_bloatnet.py +++ b/tests/benchmark/test_bloatnet.py @@ -1,10 +1,11 @@ """ -abstract: Tests [EIP-8047 BloatNet](https://eips.ethereum.org/EIPS/eip-8047) - Test cases for [EIP-8047 BloatNet](https://eips.ethereum.org/EIPS/eip-8047)]. +abstract: Tests [BloatNet](https://bloatnet.info) + Test cases for [BloatNet](https://bloatnet.info). """ import pytest +from ethereum_test_base_types import HashInt from ethereum_test_forks import Fork from ethereum_test_tools import ( Account, @@ -79,3 +80,89 @@ def test_bloatnet( post = {contract_address: Account(storage=storage)} blockchain_test(pre=pre, blocks=[Block(txs=[tx_0_1, tx_1_2])], post=post) + + +# Warm reads are very cheap, which means you can really fill a block +# with them. Only fill the block by a factor of SPEEDUP. +SPEEDUP: int = 100 + + +@pytest.mark.valid_from("Prague") +def test_bloatnet_sload_warm(blockchain_test: BlockchainTestFiller, pre: Alloc, fork: Fork): + """Test that loads warm storage locations many times.""" + gas_costs = fork.gas_costs() + + # Pre-fill storage with values + num_slots = 100 # Number of storage slots to warm up + storage = Storage({HashInt(i): HashInt(0xDEADBEEF + i) for i in range(num_slots)}) + + # Calculate gas costs + totalgas = fork.transaction_intrinsic_cost_calculator()(calldata=b"") + + # First pass - warm up all slots (cold access) + warmup_gas = num_slots * (gas_costs.G_COLD_SLOAD + gas_costs.G_BASE) + totalgas += warmup_gas + + # Calculate how many warm loads we can fit + gas_increment = gas_costs.G_WARM_SLOAD + gas_costs.G_BASE # Warm SLOAD + POP + remaining_gas = Environment().gas_limit - totalgas + num_warm_loads = remaining_gas // (SPEEDUP * gas_increment) + + # Build the complete code: warmup + repeated warm loads + sload_code = Op.SLOAD(0) + Op.POP if num_slots > 0 else Op.STOP + for i in range(1, num_slots): + sload_code = sload_code + Op.SLOAD(i) + Op.POP + for i in range(num_warm_loads): + sload_code = sload_code + Op.SLOAD(i % num_slots) + Op.POP + + sender = pre.fund_eoa() + contract_address = pre.deploy_contract( + code=sload_code, + storage=storage, + ) + + tx = Transaction( + to=contract_address, + gas_limit=Environment().gas_limit, + data=b"", + value=0, + sender=sender, + ) + + post = {contract_address: Account(storage=storage)} + blockchain_test(pre=pre, blocks=[Block(txs=[tx])], post=post) + + +@pytest.mark.valid_from("Prague") +def test_bloatnet_sload_cold(blockchain_test: BlockchainTestFiller, pre: Alloc, fork: Fork): + """Test that loads many different cold storage locations.""" + gas_costs = fork.gas_costs() + + # Calculate gas costs and max slots + totalgas = fork.transaction_intrinsic_cost_calculator()(calldata=b"") + # PUSH + Cold SLOAD + POP + gas_increment = gas_costs.G_VERY_LOW + gas_costs.G_COLD_SLOAD + gas_costs.G_BASE + max_slots = (Environment().gas_limit - totalgas) // gas_increment + + # Build storage and code for all slots + storage = Storage({HashInt(i): HashInt(0xC0FFEE + i) for i in range(max_slots)}) + sload_code = Op.SLOAD(0) + Op.POP if max_slots > 0 else Op.STOP + for i in range(1, max_slots): + sload_code = sload_code + Op.SLOAD(i) + Op.POP + + sender = pre.fund_eoa() + contract_address = pre.deploy_contract( + code=sload_code, + storage=storage, + ) + + tx = Transaction( + to=contract_address, + gas_limit=Environment().gas_limit, + data=b"", + value=0, + sender=sender, + ) + + post = {contract_address: Account(storage=storage)} + blockchain_test(pre=pre, blocks=[Block(txs=[tx])], post=post) From 4164f3c65bce751ad23a2bca927a79014f18525f Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Thu, 28 Aug 2025 06:08:23 -0400 Subject: [PATCH 21/31] review feedback --- tests/benchmark/test_bloatnet.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/benchmark/test_bloatnet.py b/tests/benchmark/test_bloatnet.py index 7e468e2a461..ef9ed139bc4 100644 --- a/tests/benchmark/test_bloatnet.py +++ b/tests/benchmark/test_bloatnet.py @@ -1,6 +1,6 @@ """ -abstract: Tests [BloatNet](https://bloatnet.info) - Test cases for [BloatNet](https://bloatnet.info). +abstract: Tests that benchmarks EVMs to estimate the costs of stateful opcodes. + Tests that benchmarks EVMs to estimate the costs of stateful opcodes.. """ import pytest @@ -18,8 +18,8 @@ ) from ethereum_test_tools.vm.opcode import Opcodes as Op -REFERENCE_SPEC_GIT_PATH = "DUMMY/eip-DUMMY.md" -REFERENCE_SPEC_VERSION = "0.1" +REFERENCE_SPEC_GIT_PATH = "TODO" +REFERENCE_SPEC_VERSION = "TODO" @pytest.mark.valid_from("Prague") From 6d12da61b554fe836eb85ecb61d3442bc640d38e Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Fri, 29 Aug 2025 08:29:02 +0200 Subject: [PATCH 22/31] more review feedback --- tests/benchmark/test_bloatnet.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/benchmark/test_bloatnet.py b/tests/benchmark/test_bloatnet.py index ef9ed139bc4..92a053fb826 100644 --- a/tests/benchmark/test_bloatnet.py +++ b/tests/benchmark/test_bloatnet.py @@ -18,9 +18,6 @@ ) from ethereum_test_tools.vm.opcode import Opcodes as Op -REFERENCE_SPEC_GIT_PATH = "TODO" -REFERENCE_SPEC_VERSION = "TODO" - @pytest.mark.valid_from("Prague") @pytest.mark.parametrize("final_storage_value", [0x02 << 248, 0x02]) @@ -79,7 +76,7 @@ def test_bloatnet( post = {contract_address: Account(storage=storage)} - blockchain_test(pre=pre, blocks=[Block(txs=[tx_0_1, tx_1_2])], post=post) + blockchain_test(pre=pre, blocks=[Block(txs=[tx_0_1]), Block(txs=[tx_1_2])], post=post) # Warm reads are very cheap, which means you can really fill a block From b3aa527c61ec17ae42f9b587a9b2d0fe8160727b Mon Sep 17 00:00:00 2001 From: felipe Date: Tue, 2 Sep 2025 08:59:14 -0600 Subject: [PATCH 23/31] refactor(tests): Proposed patch for bloatnet SSTORE tests (#1) * refactor(tests): Proposed patch for bloatnet SSTORE tests * refactor(tests): Update tests from comments on PR PR: https://github.com/gballet/execution-spec-tests/pull/1/ Signed-off-by: fselmo * Use parametrization of the value that is written to --------- Signed-off-by: fselmo Co-authored-by: Guillaume Ballet <3272758+gballet@users.noreply.github.com> --- tests/benchmark/test_bloatnet.py | 227 ++++++++++++++++++++++++------- 1 file changed, 179 insertions(+), 48 deletions(-) diff --git a/tests/benchmark/test_bloatnet.py b/tests/benchmark/test_bloatnet.py index 92a053fb826..4c9e60474c6 100644 --- a/tests/benchmark/test_bloatnet.py +++ b/tests/benchmark/test_bloatnet.py @@ -20,63 +20,194 @@ @pytest.mark.valid_from("Prague") -@pytest.mark.parametrize("final_storage_value", [0x02 << 248, 0x02]) -def test_bloatnet( - blockchain_test: BlockchainTestFiller, pre: Alloc, fork: Fork, final_storage_value: int +@pytest.mark.parametrize("storage_value", [0x01 << 248, 0x01]) +def test_bloatnet_sstore_0_to_1( + blockchain_test: BlockchainTestFiller, pre: Alloc, fork: Fork, gas_benchmark_value: int, storage_value: int ): """ - A test that calls a contract with many SSTOREs. + Benchmark test that maximizes SSTORE operations (0 -> 1) by filling + a block with multiple transactions, with each one containing a contract + that performs a set of SSTOREs. - The first block will have many SSTORES that go from 0 -> 1 - and the 2nd block will have many SSTORES that go from 1 -> 2 + The test iteratively creates new transactions until the cumulative gas used + reaches the block's gas benchmark value. Each transaction deploys a contract + that performs as many SSTOREs as possible within the transaction's gas limit. """ - # Get gas costs for the current fork gas_costs = fork.gas_costs() - - # this is only used for computing the intinsic gas - data = final_storage_value.to_bytes(32, "big").rstrip(b"\x00") - - storage = Storage() - - # Initial gas for PUSH0 + CALLDATALOAD + POP (at the end) - totalgas = gas_costs.G_BASE * 2 + gas_costs.G_VERY_LOW - totalgas = totalgas + fork.transaction_intrinsic_cost_calculator()(calldata=data) - gas_increment = gas_costs.G_VERY_LOW * 2 + gas_costs.G_STORAGE_SET + gas_costs.G_COLD_SLOAD - sstore_code = Op.PUSH0 + Op.CALLDATALOAD - storage_slot: int = 0 - while totalgas + gas_increment < Environment().gas_limit: - totalgas += gas_increment - sstore_code = sstore_code + Op.SSTORE(storage_slot, Op.DUP1) - storage[storage_slot] = final_storage_value - storage_slot += 1 - - sstore_code = sstore_code + Op.POP # Drop last value on the stack - - sender = pre.fund_eoa() - print(sender) - contract_address = pre.deploy_contract( - code=sstore_code, - storage=Storage(), + intrinsic_gas_calc = fork.transaction_intrinsic_cost_calculator() + + tx_gas_cap = fork.transaction_gas_limit_cap() or gas_benchmark_value + + calldata = storage_value.to_bytes(32, "big").rstrip(b"\x00") + + total_sstores = 0 + total_block_gas_used = 0 + all_txs = [] + + expected_storage_state = {} + + while total_block_gas_used <= gas_benchmark_value: + remaining_block_gas = gas_benchmark_value - total_block_gas_used + tx_gas_limit = min(remaining_block_gas, tx_gas_cap) + + intrinsic_gas_with_data_floor = intrinsic_gas_calc(calldata=calldata) + if tx_gas_limit <= intrinsic_gas_with_data_floor: + break + + opcode_gas_budget = tx_gas_limit - intrinsic_gas_with_data_floor + + # Setup code to load value from calldata + tx_contract_code = Op.PUSH0 + Op.CALLDATALOAD + tx_opcode_gas = gas_costs.G_BASE + gas_costs.G_VERY_LOW # PUSH0 + CALLDATALOAD + + sstore_per_op_cost = ( + gas_costs.G_VERY_LOW * 2 # PUSH + DUP1 + + gas_costs.G_COLD_SLOAD + + gas_costs.G_STORAGE_SET # SSTORE + ) + + tx_sstores_count = (opcode_gas_budget - tx_opcode_gas) // sstore_per_op_cost + + # If no SSTOREs could be added, we've filled the block + if tx_sstores_count == 0: + break + + tx_opcode_gas += sstore_per_op_cost * tx_sstores_count + for slot in range(total_sstores, total_sstores + tx_sstores_count): + tx_contract_code += Op.SSTORE(slot, Op.DUP1) + + contract_address = pre.deploy_contract(code=tx_contract_code) + tx = Transaction( + to=contract_address, + gas_limit=tx_gas_limit, + data=calldata, + sender=pre.fund_eoa(), + ) + all_txs.append(tx) + + actual_intrinsic_consumed = intrinsic_gas_calc( + calldata=calldata, + # The actual gas consumed uses the standard intrinsic cost + # (prior execution), not the floor cost used for validation + return_cost_deducted_prior_execution=True, + ) + + tx_gas_used = actual_intrinsic_consumed + tx_opcode_gas + total_block_gas_used += tx_gas_used + + # update expected storage state for each contract + expected_storage_state[contract_address] = Account( + storage=Storage( + { + HashInt(slot): HashInt(storage_value) + for slot in range(total_sstores, total_sstores + tx_sstores_count) + } + ) + ) + + total_sstores += tx_sstores_count + + blockchain_test( + pre=pre, + blocks=[Block(txs=all_txs)], + post=expected_storage_state, + expected_benchmark_gas_used=total_block_gas_used, ) - tx_0_1 = Transaction( - to=contract_address, - gas_limit=Environment().gas_limit, - data=(final_storage_value // 2).to_bytes(32, "big").rstrip(b"\x00"), - value=0, - sender=sender, - ) - tx_1_2 = Transaction( - to=contract_address, - gas_limit=Environment().gas_limit, - data=final_storage_value.to_bytes(32, "big").rstrip(b"\x00"), - value=0, - sender=sender, - ) - post = {contract_address: Account(storage=storage)} +@pytest.mark.valid_from("Prague") +@pytest.mark.parametrize("final_storage_value", [0x02 << 248, 0x02]) +def test_bloatnet_sstore_1_to_2( + blockchain_test: BlockchainTestFiller, pre: Alloc, fork: Fork, gas_benchmark_value: int, final_storage_value: int +): + """ + Benchmark test that maximizes SSTORE operations (1 -> 2). - blockchain_test(pre=pre, blocks=[Block(txs=[tx_0_1]), Block(txs=[tx_1_2])], post=post) + This test pre-fills storage slots with value=1, then overwrites them with value=2. + This represents the case of changing a non-zero value to a different non-zero value, + """ + gas_costs = fork.gas_costs() + intrinsic_gas_calc = fork.transaction_intrinsic_cost_calculator() + tx_gas_cap = fork.transaction_gas_limit_cap() or gas_benchmark_value + + initial_value = final_storage_value // 2 + calldata = final_storage_value.to_bytes(32, "big").rstrip(b"\x00") + + total_sstores = 0 + total_block_gas_used = 0 + all_txs = [] + expected_storage_state = {} + + while total_block_gas_used <= gas_benchmark_value: + remaining_block_gas = gas_benchmark_value - total_block_gas_used + tx_gas_limit = min(remaining_block_gas, tx_gas_cap) + + intrinsic_gas_with_data_floor = intrinsic_gas_calc(calldata=calldata) + if tx_gas_limit <= intrinsic_gas_with_data_floor: + break + + opcode_gas_budget = tx_gas_limit - intrinsic_gas_with_data_floor + + # Setup code to load value from calldata + tx_contract_code = Op.PUSH0 + Op.CALLDATALOAD + tx_opcode_gas = gas_costs.G_BASE + gas_costs.G_VERY_LOW # PUSH0 + CALLDATALOAD + + sstore_per_op_cost = ( + gas_costs.G_VERY_LOW * 2 # PUSH + DUP1 + + gas_costs.G_COLD_SLOAD + + gas_costs.G_STORAGE_RESET # SSTORE + ) + + tx_sstores_count = (opcode_gas_budget - tx_opcode_gas) // sstore_per_op_cost + + if tx_sstores_count == 0: + break + + tx_opcode_gas += sstore_per_op_cost * tx_sstores_count + for slot in range(total_sstores, total_sstores + tx_sstores_count): + tx_contract_code += Op.SSTORE(slot, Op.DUP1) + + # Pre-fill storage with initial values + initial_storage = { + slot: initial_value for slot in range(total_sstores, total_sstores + tx_sstores_count) + } + + contract_address = pre.deploy_contract( + code=tx_contract_code, + storage=initial_storage, # type: ignore + ) + tx = Transaction( + to=contract_address, + gas_limit=tx_gas_limit, + data=calldata, + sender=pre.fund_eoa(), + ) + all_txs.append(tx) + + actual_intrinsic_consumed = intrinsic_gas_calc( + calldata=calldata, return_cost_deducted_prior_execution=True + ) + + tx_gas_used = actual_intrinsic_consumed + tx_opcode_gas + total_block_gas_used += tx_gas_used + + expected_storage_state[contract_address] = Account( + storage=Storage( + { + HashInt(slot): HashInt(final_storage_value) + for slot in range(total_sstores, total_sstores + tx_sstores_count) + } + ) + ) + + total_sstores += tx_sstores_count + + blockchain_test( + pre=pre, + blocks=[Block(txs=all_txs)], + post=expected_storage_state, + expected_benchmark_gas_used=total_block_gas_used, + ) # Warm reads are very cheap, which means you can really fill a block From add1a36eb666ecaeea5fc3eecb03039f5f993408 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Tue, 2 Sep 2025 11:36:45 -0400 Subject: [PATCH 24/31] mandatory linter issue fix --- tests/benchmark/test_bloatnet.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/benchmark/test_bloatnet.py b/tests/benchmark/test_bloatnet.py index 4c9e60474c6..24a0677c8d9 100644 --- a/tests/benchmark/test_bloatnet.py +++ b/tests/benchmark/test_bloatnet.py @@ -22,7 +22,11 @@ @pytest.mark.valid_from("Prague") @pytest.mark.parametrize("storage_value", [0x01 << 248, 0x01]) def test_bloatnet_sstore_0_to_1( - blockchain_test: BlockchainTestFiller, pre: Alloc, fork: Fork, gas_benchmark_value: int, storage_value: int + blockchain_test: BlockchainTestFiller, + pre: Alloc, + fork: Fork, + gas_benchmark_value: int, + storage_value: int, ): """ Benchmark test that maximizes SSTORE operations (0 -> 1) by filling @@ -118,7 +122,11 @@ def test_bloatnet_sstore_0_to_1( @pytest.mark.valid_from("Prague") @pytest.mark.parametrize("final_storage_value", [0x02 << 248, 0x02]) def test_bloatnet_sstore_1_to_2( - blockchain_test: BlockchainTestFiller, pre: Alloc, fork: Fork, gas_benchmark_value: int, final_storage_value: int + blockchain_test: BlockchainTestFiller, + pre: Alloc, + fork: Fork, + gas_benchmark_value: int, + final_storage_value: int, ): """ Benchmark test that maximizes SSTORE operations (1 -> 2). From 134dc8c87353ab1c842c5ce3e98208c298faafae Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Tue, 2 Sep 2025 15:35:19 -0400 Subject: [PATCH 25/31] fix SLOAD test --- tests/benchmark/test_bloatnet.py | 43 +++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/tests/benchmark/test_bloatnet.py b/tests/benchmark/test_bloatnet.py index 24a0677c8d9..881961d10ab 100644 --- a/tests/benchmark/test_bloatnet.py +++ b/tests/benchmark/test_bloatnet.py @@ -17,6 +17,7 @@ Transaction, ) from ethereum_test_tools.vm.opcode import Opcodes as Op +from ethereum_test_vm import Bytecode @pytest.mark.valid_from("Prague") @@ -224,7 +225,9 @@ def test_bloatnet_sstore_1_to_2( @pytest.mark.valid_from("Prague") -def test_bloatnet_sload_warm(blockchain_test: BlockchainTestFiller, pre: Alloc, fork: Fork): +def test_bloatnet_sload_warm( + blockchain_test: BlockchainTestFiller, pre: Alloc, fork: Fork, gas_benchmark_value: int +): """Test that loads warm storage locations many times.""" gas_costs = fork.gas_costs() @@ -236,19 +239,23 @@ def test_bloatnet_sload_warm(blockchain_test: BlockchainTestFiller, pre: Alloc, totalgas = fork.transaction_intrinsic_cost_calculator()(calldata=b"") # First pass - warm up all slots (cold access) - warmup_gas = num_slots * (gas_costs.G_COLD_SLOAD + gas_costs.G_BASE) + # PUSH + SLOAD + POP for each slot + warmup_gas = num_slots * (gas_costs.G_VERY_LOW + gas_costs.G_COLD_SLOAD + gas_costs.G_BASE) totalgas += warmup_gas # Calculate how many warm loads we can fit - gas_increment = gas_costs.G_WARM_SLOAD + gas_costs.G_BASE # Warm SLOAD + POP - remaining_gas = Environment().gas_limit - totalgas + # PUSH + SLOAD + POP for warm access + gas_increment = gas_costs.G_VERY_LOW + gas_costs.G_WARM_SLOAD + gas_costs.G_BASE + tx_gas_cap = fork.transaction_gas_limit_cap() or gas_benchmark_value + remaining_gas = tx_gas_cap - totalgas num_warm_loads = remaining_gas // (SPEEDUP * gas_increment) # Build the complete code: warmup + repeated warm loads - sload_code = Op.SLOAD(0) + Op.POP if num_slots > 0 else Op.STOP - for i in range(1, num_slots): + sload_code = Bytecode() + for i in range(num_slots): sload_code = sload_code + Op.SLOAD(i) + Op.POP for i in range(num_warm_loads): + totalgas += gas_increment sload_code = sload_code + Op.SLOAD(i % num_slots) + Op.POP sender = pre.fund_eoa() @@ -259,18 +266,22 @@ def test_bloatnet_sload_warm(blockchain_test: BlockchainTestFiller, pre: Alloc, tx = Transaction( to=contract_address, - gas_limit=Environment().gas_limit, + gas_limit=tx_gas_cap, data=b"", value=0, sender=sender, ) post = {contract_address: Account(storage=storage)} - blockchain_test(pre=pre, blocks=[Block(txs=[tx])], post=post) + blockchain_test( + pre=pre, blocks=[Block(txs=[tx])], post=post, expected_benchmark_gas_used=totalgas + ) @pytest.mark.valid_from("Prague") -def test_bloatnet_sload_cold(blockchain_test: BlockchainTestFiller, pre: Alloc, fork: Fork): +def test_bloatnet_sload_cold( + blockchain_test: BlockchainTestFiller, pre: Alloc, fork: Fork, gas_benchmark_value: int +): """Test that loads many different cold storage locations.""" gas_costs = fork.gas_costs() @@ -278,12 +289,14 @@ def test_bloatnet_sload_cold(blockchain_test: BlockchainTestFiller, pre: Alloc, totalgas = fork.transaction_intrinsic_cost_calculator()(calldata=b"") # PUSH + Cold SLOAD + POP gas_increment = gas_costs.G_VERY_LOW + gas_costs.G_COLD_SLOAD + gas_costs.G_BASE - max_slots = (Environment().gas_limit - totalgas) // gas_increment + tx_gas_cap = fork.transaction_gas_limit_cap() or gas_benchmark_value + max_slots = (tx_gas_cap - totalgas) // gas_increment # Build storage and code for all slots storage = Storage({HashInt(i): HashInt(0xC0FFEE + i) for i in range(max_slots)}) - sload_code = Op.SLOAD(0) + Op.POP if max_slots > 0 else Op.STOP - for i in range(1, max_slots): + sload_code = Bytecode() + for i in range(0, max_slots): + totalgas += gas_increment sload_code = sload_code + Op.SLOAD(i) + Op.POP sender = pre.fund_eoa() @@ -294,11 +307,13 @@ def test_bloatnet_sload_cold(blockchain_test: BlockchainTestFiller, pre: Alloc, tx = Transaction( to=contract_address, - gas_limit=Environment().gas_limit, + gas_limit=tx_gas_cap, data=b"", value=0, sender=sender, ) post = {contract_address: Account(storage=storage)} - blockchain_test(pre=pre, blocks=[Block(txs=[tx])], post=post) + blockchain_test( + pre=pre, blocks=[Block(txs=[tx])], post=post, expected_benchmark_gas_used=totalgas + ) From 20c47b72d407bb71227d1fdb0b9ac496c10c0428 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Tue, 2 Sep 2025 15:54:40 -0400 Subject: [PATCH 26/31] fix linter issue --- tests/benchmark/test_bloatnet.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/benchmark/test_bloatnet.py b/tests/benchmark/test_bloatnet.py index 881961d10ab..448df1a9db3 100644 --- a/tests/benchmark/test_bloatnet.py +++ b/tests/benchmark/test_bloatnet.py @@ -12,7 +12,6 @@ Alloc, Block, BlockchainTestFiller, - Environment, Storage, Transaction, ) From f668f86026e935adca094e3fc0b36687ebfd2394 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Tue, 2 Sep 2025 16:04:23 -0400 Subject: [PATCH 27/31] break sload tests in multiple txs --- tests/benchmark/test_bloatnet.py | 148 ++++++++++++++++++++++--------- 1 file changed, 106 insertions(+), 42 deletions(-) diff --git a/tests/benchmark/test_bloatnet.py b/tests/benchmark/test_bloatnet.py index 448df1a9db3..d575d22308c 100644 --- a/tests/benchmark/test_bloatnet.py +++ b/tests/benchmark/test_bloatnet.py @@ -229,51 +229,84 @@ def test_bloatnet_sload_warm( ): """Test that loads warm storage locations many times.""" gas_costs = fork.gas_costs() + intrinsic_gas_calc = fork.transaction_intrinsic_cost_calculator() + tx_gas_cap = fork.transaction_gas_limit_cap() or gas_benchmark_value # Pre-fill storage with values num_slots = 100 # Number of storage slots to warm up storage = Storage({HashInt(i): HashInt(0xDEADBEEF + i) for i in range(num_slots)}) - # Calculate gas costs - totalgas = fork.transaction_intrinsic_cost_calculator()(calldata=b"") + # Gas costs for operations + # PUSH + SLOAD + POP for cold access + cold_sload_cost = gas_costs.G_VERY_LOW + gas_costs.G_COLD_SLOAD + gas_costs.G_BASE + # PUSH + SLOAD + POP for warm access + warm_sload_cost = gas_costs.G_VERY_LOW + gas_costs.G_WARM_SLOAD + gas_costs.G_BASE - # First pass - warm up all slots (cold access) - # PUSH + SLOAD + POP for each slot - warmup_gas = num_slots * (gas_costs.G_VERY_LOW + gas_costs.G_COLD_SLOAD + gas_costs.G_BASE) - totalgas += warmup_gas + # Calculate how many operations fit in a single transaction + intrinsic_gas = intrinsic_gas_calc(calldata=b"") + opcode_gas_budget = tx_gas_cap - intrinsic_gas - # Calculate how many warm loads we can fit - # PUSH + SLOAD + POP for warm access - gas_increment = gas_costs.G_VERY_LOW + gas_costs.G_WARM_SLOAD + gas_costs.G_BASE - tx_gas_cap = fork.transaction_gas_limit_cap() or gas_benchmark_value - remaining_gas = tx_gas_cap - totalgas - num_warm_loads = remaining_gas // (SPEEDUP * gas_increment) + # Calculate max warm loads that can fit in a transaction + # (applying SPEEDUP factor to reduce the number of operations) + num_warm_loads_per_tx = opcode_gas_budget // (SPEEDUP * warm_sload_cost) - # Build the complete code: warmup + repeated warm loads + # Build the code that does warm loads + # Since storage slots are already warmed within each transaction, + # we just need to access them repeatedly sload_code = Bytecode() + # First warm up all slots (these will be cold on first access in each tx) for i in range(num_slots): sload_code = sload_code + Op.SLOAD(i) + Op.POP - for i in range(num_warm_loads): - totalgas += gas_increment + # Then do warm loads + for i in range(num_warm_loads_per_tx): sload_code = sload_code + Op.SLOAD(i % num_slots) + Op.POP - sender = pre.fund_eoa() + # Deploy the contract once contract_address = pre.deploy_contract( code=sload_code, storage=storage, ) - tx = Transaction( - to=contract_address, - gas_limit=tx_gas_cap, - data=b"", - value=0, - sender=sender, - ) + total_block_gas_used = 0 + all_txs = [] + + # Create multiple transactions until we reach gas benchmark + while total_block_gas_used < gas_benchmark_value: + remaining_block_gas = gas_benchmark_value - total_block_gas_used + if remaining_block_gas < intrinsic_gas: + break + + tx_gas_limit = min(remaining_block_gas, tx_gas_cap) + + # Calculate actual operations that will execute + actual_opcode_budget = tx_gas_limit - intrinsic_gas + + # Check if we can at least warm up the slots + warmup_gas = num_slots * cold_sload_cost + if actual_opcode_budget < warmup_gas: + break + + # Calculate how many warm loads we can actually do + remaining_budget = actual_opcode_budget - warmup_gas + actual_warm_loads = min(num_warm_loads_per_tx, remaining_budget // warm_sload_cost) + + tx = Transaction( + to=contract_address, + gas_limit=tx_gas_limit, + data=b"", + sender=pre.fund_eoa(), + ) + all_txs.append(tx) + + tx_gas_used = intrinsic_gas + warmup_gas + (actual_warm_loads * warm_sload_cost) + total_block_gas_used += tx_gas_used post = {contract_address: Account(storage=storage)} blockchain_test( - pre=pre, blocks=[Block(txs=[tx])], post=post, expected_benchmark_gas_used=totalgas + pre=pre, + blocks=[Block(txs=all_txs)], + post=post, + expected_benchmark_gas_used=total_block_gas_used, ) @@ -283,36 +316,67 @@ def test_bloatnet_sload_cold( ): """Test that loads many different cold storage locations.""" gas_costs = fork.gas_costs() + intrinsic_gas_calc = fork.transaction_intrinsic_cost_calculator() + tx_gas_cap = fork.transaction_gas_limit_cap() or gas_benchmark_value - # Calculate gas costs and max slots - totalgas = fork.transaction_intrinsic_cost_calculator()(calldata=b"") # PUSH + Cold SLOAD + POP - gas_increment = gas_costs.G_VERY_LOW + gas_costs.G_COLD_SLOAD + gas_costs.G_BASE - tx_gas_cap = fork.transaction_gas_limit_cap() or gas_benchmark_value - max_slots = (tx_gas_cap - totalgas) // gas_increment + cold_sload_cost = gas_costs.G_VERY_LOW + gas_costs.G_COLD_SLOAD + gas_costs.G_BASE + + # Calculate max slots that fit in a single transaction + intrinsic_gas = intrinsic_gas_calc(calldata=b"") + opcode_gas_budget = tx_gas_cap - intrinsic_gas + max_slots_per_tx = opcode_gas_budget // cold_sload_cost - # Build storage and code for all slots - storage = Storage({HashInt(i): HashInt(0xC0FFEE + i) for i in range(max_slots)}) + # Calculate total slots needed to fill the benchmark + total_max_slots = gas_benchmark_value // cold_sload_cost + + # Build storage for all slots we'll potentially access + storage = Storage({HashInt(i): HashInt(0xC0FFEE + i) for i in range(total_max_slots)}) + + # Build code that loads max_slots_per_tx cold slots sload_code = Bytecode() - for i in range(0, max_slots): - totalgas += gas_increment + for i in range(max_slots_per_tx): sload_code = sload_code + Op.SLOAD(i) + Op.POP - sender = pre.fund_eoa() + # Deploy the contract once contract_address = pre.deploy_contract( code=sload_code, storage=storage, ) - tx = Transaction( - to=contract_address, - gas_limit=tx_gas_cap, - data=b"", - value=0, - sender=sender, - ) + total_block_gas_used = 0 + all_txs = [] + + # Create multiple transactions until we reach gas benchmark + while total_block_gas_used < gas_benchmark_value: + remaining_block_gas = gas_benchmark_value - total_block_gas_used + if remaining_block_gas < intrinsic_gas: + break + + tx_gas_limit = min(remaining_block_gas, tx_gas_cap) + + # Calculate actual slots that will fit + actual_opcode_budget = tx_gas_limit - intrinsic_gas + actual_slots = min(max_slots_per_tx, actual_opcode_budget // cold_sload_cost) + + if actual_slots == 0: + break + + tx = Transaction( + to=contract_address, + gas_limit=tx_gas_limit, + data=b"", + sender=pre.fund_eoa(), + ) + all_txs.append(tx) + + tx_gas_used = intrinsic_gas + (actual_slots * cold_sload_cost) + total_block_gas_used += tx_gas_used post = {contract_address: Account(storage=storage)} blockchain_test( - pre=pre, blocks=[Block(txs=[tx])], post=post, expected_benchmark_gas_used=totalgas + pre=pre, + blocks=[Block(txs=all_txs)], + post=post, + expected_benchmark_gas_used=total_block_gas_used, ) From edfc3817bf6f813cf6c0dc3ecb67f67203087c40 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Wed, 3 Sep 2025 09:02:11 +0200 Subject: [PATCH 28/31] fix doc issue in CI Co-authored-by: felipe --- docs/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 88f9bf4def0..9fc55ccb7c4 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -142,7 +142,7 @@ Users can select any of the artifacts depending on their testing needs for their ### ๐Ÿงช Test Cases -- โœจ [BloatNet](bloatnet.info)/Multidimensional Metering: Add benchmarks to be used as part of the BloatNet project and also for Multidimensional Metering. +- โœจ [BloatNet](https://bloatnet.info)/Multidimensional Metering: Add benchmarks to be used as part of the BloatNet project and also for Multidimensional Metering. - โœจ [EIP-7951](https://eips.ethereum.org/EIPS/eip-7951): Add additional test cases for modular comparison and initcode context ([#2023](https://github.com/ethereum/execution-spec-tests/pull/2023), & [#2068](https://github.com/ethereum/execution-spec-tests/pull/2068)). - ๐Ÿ”€ Refactored `BLOBHASH` opcode context tests to use the `pre_alloc` plugin in order to avoid contract and EOA address collisions ([#1637](https://github.com/ethereum/execution-spec-tests/pull/1637)). - ๐Ÿ”€ Refactored `SELFDESTRUCT` opcode collision tests to use the `pre_alloc` plugin in order to avoid contract and EOA address collisions ([#1643](https://github.com/ethereum/execution-spec-tests/pull/1643)). From 65d500f433f99a6a8458ae5d5030df0dcd4f963c Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Tue, 9 Sep 2025 13:15:47 +0200 Subject: [PATCH 29/31] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: ่”กไฝณ่ช  Louis Tsai <72684086+LouisTsai-Csie@users.noreply.github.com> Co-authored-by: Mario Vega --- tests/benchmark/test_bloatnet.py | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/tests/benchmark/test_bloatnet.py b/tests/benchmark/test_bloatnet.py index d575d22308c..298108b5880 100644 --- a/tests/benchmark/test_bloatnet.py +++ b/tests/benchmark/test_bloatnet.py @@ -253,13 +253,9 @@ def test_bloatnet_sload_warm( # Build the code that does warm loads # Since storage slots are already warmed within each transaction, # we just need to access them repeatedly - sload_code = Bytecode() - # First warm up all slots (these will be cold on first access in each tx) - for i in range(num_slots): - sload_code = sload_code + Op.SLOAD(i) + Op.POP - # Then do warm loads - for i in range(num_warm_loads_per_tx): - sload_code = sload_code + Op.SLOAD(i % num_slots) + Op.POP +sload_code = Bytecode() +sload_code += sum(Op.POP(Op.SLOAD(i)) for i in range(num_slots)) +sload_code += sum(Op.POP(Op.SLOAD(i % num_slots)) for i in range(num_warm_loads_per_tx)) # Deploy the contract once contract_address = pre.deploy_contract( @@ -293,7 +289,6 @@ def test_bloatnet_sload_warm( tx = Transaction( to=contract_address, gas_limit=tx_gas_limit, - data=b"", sender=pre.fund_eoa(), ) all_txs.append(tx) @@ -323,7 +318,7 @@ def test_bloatnet_sload_cold( cold_sload_cost = gas_costs.G_VERY_LOW + gas_costs.G_COLD_SLOAD + gas_costs.G_BASE # Calculate max slots that fit in a single transaction - intrinsic_gas = intrinsic_gas_calc(calldata=b"") + intrinsic_gas = intrinsic_gas_calc() opcode_gas_budget = tx_gas_cap - intrinsic_gas max_slots_per_tx = opcode_gas_budget // cold_sload_cost @@ -334,9 +329,7 @@ def test_bloatnet_sload_cold( storage = Storage({HashInt(i): HashInt(0xC0FFEE + i) for i in range(total_max_slots)}) # Build code that loads max_slots_per_tx cold slots - sload_code = Bytecode() - for i in range(max_slots_per_tx): - sload_code = sload_code + Op.SLOAD(i) + Op.POP + sload_code = sum(Op.POP(Op.SLOAD(i)) for i in range(max_slots_per_tx)) # Deploy the contract once contract_address = pre.deploy_contract( @@ -365,13 +358,15 @@ def test_bloatnet_sload_cold( tx = Transaction( to=contract_address, gas_limit=tx_gas_limit, - data=b"", sender=pre.fund_eoa(), ) all_txs.append(tx) tx_gas_used = intrinsic_gas + (actual_slots * cold_sload_cost) - total_block_gas_used += tx_gas_used + if actual_slots < max_slots_per_tx: + total_block_gas_used += tx_gas_limit + else: + total_block_gas_used += tx_gas_used post = {contract_address: Account(storage=storage)} blockchain_test( From 6df6895467ef5603d67c875bbb551c73737e0d5f Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Tue, 9 Sep 2025 09:24:05 -0400 Subject: [PATCH 30/31] fix linter --- tests/benchmark/test_bloatnet.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/benchmark/test_bloatnet.py b/tests/benchmark/test_bloatnet.py index 298108b5880..e31585a6083 100644 --- a/tests/benchmark/test_bloatnet.py +++ b/tests/benchmark/test_bloatnet.py @@ -253,9 +253,9 @@ def test_bloatnet_sload_warm( # Build the code that does warm loads # Since storage slots are already warmed within each transaction, # we just need to access them repeatedly -sload_code = Bytecode() -sload_code += sum(Op.POP(Op.SLOAD(i)) for i in range(num_slots)) -sload_code += sum(Op.POP(Op.SLOAD(i % num_slots)) for i in range(num_warm_loads_per_tx)) + sload_code = Bytecode() + sload_code += sum(Op.POP(Op.SLOAD(i)) for i in range(num_slots)) + sload_code += sum(Op.POP(Op.SLOAD(i % num_slots)) for i in range(num_warm_loads_per_tx)) # Deploy the contract once contract_address = pre.deploy_contract( From f3f7a0d01deb6410884fed70f2244729f682080f Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Tue, 9 Sep 2025 10:56:32 -0400 Subject: [PATCH 31/31] fix remaining issue --- tests/benchmark/test_bloatnet.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/benchmark/test_bloatnet.py b/tests/benchmark/test_bloatnet.py index e31585a6083..343a05056a4 100644 --- a/tests/benchmark/test_bloatnet.py +++ b/tests/benchmark/test_bloatnet.py @@ -259,7 +259,7 @@ def test_bloatnet_sload_warm( # Deploy the contract once contract_address = pre.deploy_contract( - code=sload_code, + code=bytes(sload_code), storage=storage, ) @@ -333,7 +333,7 @@ def test_bloatnet_sload_cold( # Deploy the contract once contract_address = pre.deploy_contract( - code=sload_code, + code=bytes(sload_code), storage=storage, )