diff --git a/specs/protocol/l2-contract-upgrades.md b/specs/protocol/l2-contract-upgrades.md new file mode 100644 index 000000000..63199f4d7 --- /dev/null +++ b/specs/protocol/l2-contract-upgrades.md @@ -0,0 +1,155 @@ +# L2 Contract Upgrades + + + +**Table of Contents** + +- [Overview](#overview) +- [ConditionalDeployer](#conditionaldeployer) + - [Overview](#overview-1) + - [Definitions](#definitions) + - [CREATE2 Collision](#create2-collision) + - [Deterministic Deployment Proxy](#deterministic-deployment-proxy) + - [Assumptions](#assumptions) + - [a01-001: Deterministic Deployment Proxy is Available and Correct](#a01-001-deterministic-deployment-proxy-is-available-and-correct) + - [Mitigations](#mitigations) + - [a02-002: Initcode is Well-Formed](#a02-002-initcode-is-well-formed) + - [Mitigations](#mitigations-1) + - [Invariants](#invariants) + - [i01-001: Deterministic Address Derivation](#i01-001-deterministic-address-derivation) + - [Impact](#impact) + - [i02-002: Idempotent Deployment Operations](#i02-002-idempotent-deployment-operations) + - [Impact](#impact-1) + - [i03-003: Non-Reverting Collision Handling](#i03-003-non-reverting-collision-handling) + - [Impact](#impact-2) + - [i04-004: Collision Detection Accuracy](#i04-004-collision-detection-accuracy) + - [Impact](#impact-3) + + + +## Overview + +This specification defines the mechanism for upgrading L2 predeploy contracts through deterministic, hard fork-driven +Network Upgrade Transactions (NUTs). The system enables safe, well-tested upgrades of L2 contracts with both +implementation and upgrade paths written in Solidity, ensuring determinism, verifiability, and testability across all +client implementations. + +The upgrade system maintains the existing pattern of injecting Network Upgrade Transactions at specific fork block +heights while improving the development and testing process. Upgrade transactions are defined in JSON bundles that are +tracked in git, generated from Solidity scripts, and executed deterministically at fork activation. + +## ConditionalDeployer + +### Overview + +The ConditionalDeployer contract enables deterministic deployment of contract implementations while maintaining +idempotency across upgrade transactions. It ensures that unchanged contract bytecode always deploys to the same +address, and that attempting to deploy already-deployed bytecode succeeds silently _rather than reverting_. + +This component enables upgrade transactions to unconditionally deploy for all implementation contracts without +requiring developers to manually track which contracts have changed between upgrades. + +### Definitions + +#### CREATE2 Collision + +A CREATE2 collision occurs when attempting to deploy contract bytecode to an address where a contract with identical +bytecode already exists. This happens when the same initcode and salt are used in multiple deployment attempts. + +Note: when CREATE2 targets an address that already has code, the +[zero address is placed on the stack][create2-spec] (execution specs). + +[create2-spec]: +https://github.com/ethereum/execution-specs/blob/4ef381a0f75c96b52da635653ab580e731d3882a/src/ethereum/forks/prague/vm/instructions/system.py#L112 + +#### Deterministic Deployment Proxy + +The canonical deterministic deployment proxy contract at address `0x4e59b44847b379578588920cA78FbF26c0B4956C`, +originally deployed by Nick Johnson (Arachnid). This contract provides CREATE2-based deployment with a fixed deployer +address across all chains. + +Note: when the deterministic deployment proxy deploys to an address that already has code, [it will revert with no data](https://github.com/Arachnid/deterministic-deployment-proxy/blob/be3c5974db5028d502537209329ff2e730ed336c/source/deterministic-deployment-proxy.yul#L13). +Otherwise the ConditionalDeployer would not be required. + +### Assumptions + +#### a01-001: Deterministic Deployment Proxy is Available and Correct + +The [Deterministic Deployment Proxy](#deterministic-deployment-proxy) exists at the expected address and correctly +implements CREATE2 deployment semantics. The proxy must deterministically compute deployment addresses and execute +deployments as specified. + +##### Mitigations + +- The [Deterministic Deployment Proxy](#deterministic-deployment-proxy) is a well-established contract deployed across + all EVM chains using the same keyless deployment transaction +- The proxy's behavior is verifiable by inspecting its bytecode and testing deployment operations +- The proxy contract is immutable and cannot be upgraded or modified + +#### a02-002: Initcode is Well-Formed + +Callers provide valid EVM initcode that, when executed, will either successfully deploy a contract or revert with a +clear error. Malformed initcode that produces undefined behavior is not considered. + +##### Mitigations + +- Initcode is generated by the Solidity compiler from verified source code +- The upgrade transaction generation process includes validation of all deployment operations +- Fork-based testing exercises all deployments before inclusion in upgrade bundles + +### Invariants + +#### i01-001: Deterministic Address Derivation + +For any given initcode and salt combination, the ConditionalDeployer MUST always compute the same deployment address, +regardless of whether the contract has been previously deployed. The address calculation MUST match the CREATE2 +address that would be computed by the [Deterministic Deployment Proxy](#deterministic-deployment-proxy). + +##### Impact + +**Severity: Critical** + +If address derivation is non-deterministic or inconsistent with CREATE2 semantics, upgrade transactions could deploy +implementations to unexpected addresses. This would break proxy upgrade operations that expect implementations at +specific predetermined addresses, potentially causing proxies to point to non-existent or incorrect implementations. + +#### i02-002: Idempotent Deployment Operations + +Calling the ConditionalDeployer multiple times with identical initcode and salt MUST produce the same outcome: the +first call deploys the contract, and subsequent calls succeed without modification. No operation should revert due to +a [CREATE2 Collision](#create2-collision). + +##### Impact + +**Severity: Critical** + +If deployments are not idempotent, upgrade transactions that attempt to deploy unchanged implementations would revert +or deploy the implementation to an unexpected address. In the latter case, the proxy would then be upgrade incorrectly, +as we must predict the implementation address in advance in order to correctly generate the NUT bundle. + +#### i03-003: Non-Reverting Collision Handling + +When a [CREATE2 Collision](#create2-collision) is detected (contract already deployed at the target address), the +ConditionalDeployer MUST return successfully without reverting and without modifying blockchain state. + +##### Impact + +**Severity: Medium** + +If the collisions cause reverts, the following transactions can still proceed, however the presence of reverting +transactions in an upgrade block is likely to lead to confusion. + +#### i04-004: Collision Detection Accuracy + +The ConditionalDeployer MUST correctly distinguish between addresses where no contract exists (deploy needed) and +addresses where a contract already exists (collision detected). False negatives (failing to detect existing contracts) +and false positives (detecting non-existent contracts) are both prohibited. + +##### Impact + +**Severity: High** + +False negatives would cause the ConditionalDeployer to attempt redeployment to occupied addresses, resulting in +failed deployments and breaking the upgrade process. False positives would prevent legitimate deployments from +occurring, causing proxies to reference non-existent implementation addresses. +