Skip to content

test_multi_block_observed_coinbase_balance()

Documentation for tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_multi_block.py::test_multi_block_observed_coinbase_balance@c74f1a67.

Generate fixtures for these test cases for Amsterdam with:

fill -v tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_multi_block.py::test_multi_block_observed_coinbase_balance --fork Amsterdam

Observe coinbase balance between state-creating transactions.

A reporter contract reads BALANCE(COINBASE) and stores it. This makes receipt_gas_used directly observable: if a client computes a different receipt_gas_used for prior transactions, the stored balance will differ and the state root will not match.

Block 1

Tx 1: SSTORE zero-to-nonzero (coinbase earns fee). Tx 2: Store BALANCE(COINBASE) in slot 0.

Block 2

Tx 3: Child spills state gas then reverts; parent SSTOREs (coinbase earns fee through different code path). Tx 4: Store BALANCE(COINBASE) in slot 0.

Source code in tests/amsterdam/eip8037_state_creation_gas_cost_increase/test_state_gas_multi_block.py
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
@pytest.mark.valid_from("EIP8037")
def test_multi_block_observed_coinbase_balance(
    blockchain_test: BlockchainTestFiller,
    pre: Alloc,
    fork: Fork,
) -> None:
    """
    Observe coinbase balance between state-creating transactions.

    A reporter contract reads `BALANCE(COINBASE)` and stores it.
    This makes `receipt_gas_used` directly observable: if a client
    computes a different `receipt_gas_used` for prior transactions,
    the stored balance will differ and the state root will not match.

    Block 1:
      Tx 1: SSTORE zero-to-nonzero (coinbase earns fee).
      Tx 2: Store `BALANCE(COINBASE)` in slot 0.

    Block 2:
      Tx 3: Child spills state gas then reverts; parent SSTOREs
      (coinbase earns fee through different code path).
      Tx 4: Store `BALANCE(COINBASE)` in slot 0.
    """
    sstore_state_gas = Op.SSTORE(new_value=1).state_cost(fork)

    reporter1 = pre.deploy_contract(
        code=(Op.SSTORE(0, Op.BALANCE(Op.COINBASE))),
    )
    reporter2 = pre.deploy_contract(
        code=(Op.SSTORE(0, Op.BALANCE(Op.COINBASE))),
    )

    # Block 1 tx 1: simple SSTORE
    sstore_storage = Storage()
    sstore_contract = pre.deploy_contract(
        code=(Op.SSTORE(sstore_storage.store_next(1), 1)),
    )

    # Block 2 tx 3: child spill + revert, parent SSTORE
    reverting_child = pre.deploy_contract(
        code=(Op.SSTORE(0, 1) + Op.SSTORE(1, 1) + Op.REVERT(0, 0)),
    )
    spill_storage = Storage()
    spill_parent = pre.deploy_contract(
        code=(
            Op.POP(Op.CALL(gas=500_000, address=reverting_child))
            + Op.SSTORE(spill_storage.store_next(1), 1)
        ),
    )

    blocks = [
        Block(
            txs=[
                Transaction(
                    to=sstore_contract,
                    state_gas_reservoir=sstore_state_gas,
                    max_priority_fee_per_gas=1,
                    max_fee_per_gas=8,
                    sender=pre.fund_eoa(),
                ),
                Transaction(
                    to=reporter1,
                    state_gas_reservoir=0,
                    max_priority_fee_per_gas=1,
                    max_fee_per_gas=8,
                    sender=pre.fund_eoa(),
                ),
            ]
        ),
        Block(
            txs=[
                Transaction(
                    to=spill_parent,
                    state_gas_reservoir=sstore_state_gas,
                    max_priority_fee_per_gas=1,
                    max_fee_per_gas=8,
                    sender=pre.fund_eoa(),
                ),
                Transaction(
                    to=reporter2,
                    state_gas_reservoir=0,
                    max_priority_fee_per_gas=1,
                    max_fee_per_gas=8,
                    sender=pre.fund_eoa(),
                ),
            ]
        ),
    ]

    post = {
        sstore_contract: Account(storage=sstore_storage),
        spill_parent: Account(storage=spill_storage),
    }
    blockchain_test(pre=pre, blocks=blocks, post=post)

Parametrized Test Cases

This test generates 1 parametrized test case across 1 fork.