@pytest.mark.parametrize(
"beyond_target",
[
pytest.param(False, id="nothing_queued"),
pytest.param(True, id="queued_beyond_target"),
],
)
@EIPChecklist.SystemContract.Test.InputLengths.Zero()
def test_fee_getter(
blockchain_test: BlockchainTestFiller,
pre: Alloc,
request_class: Type[FeeSystemContractRequest],
beyond_target: bool,
) -> None:
"""
A relay queues requests and then calls the predeploy with empty calldata,
which returns the current fee; calling it again with value attached
reverts.
"""
predeploy = request_class.system_contract_address
per_block = request_class.max_per_block
if beyond_target:
excess = request_class.get_n_fee_increments(1)[0]
queued = request_class.target_per_block + excess
sweep_changes = [
BalStorageSlot(
slot=request_class.excess_slot,
slot_changes=[
BalStorageChange(block_access_index=2, post_value=excess)
],
),
queued_count_changes(request_class, queued, 2),
]
if queued > per_block:
# The sweep takes a block's worth and moves the head past them.
sweep_changes.append(
BalStorageSlot(
slot=request_class.queue_head_slot,
slot_changes=[
BalStorageChange(
block_access_index=2, post_value=per_block
)
],
)
)
predeploy_bal = BalAccountExpectation(
storage_changes=sweep_changes
)
else:
predeploy_bal = BalAccountExpectation(
storage_reads=[request_class.queue_head_slot],
storage_changes=sweep_changes,
)
else:
excess = 0
queued = 0
predeploy_bal = BalAccountExpectation(
storage_reads=[
request_class.excess_slot,
request_class.count_slot,
request_class.queue_head_slot,
request_class.queue_tail_slot,
],
storage_changes=[],
)
requests = [
request_class.from_index(i).copy(fee=fee)
for i, fee in enumerate(request_class.get_enqueue_fees(queued))
]
queued_value = sum(request.value for request in requests)
# Per-call pricing already counts the requests queued this block;
# per-block pricing returns the minimum until the system call runs.
if request_class.excess_fee_processing == "call":
quoted_fee = request_class.get_fee(excess)
elif request_class.excess_fee_processing == "block":
quoted_fee = request_class.get_fee(0)
else:
raise ValueError(
f"unhandled fee processing {request_class.excess_fee_processing}"
)
storage = Storage()
fee_getter = (
Op.SSTORE(
storage.store_next(1, "getter_success"),
Op.CALL(Op.GAS, predeploy, 0, 0, 0, 0, 32),
)
+ Op.SSTORE(
storage.store_next(32, "getter_return_size"), Op.RETURNDATASIZE
)
+ Op.SSTORE(storage.store_next(quoted_fee, "fee"), Op.MLOAD(0))
+ Op.SSTORE(
storage.store_next(0, "getter_with_value_success"),
Op.CALL(Op.GAS, predeploy, 1, 0, 0, 0, 0),
)
)
relay = pre.deploy_contract(
relay_contract_code(
requests, call_type=Op.CALL, extra_code=fee_getter
),
balance=queued_value + 1,
)
tx = Transaction(
sender=pre.fund_eoa(),
to=relay,
data=b"".join(request.calldata for request in requests),
)
blockchain_test(
pre=pre,
blocks=[
Block(
txs=[tx],
header_verify=Header(
requests_hash=Requests(
*(
r.with_source_address(relay)
for r in requests[:per_block]
)
)
),
expected_block_access_list=BlockAccessListExpectation(
account_expectations={
relay: BalAccountExpectation(
storage_changes=[
BalStorageSlot(
slot=slot,
slot_changes=[
BalStorageChange(
block_access_index=1,
post_value=value,
)
],
)
for slot, value in storage.root.items()
if value != 0
],
),
predeploy: predeploy_bal,
}
),
)
],
post={
relay: Account(storage=storage, balance=1),
predeploy: Account(balance=queued_value),
},
)