@pytest.mark.parametrize(
"deposit_count",
[
pytest.param(
Spec.MAX_DEPOSIT_REQUESTS_PER_PAYLOAD + 1,
id="over_consensus_layer_payload_maximum",
),
],
)
@pytest.mark.slow()
def test_deposit_high_count(
blockchain_test: BlockchainTestFiller,
pre: Alloc,
fork: Fork,
deposit_count: int,
) -> None:
"""
Test a single block carrying more deposits than a consensus layer payload
is allowed to contain, since EIP-6110 leaves the execution layer request
list unbounded.
The deposits are driven by a relay contract that loops over one deposit
call, so they only differ in the index the deposit contract assigns them.
"""
deposit = DepositRequest(
pubkey=0x01,
withdrawal_credentials=0x02,
amount=Spec.MIN_DEPOSIT_AMOUNT,
signature=0x03,
index=0x0,
)
sender = pre.fund_eoa()
relay_setup_code = Op.CALLDATACOPY(
0, 32, len(deposit.calldata)
) + Op.CALLDATALOAD(0)
relay_loop_code = While(
body=Op.POP(
Op.CALL(
address=deposit.system_contract_address,
value=deposit.value,
args_offset=0,
args_size=len(deposit.calldata),
value_transfer=True,
address_warm=True,
)
),
condition=Op.PUSH1(1) + Op.SWAP1 + Op.SUB + Op.DUP1,
)
deposit_relay_code = relay_setup_code + relay_loop_code
deposit_relay_iteration_gas = relay_loop_code.gas_cost(fork)
relay_contract = pre.deploy_contract(
code=deposit_relay_code,
balance=deposit.value * deposit_count,
)
intrinsic_gas_calculator = fork.transaction_intrinsic_cost_calculator()
# The deposit contract's Merkle branch loop runs once per trailing zero
# bit of the new deposit count, so no deposit iterates it more often than
# the count's bit length. Budgeting that depth for every deposit, rather
# than the single iteration they average, leaves room for the storage
# slots each transaction writes for the first time.
gas_per_deposit = (
deposit_relay_iteration_gas
+ deposit_contract_execution_gas(
fork, branch_updates=deposit_count.bit_length()
)
)
deposits_per_transaction = deposit_count
gas_limit_cap = fork.transaction_gas_limit_cap()
if gas_limit_cap is not None:
deposits_per_transaction = (
gas_limit_cap
- intrinsic_gas_calculator(
calldata=Hash(deposit_count) + deposit.calldata
)
- relay_setup_code.gas_cost(fork)
) // gas_per_deposit
txs: List[Transaction] = []
for start in range(0, deposit_count, deposits_per_transaction):
count = min(deposits_per_transaction, deposit_count - start)
data = Hash(count) + deposit.calldata
txs.append(
Transaction(
sender=sender,
to=relay_contract,
data=data,
gas_limit=intrinsic_gas_calculator(calldata=data)
+ (count * gas_per_deposit),
)
)
blockchain_test(
genesis_environment=Environment(
gas_limit=sum(int(tx.gas_limit) for tx in txs)
),
pre=pre,
post={relay_contract: Account(balance=0)},
blocks=[
Block(
txs=txs,
header_verify=Header(
requests_hash=Requests(
*[
deposit.copy(index=index)
for index in range(deposit_count)
]
),
),
# Constrain fixture size.
include_receipts_in_output=False,
)
],
)