47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138 | @EIPChecklist.BlockLevelConstraint.Test.ForkTransition.AcceptedBeforeFork()
@EIPChecklist.BlockLevelConstraint.Test.ForkTransition.AcceptedAfterFork()
@EIPChecklist.BlockLevelConstraint.Test.ForkTransition.RejectedAfterFork()
@pytest.mark.parametrize(
"timestamp,block_gas_limit_delta,block_is_invalid",
[
pytest.param(PRE_FORK_TIMESTAMP, -1, False, id="accepted_before_fork"),
pytest.param(POST_FORK_TIMESTAMP, 0, False, id="accepted_after_fork"),
pytest.param(
POST_FORK_TIMESTAMP,
-1,
True,
marks=pytest.mark.exception_test,
id="rejected_after_fork",
),
],
)
def test_trailing_tx_admission_at_fork_transition(
blockchain_test: BlockchainTestFiller,
pre: Alloc,
fork: TransitionFork,
timestamp: int,
block_gas_limit_delta: int,
block_is_invalid: bool,
) -> None:
"""
Pin the block gas allowance flip across the activation boundary.
The block gas limit leaves room for the trailing transaction only
when the refund is credited back to the accumulator, so the same
shape is accepted before the fork and rejected after it.
"""
sub_fork = fork.fork_at(timestamp=timestamp)
refund_tx = RefundTransaction.build(
fork=sub_fork,
sender=pre.fund_eoa(INITIAL_FUND),
refund_types={RefundTypes.STORAGE_CLEAR},
refunds_count=REFUNDS_COUNT,
)
refund_tx.set_pre(pre)
receipt_gas_used = refund_tx.expected_receipt.gas_used
assert receipt_gas_used is not None
# Without a refund the two accumulators coincide and the boundary is
# unobservable.
assert refund_tx.block_execution() > receipt_gas_used, (
"Parametrization must produce a refund; without one pre- and "
"post-refund accounting agree"
)
stop_address = pre.deterministic_deploy_contract(deploy_code=Op.STOP)
intrinsic_cost_calc = sub_fork.transaction_intrinsic_cost_calculator()
# Slack so a post-refund gate admits the trailing tx and still keeps
# the block within its gas limit, instead of being caught by the
# header gas_used check.
trailing_tx_gas_limit = 2 * intrinsic_cost_calc(calldata=b"")
trailing_tx = Transaction(
to=stop_address,
gas_limit=trailing_tx_gas_limit,
sender=pre.fund_eoa(),
error=TransactionException.GAS_ALLOWANCE_EXCEEDED
if block_is_invalid
else None,
)
# Sized against the post-refund accumulator, which only the
# post-fork rules exceed.
block_gas_limit = (
refund_tx.block_execution()
+ trailing_tx_gas_limit
+ block_gas_limit_delta
)
blockchain_test(
pre=pre,
blocks=[
Block(
timestamp=timestamp,
txs=[refund_tx, trailing_tx],
gas_limit=block_gas_limit,
exception=[
BlockException.GAS_USED_OVERFLOW,
TransactionException.GAS_ALLOWANCE_EXCEEDED,
]
if block_is_invalid
else None,
)
],
post=refund_tx.post(pre, block_is_invalid=block_is_invalid),
genesis_environment=Environment(gas_limit=block_gas_limit),
)
|