Summary
Over the last few weeks we shipped two coordinated hardforks across the
Polygon PoS clients: Austin HF on Bor (v2.10.0) and Kyoto HF on
Heimdall (v0.11.0). Both address security and liveness issues. As is
standard practice for consensus-affecting fixes, they were first rolled out
privately and validated on Amoy before mainnet activation, and made public
once the fleet was safe.
This post summarizes what each hardfork fixed, what the impact could have
been, and what node operators need to do. Both hardforks are already active
on Amoy and mainnet — if you haven’t upgraded past the activation heights
below, your node has already forked off canonical and needs to catch up.
Bor v2.10.0 — Austin hardfork
Two changes, both closing denial-of-service paths in block processing.
-
Per-block state-sync gas bound. State-sync events (L1→L2 bridge
deposits) execute contract code and precompiles just like ordinary
transactions, but the gas they consume wasn’t metered against a hard cap.
A block carrying enough state-sync events, or one especially expensive
event, could make block processing take long enough to transiently stall
the chain. Austin adds an explicit per-block bound on state-sync gas,
matching the treatment normal transaction gas already gets. -
Removal of the
TxDependencywire field. Bor used this block
extra-data field as a hint for parallel execution — which transactions in
a block don’t conflict with each other. It carried no size limit, so a
block producer could include an arbitrarily largeTxDependencyblob in
an otherwise valid sibling block, and any peer receiving it would crash
trying to process it. Austin removes the field from the wire format
entirely; parallel execution doesn’t depend on peers trusting a
producer’s hint to work correctly, so nothing downstream is affected.
Activation:
- Amoy: block
44,120,000 - Mainnet: block
91,949,700
Impact if unfixed: both were resource-exhaustion / denial-of-service
vectors against block processing, not consensus-correctness issues. Neither
was observed causing disruption on mainnet — they were addressed
proactively ahead of any exploitation.
Heimdall v0.11.0 — Kyoto hardfork
Kyoto bundles a set of consensus-hardening and input-validation fixes
across ABCI, milestone, bor, stake, topup, clerk, and bridge processing.
-
Bound on nested
google.protobuf.Anydepth in transactions. Heimdall
transactions wrap their inner messages inAnytypes, andAnycan nest
insideAny. Without a cap, a single crafted transaction with deeply
nestedAnyfields could force every validator to do a large amount of
decode work at once — cheap for the sender to construct, expensive for
the whole network to process. Kyoto adds a byte-level pre-scan that
rejects a transaction once its nesting passes a threshold, enforced
identically at mempool admission (CheckTx) and on the consensus path
(ProcessProposal), so the same transaction can’t be admitted on one
path and rejected on another. The threshold sits above anything a
legitimate transaction would ever produce. -
Fee-coin count cap. A transaction could previously declare an
unbounded list of fee coins, and the validation scan over that list was
O(n). Kyoto caps the count before the scan runs. -
Checkpoint signature recovery-byte normalization. A checkpoint
signature’s recovery byte wasn’t canonicalized before L1 submission, so a
validly-signed checkpoint could be submitted in a form that fails
signature recovery on L1 — stalling checkpoint anchoring without forging
anything. -
Idempotent producer-downtime handling. Processing the same downtime
message twice used to create a second span each time; it’s now a no-op
on repeat. -
Milestone range voting bound to the signed parent hash. A
validator’s vote could previously count toward a milestone range
extension without having attested to the specific parent it extended
from. -
Checkpoint-window continuity check before the Bor RPC call in
vote-extension validation, closing a gap where a discontinuous checkpoint
window could reach that call in an unexpected state. -
Non-halting future-span creation. A failed future-span creation no
longer blocks milestone commit — it degrades and retries at the next
boundary instead. -
Injective L1-event replay keys for topup, clerk, and stake, for
out-of-range log indexes — closes a narrow case where two distinct L1
events could hash to the same replay key and one would silently shadow
the other.
All Kyoto-gated behavior is inert below the fork height; the guards only
fire on the crafted inputs they’re designed to catch, and normal traffic is
unaffected.
Activation:
- Amoy: height
42,252,000 - Mainnet: height
51,533,000
Impact if unfixed: the nested-Any decode bound closes the most
severe issue in this batch — a permissionless way to force costly, correlated
work across the whole validator set. The remaining items harden checkpoint
finality, milestone accounting, and L1-event replay against edge cases and
resource pressure. All were resolved and validated before either hardfork
activated.
What to upgrade
| Release | Who needs it | Urgency |
|---|---|---|
| Bor v2.10.0 (Austin) | All nodes | Mandatory — already active on Amoy and mainnet |
| Heimdall v0.11.0 (Kyoto) | All validators and full nodes | Mandatory — already active on Amoy and mainnet |
Both are plain binary upgrades — no state migration, no genesis change, no
resync required. If your node is on an earlier version past the activation
heights above, it has already fallen out of consensus; upgrading and rolling back
to pre-HFs heights lets it resync and rejoin.