PreimageOracle
Table of Contents
Overview
The PreimageOracle provides data availability for fault proof execution by storing preimage data indexed by cryptographic keys. It supports multiple preimage types including local data, hash-based preimages, blob data, and precompile results. For large preimages that cannot be submitted in a single transaction, it implements a Large Preimage Proposal system with a challenge mechanism to ensure data integrity.
Definitions
Preimage Key
A 32-byte identifier for preimage data where the first byte indicates the preimage type and the remaining 31 bytes contain type-specific data. Preimage types include: local (0x01), keccak256 (0x02), sha256 (0x04), blob (0x05), and precompile (0x06).
Large Preimage Proposal
A mechanism for submitting preimages larger than the minimum proposal size through an incremental merkleization process. Proposals include keccak256 state commitments for each 136-byte block and are subject to a challenge period before finalization.
Challenge Period
The time duration after a Large Preimage Proposal is finalized during which anyone can challenge the correctness of the keccak256 computation. If no successful challenge occurs during this period, the proposal can be squeezed to make the preimage data available.
Preimage Part
A 32-byte segment of preimage data at a specific offset. Preimages are stored with an 8-byte big-endian length prefix followed by the actual data, and can be read in 32-byte parts at any valid offset.
Keccak Block
A 136-byte chunk of data absorbed during keccak256 computation. Large Preimage Proposals process data in keccak blocks and commit to the internal state after each block is absorbed and permuted.
Bond
ETH locked when creating a Large Preimage Proposal. The bond is returned to the claimant if the proposal passes the challenge period unchallenged, or paid to a challenger if the proposal is successfully challenged.
Assumptions
a01-001: Challengers Monitor Proposals
Honest challengers monitor Large Preimage Proposals and challenge any proposals with incorrect keccak256 computations during the Challenge Period.
Mitigations
- Challenge Period provides sufficient time for monitoring
- Bond incentivizes challenges by rewarding successful challengers
- Proposals are publicly observable through events and storage
a01-002: Sufficient Gas for Precompile Calls
Callers of loadPrecompilePreimagePart provide sufficient gas to execute the precompile call as specified by the requiredGas parameter.
Mitigations
- Contract enforces minimum gas check before calling precompile
- Reserved gas buffer accounts for call setup overhead
Invariants
i01-001: Preimage Data Integrity
Once a Preimage Part is marked as available (preimagePartOk is true), the stored data at that key and offset never changes and always corresponds to the correct preimage for that key.
Impact
Severity: Critical
If preimage data could be modified after being stored, fault proofs could be invalidated or manipulated, allowing invalid state transitions to be proven valid or preventing valid proofs from being generated.
i01-002: Large Preimage Proposal Finality
Once a Large Preimage Proposal passes the Challenge Period without being countered, the preimage data it represents becomes permanently available and cannot be modified or removed.
Impact
Severity: Critical
If finalized proposals could be modified or removed, fault proofs depending on that data would fail, breaking the security guarantees of the fault proof system.
i01-003: Challenge Correctness
A Large Preimage Proposal can only be successfully challenged if it contains an incorrect keccak256 state transition, and all proposals with incorrect state transitions can be successfully challenged.
Impact
Severity: Critical
If incorrect proposals could pass unchallenged or correct proposals could be falsely challenged, the integrity of the preimage data would be compromised, allowing invalid fault proofs or preventing valid ones.
i01-004: Bond Conservation
The total ETH held in proposal Bonds equals the sum of all active proposal Bonds, and Bonds are only transferred to the claimant (on successful squeeze) or challenger (on successful challenge).
Impact
Severity: High
If Bonds could be lost or stolen, the economic security of the Large Preimage Proposal system would be compromised, reducing incentives for honest behavior.
i01-005: Local Data Isolation
Preimage data loaded via loadLocalData for a given caller and local context can only be written by that specific caller, ensuring isolation between different callers' local data.
Impact
Severity: High
If local data could be overwritten by other callers, fault proofs could be manipulated by providing incorrect local context data, compromising the security of the dispute resolution process.
Function Specification
readPreimage
Reads a Preimage Part from storage.
Parameters:
_key: The Preimage Key identifying the preimage_offset: The byte offset within the preimage (including 8-byte length prefix)
Behavior:
- MUST revert if the Preimage Part at the specified key and offset has not been loaded (preimagePartOk is false)
- MUST return up to 32 bytes of preimage data starting at the offset
- MUST return the actual length of data available, which may be less than 32 bytes if the offset is near the end of the preimage
- MUST include the 8-byte big-endian length prefix in offset calculations (offset 0 starts at the length prefix)
loadLocalData
Loads caller-specific local data into the preimage oracle.
Parameters:
_ident: The local data identifier (1=L1 Head Hash, 2=Output Root, 3=Root Claim, 4=L2 Block Number, 5=Chain ID)_localContext: Context value for key localization_word: The data word to store_size: Number of bytes in the word to load (maximum 32)_partOffset: Offset within the preimage to write
Behavior:
- MUST compute the localized key using the identifier, caller address, and local context
- MUST revert if partOffset is greater than or equal to size + 8
- MUST revert if size is greater than 32
- MUST store the Preimage Part at the computed key and offset
- MUST mark the Preimage Part as available
- MUST store the preimage length as the provided size
- MUST return the computed localized key
loadKeccak256PreimagePart
Prepares a keccak256 Preimage Part for reading.
Parameters:
_partOffset: Offset within the preimage to prepare_preimage: The complete preimage data
Behavior:
- MUST revert if partOffset is greater than or equal to the preimage size + 8
- MUST compute the Preimage Key as keccak256(preimage) with the first byte set to 0x02
- MUST store the 32-byte Preimage Part starting at the specified offset (including 8-byte length prefix)
- MUST mark the Preimage Part as available
- MUST store the preimage length
loadSha256PreimagePart
Prepares a sha256 Preimage Part for reading.
Parameters:
_partOffset: Offset within the preimage to prepare_preimage: The complete preimage data
Behavior:
- MUST revert if partOffset is greater than or equal to the preimage size + 8
- MUST compute the Preimage Key using the SHA-256 precompile with the first byte set to 0x04
- MUST revert if the SHA-256 precompile call fails
- MUST store the 32-byte Preimage Part starting at the specified offset (including 8-byte length prefix)
- MUST mark the Preimage Part as available
- MUST store the preimage length
loadBlobPreimagePart
Verifies and loads a blob Preimage Part using KZG proof verification.
Parameters:
_z: The evaluation point (big-endian)_y: The evaluation result (big-endian), which is the preimage data_commitment: The KZG commitment (48 bytes)_proof: The KZG proof (48 bytes)_partOffset: Offset within the preimage to prepare
Behavior:
- MUST verify the KZG proof using the point evaluation precompile (address 0x0A)
- MUST revert if the KZG proof verification fails
- MUST revert if partOffset is greater than or equal to 40 (32 bytes data + 8 bytes prefix)
- MUST compute the Preimage Key as keccak256(commitment || z) with the first byte set to 0x05
- MUST store the 32-byte Preimage Part (the value y) at the specified offset
- MUST mark the Preimage Part as available
- MUST store the preimage length as 32
loadPrecompilePreimagePart
Executes a precompile and stores the result as a preimage.
Parameters:
_partOffset: Offset within the preimage result to prepare_precompile: Address of the precompile to call_requiredGas: Minimum gas required for the precompile execution_input: Input data for the precompile call
Behavior:
- MUST verify sufficient gas is available (gas >= (requiredGas * 64 / 63) + PRECOMPILE_CALL_RESERVED_GAS)
- MUST revert if insufficient gas is available
- MUST call the precompile with the provided input
- MUST compute the Preimage Key as keccak256(precompile || requiredGas || input) with the first byte set to 0x06
- MUST store the result as a 1-byte status (success/failure) followed by the return data
- MUST revert if partOffset is greater than or equal to the result size + 8
- MUST store the Preimage Part at the specified offset
- MUST mark the Preimage Part as available
- MUST store the preimage length as 1 + returndatasize
initLPP
Initializes a new Large Preimage Proposal.
Parameters:
_uuid: Unique identifier for the proposal_partOffset: Offset of the Preimage Part to extract_claimedSize: Total size of the preimage being proposed
Behavior:
- MUST accept ETH payment as the proposal Bond
- MUST revert if the Bond is less than MIN_Bond_SIZE (0.25 ether)
- MUST revert if the caller is not an EOA (legacy check, can be bypassed with EIP-7702)
- MUST revert if partOffset is greater than or equal to claimedSize + 8
- MUST revert if claimedSize is less than MIN_LPP_SIZE_BYTES
- MUST revert if a proposal with the same claimant and uuid already exists
- MUST initialize proposal metadata with the part offset and claimed size
- MUST add the proposal to the proposals array
- MUST store the Bond amount
addLeavesLPP
Adds keccak256 state commitments to a Large Preimage Proposal merkle tree.
Parameters:
_uuid: Unique identifier for the proposal_inputStartBlock: Expected starting block number for this input_input: Input data to process (must be multiple of 136 bytes)_stateCommitments: Array of keccak256 state commitments after each block_finalize: Whether to finalize the proposal after adding these leaves
Behavior:
- MUST pad the input if finalizing, otherwise use input as-is
- MUST revert if the caller is not an EOA (legacy check, can be bypassed with EIP-7702)
- MUST revert if the proposal has not been initialized
- MUST revert if the proposal has already been finalized
- MUST revert if inputStartBlock does not match the number of blocks already processed
- MUST revert if input length is not a multiple of 136 bytes
- MUST revert if the number of state commitments does not equal input length / 136
- MUST extract and store the Preimage Part if the part offset falls within the current input
- MUST add each Keccak Block to the merkle tree with its corresponding state commitment
- MUST revert if the total blocks processed exceeds MAX_LEAF_COUNT (2^16 - 1)
- MUST update metadata with blocks processed and bytes processed
- MUST set the timestamp to current block timestamp if finalizing
- MUST revert if finalizing and bytes processed does not equal claimed size
- MUST store the block number where leaves were added
- MUST emit a log0 event with caller address and all calldata
challengeLPP
Challenges an incorrect keccak256 state transition in a Large Preimage Proposal.
Parameters:
_claimant: Address of the proposal claimant_uuid: Unique identifier for the proposal_stateMatrix: The keccak256 state matrix before the postState block_preState: The leaf containing the pre-state commitment_preStateProof: Merkle proof for the pre-state leaf_postState: The leaf containing the post-state commitment_postStateProof: Merkle proof for the post-state leaf
Behavior:
- MUST verify both leaves are present in the proposal's merkle tree
- MUST revert if either merkle proof is invalid
- MUST verify the provided state matrix matches the preState commitment
- MUST revert if the state matrix does not match
- MUST verify preState and postState are contiguous (postState.index = preState.index + 1)
- MUST revert if the states are not contiguous
- MUST absorb the postState input into the state matrix and permute
- MUST verify the resulting state does NOT match the postState commitment
- MUST revert if the resulting state matches (challenge is invalid)
- MUST mark the proposal as countered
- MUST transfer the Bond to the challenger
challengeFirstLPP
Challenges the first keccak256 block in a Large Preimage Proposal.
Parameters:
_claimant: Address of the proposal claimant_uuid: Unique identifier for the proposal_postState: The first leaf in the proposal_postStateProof: Merkle proof for the first leaf
Behavior:
- MUST verify the leaf is present in the proposal's merkle tree
- MUST revert if the merkle proof is invalid
- MUST verify the postState index is 0
- MUST revert if the index is not 0
- MUST absorb the postState input into a fresh state matrix and permute
- MUST verify the resulting state does NOT match the postState commitment
- MUST revert if the resulting state matches (challenge is invalid)
- MUST mark the proposal as countered
- MUST transfer the Bond to the challenger
squeezeLPP
Finalizes a Large Preimage Proposal after the Challenge Period, making the preimage data available.
Parameters:
_claimant: Address of the proposal claimant_uuid: Unique identifier for the proposal_stateMatrix: The keccak256 state matrix before the final block_preState: The second-to-last leaf in the proposal_preStateProof: Merkle proof for the pre-state leaf_postState: The final leaf in the proposal_postStateProof: Merkle proof for the post-state leaf
Behavior:
- MUST revert if the proposal has been countered
- MUST revert if the proposal has not been finalized (timestamp is 0)
- MUST revert if the Challenge Period has not elapsed
- MUST verify both leaves are present in the proposal's merkle tree
- MUST revert if either merkle proof is invalid
- MUST verify the provided state matrix matches the preState commitment
- MUST revert if the state matrix does not match
- MUST verify preState and postState are contiguous and postState is the final block
- MUST revert if the states are not contiguous or postState is not final
- MUST absorb the postState input, permute, and squeeze to get the final keccak256 digest
- MUST set the first byte of the digest to 0x02 (keccak256 preimage type)
- MUST store the Preimage Part at the specified offset with the computed key
- MUST mark the Preimage Part as available
- MUST store the preimage length
- MUST transfer the Bond to the claimant
getTreeRootLPP
Computes the merkle root of a Large Preimage Proposal.
Parameters:
_owner: Address of the proposal claimant_uuid: Unique identifier for the proposal
Behavior:
- MUST compute the merkle root from the proposal's branch and the number of blocks processed
- MUST use zero hashes for empty branches
- MUST return the computed root
proposalCount
Returns the total number of Large Preimage Proposals created.
Behavior:
- MUST return the length of the proposals array
proposalBlocksLen
Returns the number of addLeavesLPP calls made for a proposal.
Parameters:
_claimant: Address of the proposal claimant_uuid: Unique identifier for the proposal
Behavior:
- MUST return the length of the proposalBlocks array for the specified proposal
challengePeriod
Returns the Challenge Period duration.
Behavior:
- MUST return the CHALLENGE_PERIOD immutable value
minProposalSize
Returns the minimum size for Large Preimage Proposals.
Behavior:
- MUST return the MIN_LPP_SIZE_BYTES immutable value