The fault proof alpha system will be deployed to the Goerli testnet as a sidecar to the current system. During the alpha, the outcome of fault dispute games will have no influence on the official bridge contracts nor the official `L2OutputOracle`.
During this early phase of ongoing development, we invite security researchers and developers to engage with the system and attempt to break its current components.
The current system is not production ready, however the core infrastructure for creating an instruction trace ([Cannon][cannon] + the [`op-program`][op-program]), the off-chain challenge agent ([`op-challenger`][op-challenger]),
and the on-chain infrastructure for the [Dispute Game][dispute-game] are all in place.
For the Fault Proof Alpha security review, we've pinned `546fb2c7a5796b7fe50b0b7edc7666d3bd281d6f` as the commit hash in the monorepo. This commit hash was the head of the `develop` branch at the time of the alpha's launch. All
security reviews and PoCs should be derived from this commit hash, as the contracts and off-chain agents are being updated frequently at this stage of development.
### Resources
> **Note**
> Prior to moving forward, we recommended reading into the technical documentation for the components of Fault Proof Alpha.
The alpha system is not prepared for mainnet, and as such, there are a number of known issues that we are working on fixing and components of the system that must be improved prior to it being sustainable.
1. DoS attacks are currently likely to occur due to the lack of bonds in the alpha system as well as the lack of an extra layer of bisection in the dispute game to reduce the running time of [Cannon][cannon]. It is possible to
DoS the network of honest challengers by creating a large number of invalid challenges.
1. Limitations of pre-image oracle inputs. The pre-image oracle currently does not support the full specified set of inputs.
In particular, arbitrary pre-image value size and preimage key types other than `local` (type 1) `keccak256` (type 2) are not supported.
The pre-image value size is limited to what the current oracle can verify: gas and calldata limits constrain this more than the pre-images are, rendering some state-transitions that include large pre-images impossible to prove with the oracle as-is. This does not affect most proofs. L1/L2 activity that breaks this pre-image size limitation does not qualify for the bounty.
The remaining pre-images types are not supported, as the types are not used by the current op-program, but may be supported for future program proving, e.g. type 3 for application-specific proofs, and new types 4, 5, etc. for ethereum extensions like SHA2 and KZG point verification.
1. Non-standard rollup chain configurations do not qualify. Output roots span a range of L2 blocks derived from a range of L1 blocks, built on top of the previous agreed upon L2 state. By breaking time or input-range chain parameters, the proof program may not complete or fail in undefined ways.
### Reviewer Notes
1.**Any bug report without a proof-of-concept in the form of a test in `op-e2e` will not be considered a valid bug report.**
1. A guide on creating an e2e test with an invalid output proposal to dispute can be found [here][invalid-proposal-doc].
1. Exploits against the alpha system that take advantage of the aforementioned issues will not be considered valid bug reports.
1. The [AlphabetVM][alphabet-vm] is not equivalent to the MIPS thread context in behavior. Bug reports submitted against the [AlphabetVM][alphabet-vm] will not be considered valid bug reports, this mock VM is used solely for testing.
### Plans for the next iteration
Going past alpha, we have a number of plans for improving the system and fixing some of the aforementioned issues in preparation for full integration with the current system. These include:
1. Including an extra layer of bisection over output roots prior to beginning execution trace bisection, enabling the off-chain challenge agents to only need to run [Cannon][cannon] over a single block rather than a string of blocks. This will heavily reduce the hardware cost of running the off
chain challenge agent and provide an upper bound on what Cannon will have to execute, allowing for sparse proposals.
1. Adding bonds to the system to preserve incentive compatibility. In the alpha, defenses of the honest L2 state are not incentivized, which also means that attacks on the honest L2 state are not disincentivized. Adding bonds to each claim
made in the dispute game will preserve the incentives of the system as well as make it more costly to attack.
1. Improving the [Dispute Game][dispute-game]'s resolution algorithm to reduce the number of interactions that the off-chain challenge agents need to have with the on-chain dispute game. This will reduce the cost of running the off-chain challenge
agent, ensure that an honest challenger's participation always results in a profitable move, and possibly prevent the need for challengers to respond to every invalid claim within the game.
1. The fault proof system will be integrated into the bridge contracts, specifically the `OptimismPortal`, in order to enable the system to be used in production and verify the correctness of output roots that withdrawals are proven against.
1. The pre-image oracle limitations related to pre-image size and typing support will be addressed to cover the full scope of valid onchain L1 and L2 activity.
## Bounty Scope
The scope of the bounty is limited to the fault proof alpha system. This includes the following components, in order of security review priority:
1.**Cannon**: The [Cannon][cannon] binary and its dependencies, as defined in the monorepo. The archived legacy version, and alternative implementations, do not qualify.
1.**op-program**: The [`op-program`][op-program] binary and its dependencies.
1.**Smart Contracts**
1. The [Cannon][cannon-contracts] contracts and their dependencies.
1. The [Dispute Game][dispute-game] and their dependencies.
1.**op-challenger**: The [`op-challenger`][op-challenger] binary and its dependencies.
As mentioned above in the "[Plans for the next iteration](#plans-for-the-next-iteration)" section, there will soon be a number of large architectural changes to the [dispute smart contracts][dispute-game]
as well as the [`op-challenger`][op-challenger] in order to support the features that will bring the system to a production ready state. During this time, it is unlikely that [Cannon][cannon], the [Cannon contracts][cannon-contracts],
or the [`op-program`][op-program] will change significantly, and as such, we recommend focusing efforts primarily on these components.
There are several key invariants that must be maintained in order for the system to be considered secure. A bounty report must demonstrate a bug which breaks one of these invariants.
1.**Cannon**
1.[Cannon][cannon]'s `mipsevm` must be functionally equivalent to the [MIPS thread context][cannon-contracts] implemented in Solidity. Any disparities that result in different `op-program` execution are a bug.
1. Both [Cannon][cannon] and the on-chain [MIPS thread context][cannon-contracts] must produce the same output given an identical setup state and input data.
1. Both [Cannon][cannon] and the on-chain [MIPS thread context][cannon-contracts] must produce a deterministic output given an identical setup state and input data.
1. Both [Cannon][cannon] and the on-chain [MIPS thread context][cannon-contracts] must never panic on a state transition with honest input data / setup state.
1. Note: There are a number of instructions from MIPS, and system calls in Linux, that Cannon does not support. Specifically, this invariant covers panic conditions within the realm of supported instructions and valid honest input data / setup state where cannon otherwise should have completed execution and produced a valid/invalid opinion about the state transition. The op-program may contain "dead code", non-reachable invalid instructions that do not affect the output.
1. The `PreimageOracle` contract's local data storage must not be able to be corrupted by an external party.
1.**op-program**
1. The [`op-program`][op-program] must produce a deterministic output given an identical setup state and input data.
1.**Dispute Game Contracts**
1. Assuming the presence of an `honest challenger` (defined by the behavior of the [`op-challenger`][op-challenger]) participating within the game, the `FaultDisputeGame` utilizing the `MIPS` VM **must always** resolve favorably towards the honest L2 state.
1.*Note (1)*: The presence of an honest challenger implies that the honest challenger has exhausted all moves it would have made - any game where the honest challenger was unable to exhaust its move set can resolve unfavorably to their desired outcome. The aforementioned DoS vector is one such reason the honest challenger may not perform all its moves.
1.**op-challenger**
1. The honest `op-challenger` must never make a claim that does not support the honest outcome of the dispute game (i.e., the outcome which favors the honest L2 state being considered canonical).
1.*Note:* Because of the rules in the current solving / resolution mechanism, the challenger will counter all claims that have a different view of the root claim's validity. While this is an inefficiency, it is not considered a violation of this invariant, as this behavior is necessary to ensure that all invalid claims have been countered.
Bug reports in the form of a proof-of-concept `op-e2e` test that demonstrates a violation of any of the above invariants will be considered valid bug reports and eligible for a reward*.
* All proof of concept reports should be configured to run against the parameters of the system deployed on the `goerli` testnet or with the environment defined in the `op-e2e``faultproof_test.go` file. Bug reports that otherwise violate the above invariants
but use custom configurations will be assessed on a case by case basis, and their validity is not guaranteed.
### Bounty Rewards
See our bounty program on [Immunefi][immunefi] for information regarding reward sizes.
The process of disputing an output root starts by creating a new dispute game. There are conceptually three key inputs
required for a dispute game:
- The output root being disputed
- The agreed output root the derivation process will start from
- The L1 head block that defines the canonical L1 chain containing all required batch data to perform the derivation
The creator of the game selects the output root to dispute. It is identified by its L2 block number which can be used to
look up the full details in the L2 output oracle.
The agreed output root is defined as the output root immediately prior to the disputed output root in the L2 output
oracle. Therefore, a dispute game should only be created for the first invalid output root. If it is successfully
disputed, all output roots after it are considered invalid by inference.
The L1 head block can be any L1 block where the disputed output root is present in the L2 output oracle. Proposers
should therefore ensure that all batch data has been submitted to L1 before submitting a proposal. The L1 head block is
recorded in the `BlockOracle` and then referenced by its block number.
Creating a game requires two separate transactions. First the L1 head block is recorded in the `BlockOracle` by calling
its `checkpoint` function. This records the parent of the block the transaction is included in. The `BlockOracle` emits
a log `Checkpoint(blockNumber, blockHash, childTimestamp)`.
Now, using the L1 head along with output root info available in the L2 output oracle, cannon can be executed to
determine the root claim to use when creating the game. In simple cases, where the claim is expected to be incorrect, an
arbitrary hash can be used for claim values. For more advanced cases [cannon can be used](./cannon.md) to generate a
trace, including the claim values to use at specific steps. Note that it is not valid to create a game that disputes an
output root, using the final hash from a trace that confirms the output root is valid. To dispute an output root
successfully, the trace must resolve that the disputed output root is invalid. This is indicated by the first byte of
the claim value being set to the invalid [VM status](https://github.com/ethereum-optimism/specs/blob/main/specs/fault-proof/cannon-fault-proof-vm.md#state-hash)(`0x01`).
The game can then be created by calling the `create` method on the `DisputeGameFactory` contract. This requires three
parameters:
-`gameType` - a `uint8` representing the type of game to create. For fault dispute games using cannon and op-program
traces, the game type is 0.
-`rootClaim` - a `bytes32` hash of the final state from the trace.
-`extraData` - arbitrary bytes which are used as the initial inputs for the game. For fault dispute games using cannon
and op-program traces, this is the abi encoding of `(uint256(l2_block_number), uint256(l1_checkpoint))`.
-`l2_block_number` is the L2 block number from the output root being disputed
-`l1_checkpoint` is the L1 block number recorded by the `BlockOracle` checkpoint
This emits a log event `DisputeGameCreated(gameAddress, gameType, rootClaim)` where `gameAddress` is the address of the
newly created dispute game.
The helper script, [create_game.sh](../../op-challenger#create_gamesh) can be used to easily create a new dispute
game and also acts as an example of using `cast` to manually create a game.
### Performing Moves
The dispute game progresses by actors countering existing claims via either the `attack` or `defend` methods in
the `FaultDisputeGame` contract. Note that only `attack` can be used to counter the root claim. In both cases, there are
two inputs required:
-`parentIndex` - the index in the claims array of the parent claim that is being countered.
-`claim` - a `bytes32` hash of the state at the trace index corresponding to the new claim’s position.
The helper script, [move.sh](../../op-challenger#movesh), can be used to easily perform moves and also
acts as an example of using `cast` to manually call `attack` and `defend`.
### Performing Steps
Attacking or defending are the only available actions before the maximum depth of the game is reached. To counter claims
at the maximum depth, a step must be performed instead. Calling the `step` method in the `FaultDisputeGame` contract
counters a claim at the maximum depth by running a single step of the cannon VM on chain. The `step` method will revert
unless the cannon execution confirms the claim being countered is invalid. Note, if an actor's clock runs out at any
point, the game can be [resolved](#resolving-a-game).
The inputs for step are:
-`claimIndex` - the index in the claims array of the claim that is being countered
-`isAttack` - Similar to regular moves, steps can either be attacking or defending
-`stateData` - the full cannon state witness to use as the starting state for execution
-`proof` - the additional proof data for the state witness required by cannon to perform the step
When a step is attacking, the caller is asserting that the claim at `claimIndex` is incorrect, and the claim for
the previous trace index (made at a previous level in the game) was correct. The `stateData` must be the pre-image for
the agreed correct hash at the previous trace index. The call to `step` will revert if the post-state from cannon
matches the claim at `claimIndex` since the on-chain execution has proven the claim correct and it should not be
countered.
When a step is defending, the caller is asserting that the claim at `claimIndex` is correct, and the claim for
the next trace index (made at a previous level in the game) is incorrect. The `stateData` must be the pre-image for the
hash in the claim at `claimIndex`.
The `step` function will revert with `ValidStep()` if the cannon execution proves that the claim attempting to be
countered is correct. As a result, claims at the maximum game depth can only be countered by a valid execution of the
single instruction in cannon running on-chain.
#### Populating the Pre-image Oracle
When the instruction to be executed as part of a `step` call reads from some pre-image data, that data must be loaded
into the pre-image oracle prior to calling `step`.
For [local pre-image keys](https://github.com/ethereum-optimism/specs/blob/main/specs/fault-proof/index.md#type-1-local-key), the pre-image must be populated via
the `FaultDisputeGame` contract by calling the `addLocalData` function.
For [global keccak256 keys](https://github.com/ethereum-optimism/specs/blob/main/specs/fault-proof/index.md#type-2-global-keccak256-key), the data should be added directly
to the pre-image oracle contract.
### Resolving a Game
The final action required for a game is to resolve it by calling the `resolve` method in the `FaultDisputeGame`
contract. This can only be done once the clock of the left-most uncontested claim’s parent has expired. A game can only
be resolved once.
There are no inputs required for the `resolve` method. When successful, a log event is emitted with the game’s final
status.
The helper script, [resolve.sh](../../op-challenger#resolvesh), can be used to easily resolve a game and also acts as an
example of using `cast` to manually call `resolve` and understand the result.