Worked example · 2023-04
Optimism Goerli Testnet Governance Replay — Optimism — 2023-04
Summary
In April 2023, an attacker discovered that a governance transaction deployed and executed on Optimism Goerli (the Optimism testnet) could be replayed on Optimism mainnet. The transaction had been submitted and executed on the Goerli testnet first — a standard deployment workflow where governance proposals are tested on the testnet instance before mainnet execution. However, the transaction's signature was valid on mainnet because the message format lacked sufficient domain separation between the testnet and mainnet instances of the Optimism chain architecture.
The structural condition that enabled the replay was that Optimism Goerli and Optimism mainnet share the same chain ID by design (a common L2 architectural choice where testnet and mainnet are instances of the same rollup stack, differentiated by the L1 anchor rather than by the EIP-155 chain ID). Because the governance transaction's signed message did not include a domain separator that distinguished between testnet and mainnet — and because chain ID was therefore not a distinguishing field — the message's signature was valid in both contexts. The attacker observed the Goerli governance transaction, extracted the signed message, and replayed it on mainnet.
The specific governance transaction replayed was benign — a proposal that had already been approved through Optimism's governance process and was intended for mainnet execution in due course. The attacker effectively front-ran the legitimate mainnet execution by replaying the testnet instance of the transaction. Although the specific exploitation was limited and no user funds were directly at risk, the incident demonstrated a live replay surface between L1 testnet transactions and L1 mainnet that is structurally generalisable: any signed message generated on a testnet instance of a chain whose chain ID is identical to the mainnet instance is potentially replayable on mainnet if the message format lacks a testnet/mainnet domain separator.
The incident was disclosed and investigated by the Optimism team. The root cause was identified as insufficient domain separation in the governance transaction's message format — the message included chain ID (which was identical between Goerli and mainnet) but lacked a testnet/mainnet indicator. The Optimism team patched the message format to include explicit testnet/mainnet domain separation, and the incident was publicly characterised as a near-miss whose architectural lesson generalises beyond Optimism to any L2 architecture where testnet and mainnet share a chain ID.
For OAK's purposes, this incident is the most instructive T10.003 edge case: it demonstrates that chain-ID binding alone does not constitute full message-scope binding when the chain architecture deliberately assigns the same chain ID to multiple instances (testnet and mainnet). The canonical T10.003 message-scope-binding fields (chain ID, instance address, per-instance nonce) must be supplemented with a domain separator when the underlying chain architecture reuses chain ID across instances. This is a T10.003 lesson that the standard framing of "include chain ID in every signed message" does not fully capture — and the Optimism Goerli replay incident is the live production demonstration of that gap.
Timeline (UTC)
| When | Event | OAK ref |
|---|---|---|
| Pre-incident | Optimism deploys governance proposal on Goerli testnet as part of standard governance workflow; the proposal's signed message includes chain ID but no testnet/mainnet domain separator | (standing T10.003 surface) |
| 2023-04 (T+0) | Attacker observes the Goerli governance transaction and extracts the signed message | T10.003 observation |
| same transaction | Attacker replays the signed message on Optimism mainnet; the mainnet governance contract accepts the message because chain ID is identical and no testnet/mainnet domain separator is present in the message format | T10.003 replay execution |
| 2023-04 (within hours) | Optimism team detects unauthorised governance execution on mainnet and investigates | (detection) |
| 2023-04 (within days) | Optimism team confirms the replay surface, patches the message format to include explicit testnet/mainnet domain separation, and publishes disclosure | (remediation + disclosure) |
| Post-incident | Industry absorbs the lesson: chain-ID binding is insufficient for L2 architectures where testnet and mainnet share the same chain ID; testnet/mainnet domain separation is a required message-scope-binding field | (downstream standard) |
Realised extraction
The direct financial extraction from this specific incident was negligible — the replayed transaction was a governance operation, not a value transfer. The incident's extraction profile is at the architectural-lesson layer, not the dollar-extraction layer. The incident's contribution to the T10.003 public record is the live demonstration that (a) testnet-to-mainnet replay is a real, exploitable surface in L2 architectures, and (b) chain-ID binding alone is insufficient when testnet and mainnet share the same chain ID. The lesson has been absorbed into bridge-architecture review methodology: testnet/mainnet domain separation is now a standard message-scope-binding field in bridge-architecture review alongside chain ID, instance address, and per-instance nonce.
References
- Optimism team public disclosure (April 2023) — primary incident disclosure and root-cause analysis.
[zhou2023sok]— academic bridge-security taxonomy; cross-chain replay class characterisation.- Halborn / Trail of Bits bridge-architecture review methodology — message-scope-binding completeness as a standard review item; testnet/mainnet domain separation as a required field in L2 architectures.
- Industry post-incident commentary: the incident was widely discussed in bridge-security and L2-architecture circles as the first live production demonstration of testnet-to-mainnet replay in an L2 architecture; the coverage is primarily in security-researcher Twitter threads and bridge-architecture review guidelines rather than in headline dollar-extraction reporting.
Public references
See citations in corresponding technique file.
Discussion
The Optimism Goerli governance replay incident is structurally important to OAK because it demonstrates a T10.003 edge case that the standard "include chain ID" mitigation does not cover. The conventional T10.003 mitigation — "every signed cross-chain message must include chain ID, instance address, and per-instance nonce" — assumes that chain ID is a distinguishing field across all deployment contexts. In L2 architectures where testnet and mainnet share the same chain ID by design, chain ID provides zero replay protection across the testnet/mainnet boundary. The defender lesson is that message-scope binding must include a domain separator that distinguishes between all deployment contexts where the same chain ID applies — testnet vs mainnet, staging vs production, canary vs stable, and any other deployment-context pair that shares chain ID.
This incident is also the clearest public-record demonstration that testnet-to-mainnet replay is a real, exploitable surface — not a theoretical architecture-review concern. The benign nature of the specific governance transaction replayed (a governance operation rather than a value transfer) makes the case more instructive, not less: it demonstrates that the replay surface exists independent of the attacker's extraction intent. An attacker who discovers the same surface on a bridge contract's withdrawal-authorisation message format would face no additional technical barrier — the same missing-domain-separator condition that enabled governance-transaction replay would enable value-transfer replay.
The incident's placement as the third T10.003 example in OAK complements the existing cohort-level examples: the 2022–2025 cross-chain replay cohort anchors the class-level architecture-review concern, the Ethereum PoW fork replay cohort anchors the chain-fork replay surface, and the Optimism Goerli incident anchors the testnet-to-mainnet replay edge case. Together, the three examples cover the full T10.003 surface: cross-instance replay (cohort), chain-fork replay (ETHPoW), and testnet-to-mainnet replay (Optimism Goerli).
Contributors writing future T10.003 examples should preserve the distinction between the three sub-classes (cross-instance, chain-fork, testnet-to-mainnet) and should document the message-scope-binding fields that were present and absent in each case. The T10.003 lesson — full message-scope binding, including domain separation for deployment contexts that share chain ID — is the same across all three sub-classes; the cases differ in which specific binding field was missing.