-
Notifications
You must be signed in to change notification settings - Fork 392
feat(benchmarks): fix bytecode attack for CALL-like opcodes to work in Osaka #1850
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: forks/osaka
Are you sure you want to change the base?
Changes from all commits
0de5bac
d7ff98f
90d15e1
3aa28b0
d8c8b05
5dee307
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -18,6 +18,7 @@ | |||||||||||||||||||||||||||||||||
| import pytest | ||||||||||||||||||||||||||||||||||
| from execution_testing import ( | ||||||||||||||||||||||||||||||||||
| Account, | ||||||||||||||||||||||||||||||||||
| Address, | ||||||||||||||||||||||||||||||||||
| Alloc, | ||||||||||||||||||||||||||||||||||
| BenchmarkTestFiller, | ||||||||||||||||||||||||||||||||||
| Block, | ||||||||||||||||||||||||||||||||||
|
|
@@ -67,6 +68,10 @@ def test_xcall( | |||||||||||||||||||||||||||||||||
| max_contract_size = fork.max_code_size() | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| gas_costs = fork.gas_costs() | ||||||||||||||||||||||||||||||||||
| tx_gas_limit_cap = fork.transaction_gas_limit_cap() | ||||||||||||||||||||||||||||||||||
| assert tx_gas_limit_cap is not None, ( | ||||||||||||||||||||||||||||||||||
| "This benchmark requires a tx gas limit cap" | ||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| # Calculate the absolute minimum gas costs to deploy the contract This does | ||||||||||||||||||||||||||||||||||
| # not take into account setting up the actual memory (using KECCAK256 and | ||||||||||||||||||||||||||||||||||
|
|
@@ -90,7 +95,7 @@ def test_xcall( | |||||||||||||||||||||||||||||||||
| + gas_costs.G_COLD_ACCOUNT_ACCESS # Opcode cost | ||||||||||||||||||||||||||||||||||
| + 30 # ~Gluing opcodes | ||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||
| # Calculate the number of contracts to be targeted | ||||||||||||||||||||||||||||||||||
| # Calculate an upper bound of the number of contracts to be targeted | ||||||||||||||||||||||||||||||||||
| num_contracts = ( | ||||||||||||||||||||||||||||||||||
| # Base available gas = GAS_LIMIT - intrinsic - (out of loop MSTOREs) | ||||||||||||||||||||||||||||||||||
| attack_gas_limit - intrinsic_gas_cost_calc() - gas_costs.G_VERY_LOW * 4 | ||||||||||||||||||||||||||||||||||
|
|
@@ -108,6 +113,128 @@ def test_xcall( | |||||||||||||||||||||||||||||||||
| "during the setup phase of this test." | ||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| initcode, factory_address, factory_caller_address = ( | ||||||||||||||||||||||||||||||||||
| _deploy_max_contract_factory(pre, fork) | ||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+116
to
+118
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I extracted the deployment of the factory contract to a separate function, mainly to avoid such a big method here which hurts readability, but mostly because I think for the upcoming
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's nice, if you want to share the helper function you could place it under |
||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| # Deploy num_contracts via multiple txs (each capped by tx gas limit). | ||||||||||||||||||||||||||||||||||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the setup phase, we can't create all the required bytecodes in a single tx because this is disallowed by Osaka (unfortuantely for us here). This means we have to split the creation in multiple transactions. This will have an unavoidable hit in filling performance, since we actually used the previous strategy for that reason. For 60M it takes ~2m in my machine, which maybe isn't as bad -- but still I'm not sure we can workaround this with Osaka limitations.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In my personal view, ~2 minutes isn’t too bad for 60M. When I tried this refactoring earlier, the setup + attack phases took more than 20 minutes (I don’t recall the exact number), this makes it very hard to check the result. The recent fill optimizations have already made a huge difference. If this does become a bottleneck again, we can profile the fill process and identify more optimization opportunities! |
||||||||||||||||||||||||||||||||||
| with TestPhaseManager.setup(): | ||||||||||||||||||||||||||||||||||
| # Rough estimate (rounded down) of contracts per tx based on dominant | ||||||||||||||||||||||||||||||||||
| # cost factor only. E.g., 17M gas limit + 24KiB contracts = ~3 per tx. | ||||||||||||||||||||||||||||||||||
| # The goal is to involve the minimum amount of gas pricing to avoid | ||||||||||||||||||||||||||||||||||
| # complexity and potential brittleness. | ||||||||||||||||||||||||||||||||||
| # If this estimation is incorrect in the future (i.e. tx gas limit cap) | ||||||||||||||||||||||||||||||||||
| # is increased or cost per byte, the post-state check will detect it | ||||||||||||||||||||||||||||||||||
| # and can be adjusted with a more complex formula. | ||||||||||||||||||||||||||||||||||
| num_contracts_per_tx = tx_gas_limit_cap // ( | ||||||||||||||||||||||||||||||||||
| gas_costs.G_CODE_DEPOSIT_BYTE * max_contract_size | ||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||
| attack_txs = math.ceil(num_contracts / num_contracts_per_tx) | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| contracts_deployment_txs = [] | ||||||||||||||||||||||||||||||||||
| for _ in range(attack_txs): | ||||||||||||||||||||||||||||||||||
| contracts_deployment_txs.append( | ||||||||||||||||||||||||||||||||||
| Transaction( | ||||||||||||||||||||||||||||||||||
| to=factory_caller_address, | ||||||||||||||||||||||||||||||||||
| gas_limit=tx_gas_limit_cap, | ||||||||||||||||||||||||||||||||||
| gas_price=10**6, | ||||||||||||||||||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
This should be removed. When using |
||||||||||||||||||||||||||||||||||
| data=Hash(num_contracts_per_tx), | ||||||||||||||||||||||||||||||||||
| sender=pre.fund_eoa(), | ||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| post = {} | ||||||||||||||||||||||||||||||||||
| for i in range(num_contracts): | ||||||||||||||||||||||||||||||||||
| deployed_contract_address = compute_create2_address( | ||||||||||||||||||||||||||||||||||
| address=factory_address, | ||||||||||||||||||||||||||||||||||
| salt=i, | ||||||||||||||||||||||||||||||||||
| initcode=initcode, | ||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||
| post[deployed_contract_address] = Account(nonce=1) | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| attack_call = Bytecode() | ||||||||||||||||||||||||||||||||||
| if opcode == Op.EXTCODECOPY: | ||||||||||||||||||||||||||||||||||
| attack_call = Op.EXTCODECOPY( | ||||||||||||||||||||||||||||||||||
| address=Op.SHA3(32 - 20 - 1, 85), dest_offset=96, size=1000 | ||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||||||||||||
| # For the rest of the opcodes, we can use the same generic attack call | ||||||||||||||||||||||||||||||||||
| # since all only minimally need the `address` of the target. | ||||||||||||||||||||||||||||||||||
| attack_call = Op.POP(opcode(address=Op.SHA3(32 - 20 - 1, 85))) | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+146
to
+163
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All this code is the same as before -- just that the diff is a bit unfortunate. Something I noticed is that this has this I'll leave this as is for now, and when I fix
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I didn’t notice this when splitting the tests based on the folder structure. It would be good to remove it once this refactor is complete. |
||||||||||||||||||||||||||||||||||
| attack_code = ( | ||||||||||||||||||||||||||||||||||
| # Setup memory for later CREATE2 address generation loop. | ||||||||||||||||||||||||||||||||||
| # 0xFF+[Address(20bytes)]+[seed(32bytes)]+[initcode keccak(32bytes)] | ||||||||||||||||||||||||||||||||||
| Op.MSTORE(0, factory_address) | ||||||||||||||||||||||||||||||||||
| + Op.MSTORE8(32 - 20 - 1, 0xFF) | ||||||||||||||||||||||||||||||||||
| + Op.MSTORE(32, Op.CALLDATALOAD(0)) | ||||||||||||||||||||||||||||||||||
| + Op.MSTORE(64, initcode.keccak256()) | ||||||||||||||||||||||||||||||||||
| # Main loop | ||||||||||||||||||||||||||||||||||
| + While( | ||||||||||||||||||||||||||||||||||
| body=attack_call + Op.MSTORE(32, Op.ADD(Op.MLOAD(32), 1)), | ||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+164
to
+175
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the same attack code as before but with a twist. The previous version started with Now we have to split the attack in multiple txs, thus I allow reading the seed from CALLDATALOAD(0) so we can create the attack txs in a way that makes sense. |
||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| if len(attack_code) > max_contract_size: | ||||||||||||||||||||||||||||||||||
| # TODO: A workaround could be to split the opcode code into multiple | ||||||||||||||||||||||||||||||||||
| # contracts and call them in sequence. | ||||||||||||||||||||||||||||||||||
| raise ValueError( | ||||||||||||||||||||||||||||||||||
| f"Code size {len(attack_code)} exceeds maximum " | ||||||||||||||||||||||||||||||||||
| f"code size {max_contract_size}" | ||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+177
to
+183
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
This check could be removed (we could leave the Fill Lines 188 to 191 in 9523e75
Execute Lines 325 to 327 in 9523e75
|
||||||||||||||||||||||||||||||||||
| attack_address = pre.deploy_contract(code=attack_code) | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+177
to
+184
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unchanged code. |
||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| with TestPhaseManager.execution(): | ||||||||||||||||||||||||||||||||||
| full_txs = attack_gas_limit // tx_gas_limit_cap | ||||||||||||||||||||||||||||||||||
| remainder = attack_gas_limit % tx_gas_limit_cap | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| num_targeted_contracts_per_full_tx = ( | ||||||||||||||||||||||||||||||||||
| # Base available gas: | ||||||||||||||||||||||||||||||||||
| # TX_GAS_LIMIT - intrinsic - (out of loop MSTOREs) | ||||||||||||||||||||||||||||||||||
| tx_gas_limit_cap | ||||||||||||||||||||||||||||||||||
| - intrinsic_gas_cost_calc() | ||||||||||||||||||||||||||||||||||
| - gas_costs.G_VERY_LOW * 4 | ||||||||||||||||||||||||||||||||||
| ) // loop_cost | ||||||||||||||||||||||||||||||||||
| contract_start_index = 0 | ||||||||||||||||||||||||||||||||||
| opcode_txs = [] | ||||||||||||||||||||||||||||||||||
| for _ in range(full_txs): | ||||||||||||||||||||||||||||||||||
| opcode_txs.append( | ||||||||||||||||||||||||||||||||||
| Transaction( | ||||||||||||||||||||||||||||||||||
| to=attack_address, | ||||||||||||||||||||||||||||||||||
| gas_limit=tx_gas_limit_cap, | ||||||||||||||||||||||||||||||||||
| gas_price=10**9, | ||||||||||||||||||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||||||
| data=Hash(contract_start_index), | ||||||||||||||||||||||||||||||||||
| sender=pre.fund_eoa(), | ||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||
| contract_start_index += num_targeted_contracts_per_full_tx | ||||||||||||||||||||||||||||||||||
| if remainder > 0: | ||||||||||||||||||||||||||||||||||
| opcode_txs.append( | ||||||||||||||||||||||||||||||||||
| Transaction( | ||||||||||||||||||||||||||||||||||
| to=attack_address, | ||||||||||||||||||||||||||||||||||
| gas_limit=remainder, | ||||||||||||||||||||||||||||||||||
| gas_price=10**9, | ||||||||||||||||||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||||||
| data=Hash(contract_start_index), | ||||||||||||||||||||||||||||||||||
| sender=pre.fund_eoa(), | ||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+187
to
+219
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TL;DR: creating the attack txs to execute in the same block, such that none surpas the transaction gas limit cap. In contract_start_index we keep the offset we pass as CALLDATALOAD for the attack contract that I explained in my previous comment.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wanted to mention this (did not check other logic): this looks almost similar to the
BenchmarkTestFiller, however - that split txs would just split the tx (without calldata) up in multiple. Which we cannot do here due to the test logic.@LouisTsai-Csie we could maybe think of adding cases to the split txs logic here? I feel that inserting calldata is something we will see in other benchmark tests often (for instance as a "pointer" to where a certain tx should pick up where the other tx has exited - we could also do this in the contract code itself, but this means spending gas on this logic, which we want to spend on the target (the scenario/opcode we are benching))
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, this is a very common pattern in stateful scenarios as well. I need to think about it a bit more, maybe a new code generator type could help. |
||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| blockchain_test( | ||||||||||||||||||||||||||||||||||
| pre=pre, | ||||||||||||||||||||||||||||||||||
| post=post, | ||||||||||||||||||||||||||||||||||
| blocks=[ | ||||||||||||||||||||||||||||||||||
| Block(txs=contracts_deployment_txs), | ||||||||||||||||||||||||||||||||||
| Block(txs=opcode_txs), | ||||||||||||||||||||||||||||||||||
| ], | ||||||||||||||||||||||||||||||||||
| exclude_full_post_state_in_output=True, | ||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| def _deploy_max_contract_factory( | ||||||||||||||||||||||||||||||||||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The way the contract factory was created remains unchanged. Note that the |
||||||||||||||||||||||||||||||||||
| pre: Alloc, | ||||||||||||||||||||||||||||||||||
| fork: Fork, | ||||||||||||||||||||||||||||||||||
| ) -> tuple[Bytecode, Address, Address]: | ||||||||||||||||||||||||||||||||||
| max_contract_size = fork.max_code_size() | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| # The initcode will take its address as a starting point to the input to | ||||||||||||||||||||||||||||||||||
| # the keccak hash function. It will reuse the output of the hash function | ||||||||||||||||||||||||||||||||||
| # in a loop to create a large amount of seemingly random code, until it | ||||||||||||||||||||||||||||||||||
|
|
@@ -177,74 +304,7 @@ def test_xcall( | |||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||
| factory_caller_address = pre.deploy_contract(code=factory_caller_code) | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| with TestPhaseManager.setup(): | ||||||||||||||||||||||||||||||||||
| contracts_deployment_tx = Transaction( | ||||||||||||||||||||||||||||||||||
| to=factory_caller_address, | ||||||||||||||||||||||||||||||||||
| gas_limit=env.gas_limit, | ||||||||||||||||||||||||||||||||||
| gas_price=10**6, | ||||||||||||||||||||||||||||||||||
| data=Hash(num_contracts), | ||||||||||||||||||||||||||||||||||
| sender=pre.fund_eoa(), | ||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| post = {} | ||||||||||||||||||||||||||||||||||
| deployed_contract_addresses = [] | ||||||||||||||||||||||||||||||||||
| for i in range(num_contracts): | ||||||||||||||||||||||||||||||||||
| deployed_contract_address = compute_create2_address( | ||||||||||||||||||||||||||||||||||
| address=factory_address, | ||||||||||||||||||||||||||||||||||
| salt=i, | ||||||||||||||||||||||||||||||||||
| initcode=initcode, | ||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||
| post[deployed_contract_address] = Account(nonce=1) | ||||||||||||||||||||||||||||||||||
| deployed_contract_addresses.append(deployed_contract_address) | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| attack_call = Bytecode() | ||||||||||||||||||||||||||||||||||
| if opcode == Op.EXTCODECOPY: | ||||||||||||||||||||||||||||||||||
| attack_call = Op.EXTCODECOPY( | ||||||||||||||||||||||||||||||||||
| address=Op.SHA3(32 - 20 - 1, 85), dest_offset=96, size=1000 | ||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||||||||||||
| # For the rest of the opcodes, we can use the same generic attack call | ||||||||||||||||||||||||||||||||||
| # since all only minimally need the `address` of the target. | ||||||||||||||||||||||||||||||||||
| attack_call = Op.POP(opcode(address=Op.SHA3(32 - 20 - 1, 85))) | ||||||||||||||||||||||||||||||||||
| attack_code = ( | ||||||||||||||||||||||||||||||||||
| # Setup memory for later CREATE2 address generation loop. | ||||||||||||||||||||||||||||||||||
| # 0xFF+[Address(20bytes)]+[seed(32bytes)]+[initcode keccak(32bytes)] | ||||||||||||||||||||||||||||||||||
| Op.MSTORE(0, factory_address) | ||||||||||||||||||||||||||||||||||
| + Op.MSTORE8(32 - 20 - 1, 0xFF) | ||||||||||||||||||||||||||||||||||
| + Op.MSTORE(32, 0) | ||||||||||||||||||||||||||||||||||
| + Op.MSTORE(64, initcode.keccak256()) | ||||||||||||||||||||||||||||||||||
| # Main loop | ||||||||||||||||||||||||||||||||||
| + While( | ||||||||||||||||||||||||||||||||||
| body=attack_call + Op.MSTORE(32, Op.ADD(Op.MLOAD(32), 1)), | ||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| if len(attack_code) > max_contract_size: | ||||||||||||||||||||||||||||||||||
| # TODO: A workaround could be to split the opcode code into multiple | ||||||||||||||||||||||||||||||||||
| # contracts and call them in sequence. | ||||||||||||||||||||||||||||||||||
| raise ValueError( | ||||||||||||||||||||||||||||||||||
| f"Code size {len(attack_code)} exceeds maximum " | ||||||||||||||||||||||||||||||||||
| f"code size {max_contract_size}" | ||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||
| opcode_address = pre.deploy_contract(code=attack_code) | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| with TestPhaseManager.execution(): | ||||||||||||||||||||||||||||||||||
| opcode_tx = Transaction( | ||||||||||||||||||||||||||||||||||
| to=opcode_address, | ||||||||||||||||||||||||||||||||||
| gas_limit=attack_gas_limit, | ||||||||||||||||||||||||||||||||||
| gas_price=10**9, | ||||||||||||||||||||||||||||||||||
| sender=pre.fund_eoa(), | ||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| blockchain_test( | ||||||||||||||||||||||||||||||||||
| pre=pre, | ||||||||||||||||||||||||||||||||||
| post=post, | ||||||||||||||||||||||||||||||||||
| blocks=[ | ||||||||||||||||||||||||||||||||||
| Block(txs=[contracts_deployment_tx]), | ||||||||||||||||||||||||||||||||||
| Block(txs=[opcode_tx]), | ||||||||||||||||||||||||||||||||||
| ], | ||||||||||||||||||||||||||||||||||
| exclude_full_post_state_in_output=True, | ||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||
| return initcode, factory_address, factory_caller_address | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| @pytest.mark.parametrize( | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we mark this test for Osaka forward only? (Or maybe that already happens since AFAIK in all the benchmark releases only Prague was filled, since I'm not interested we want backwards performance runs. For zkVM also makes sense that previous forks aren't interesting for this attack).
I think we could make it work backwards, it would just blow up a the logic below reg tx preparations for setup and attack. This test is already quite complex so not sure it is worth it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this should be sufficient to support both pre-Osaka and post-Osaka scenarios. If
fork.transaction_gas_limit_cap()returnsNone(e.g., in Prague), it will fall back togas_benchmark_value, and the transaction preparation logic will result in a single transaction: