apollo_consensus_orchestrator,apollo_versioned_constants: cap the L2 gas price at 10x the minimum - #14946
Open
asaf-sw wants to merge 1 commit into
Open
apollo_consensus_orchestrator,apollo_versioned_constants: cap the L2 gas price at 10x the minimum#14946asaf-sw wants to merge 1 commit into
asaf-sw wants to merge 1 commit into
Conversation
…gas price at 10x the minimum A broken or malicious STRK/USD oracle reporting STRK far too cheap drives the SNIP-35 fee target arbitrarily high, and sustained congestion drives the EIP-1559 base price up ~9.5% per full block. Neither had a ceiling. Cap the published L2 gas price and the SNIP-35 fee_proposal band at max_gas_price_multiplier (10) times the minimum in force for the height, gated on starknet_version >= V0_14_4 so proposer and validator switch together. The ceiling caps both edges of the fee_proposal band, not just the upper one, so an honest proposal pinned to the ceiling is still accepted when fee_actual sits above it. Both sides read the gate from the block's own starknet_version: the proposer reads it once per proposal and threads it into the init, the band and the fin, and the validator takes it from the init it is checking. A process whose effective version differs from LATEST therefore cannot publish a proposal its peers reject. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
Author
This stack of pull requests is managed by Graphite. Learn more about stacking. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Consensus-critical. Off
main, not in the L1 oracle stack, but motivated by it.Requirement
From Ohad Barta: cap the L2 gas price in both directions, to
[minimum, 10x minimum], so a malicious or broken STRK/USD oracle cannot make the network unusable. His words: "I don't want a malicious oracle to be able to stop Starknet by causing everyone to pay $1000s on a transfer." A floor exists today; there was no ceiling anywhere.Two cap sites, because one is not enough
Both were needed, and the trace is the interesting part:
calculate_next_l2_gas_price_for_fin→calculate_next_base_gas_price) is pure EIP-1559 withmax(adjusted, min)and no ceiling. At the shipped constants the growth factor is 1.0954/block, so sustained full blocks reach 10x the minimum in 26 blocks (~68 s) with no oracle involvement at all. Capping only the SNIP-35 band would have left the requirement unmet.fee_proposal_bounds) still needs capping, becausefee_actualenters the fin path as a floor (effective_min = max(config_min, fee_actual)). An uncapped band lets the oracle push the floor above the ceiling. Capping there also gets the validator-side rejection free, since proposer and validator share that call site.max_gas_price_multiplier: 10is a versioned constant, gated onstarknet_version >= V0_14_4, so replaying 0.14.3-and-earlier blocks is bit-identical.Liveness: the cap must clamp the whole band, not just its top
upper = min(upper, cap)alone is a halt. If STRK genuinely collapses more than 10x,fee_actuallegitimately rises above the cap, an honest proposer publishescap, and the validator computeslower = fee_actual * 1000/1002which is now abovecap— so the honest proposal is rejected and blocks stop. Both edges are clamped, so the band collapses to the single pointcap, every proposal is exactlycap, and withinfee_proposal_window_sizeblocks every window entry is<= cap, hencefee_actual <= capand the band reopens.(Note the convergence argument is not median monotonicity, which is false: window
[1,300,300,300,300,300,1,1,1,1]with cap 100 has the median rise 150 → 200. The correct argument is thatupper <= capbounds every accepted proposal, so the window drains to<= capregardless.)Review also caught that
compute_fee_proposalreturnedfee_actualunclamped on the oracle-failure path. Harmless before a cap existed; a halt after it, on every oracle failure while the price is pressed against the ceiling. Both paths now go through the same clamp.Version skew
Gated on the block's
starknet_version, threaded explicitly so init and fin cannot disagree. Review found the gate was originally read through three different expressions (effective_starknet_version()on the proposer,StarknetVersion::LATESTon the validator, the block's version in the fin) — benign only because those coincide today, and a divergence the momentLATESTmoves pastV0_14_4. All three now use the block's version.validate_proposal.rsrejects a version mismatch before the band check, so both sides are always on the same side of the gate for a given block.Metrics for the forthcoming alerts
Ohad wants alerts on the calculation drifting outside the cap, not on the published price sitting at a bound (once capped, the published value carries no signal). Two counters, deliberately not gauges:
CONSENSUS_L2_GAS_PRICE_CLAMPED(labeledminimum/maximum) andSNIP35_FEE_TARGET_ABOVE_MAXIMUM.Counters because a registered-but-unset gauge renders as
0in the Prometheus exporter, so a "below minimum" alert on a fresh gauge fires on every pod restart. A counter's0is instead semantically true, andincrease(metric[window]) > 0is defined across resets.Testing
218 → 248 tests. Verified by mutation: replacing
effective_min = min(snip35_min, cap)witheffective_min = snip35_minpreviously left all 218 tests passing (its only observable effect is the clamp counter, and nothing asserted it). It now fails on thesnip35_floor_above_the_ceilingcase with the exact predicted miscount.override_l2_gas_price_friBypasses the published-price cap (matching how it already bypasses the floor, so Echonet can replay a mainnet block's own price), but not the fee-proposal band cap (the validator has no knowledge of the proposer's override config, so the band cannot depend on it).
Worth a separate discussion, not changed here
The ceiling derives from
min_l2_gas_price_per_height, which is per-deployment config. Config drift is already fatal today whenever the floor binds — which is the steady state on a quiet chain — so this adds no new operational requirement. What it adds is a second binding regime and a config dependence in the fee-proposal band, which was previously config-independent. Folding the configured minimums intoversion_constant_commitmentwould convert a latent trap into a loud init rejection; worth filing separately.Also worth flagging as a policy consequence rather than a defect: capping the published price means sustained congestion stops being price-rationed after ~1 minute. That is inherent to "cap the price in both directions" and may warrant a larger multiplier.
🤖 Generated with Claude Code