Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fault-detector: remove pre-bedrock support and update tests #6095

Merged
merged 13 commits into from
Jun 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/nervous-icons-retire.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@eth-optimism/fault-detector': minor
---

Remove pre-bedrock support from fault detector.
13 changes: 3 additions & 10 deletions packages/fault-detector/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,7 @@ yarn build
## Running the service

Copy `.env.example` into a new file named `.env`, then set the environment variables listed there. Additional env setting are listed on `--help`. If running the fault detector against
a custom op chain, the necessary contract addresses must also be set associated with the op-chain.

- Bedrock: `OptimismPortal`
- Legacy: `StateCommitmentChain`
a custom op chain, the `OptimismPortal` contract addresses must also be set associated with the op-chain.

Once your environment variables or flags have been set, run the service via:

Expand All @@ -40,13 +37,11 @@ yarn start
The `fault-detector` detects differences between the transaction results generated by your local Optimism node and the transaction results actually published to Ethereum.
Currently, transaction results take the form of [the root of the Optimism state trie](https://medium.com/@eiki1212/ethereum-state-trie-architecture-explained-a30237009d4e).

- Post bedrock upgrade, the state root of the block is published to the [`L2OutputOracle`](https://github.com/ethereum-optimism/optimism/blob/39b7262cc3ffd78cd314341b8512b2683c1d9af7/packages/contracts-bedrock/contracts/L1/L2OutputOracle.sol) contract on Ethereum.
The state root of the block is published to the [`L2OutputOracle`](https://github.com/ethereum-optimism/optimism/blob/39b7262cc3ffd78cd314341b8512b2683c1d9af7/packages/contracts-bedrock/contracts/L1/L2OutputOracle.sol) contract on Ethereum.
- ***Note***: The service accepts the `OptimismPortal` as a flag instead of the `L2OutputOracle` for backwards compatibility with early versions of these contracts. The `L2OutputOracle`
is inferred from the portal contract.
- For pre-bedrock chains, the state root of the block is published to the [`StateCommitmentChain`](https://github.com/ethereum-optimism/optimism/blob/39b7262cc3ffd78cd314341b8512b2683c1d9af7/packages/contracts/contracts/L1/rollup/StateCommitmentChain.sol) contract on Ethereum.

We can therefore detect differences by, for each block, checking the state root of the given block as reported by an Optimism node and the state root as published to Ethereum.
In order for the fault detector to differentiate between bedrock and legacy chains, please make sure to specify `--bedrock`.

We export a series of Prometheus metrics that you can use to trigger alerting when issues are detected.
Check the list of available metrics via `yarn start --help`:
Expand All @@ -62,9 +57,7 @@ Options:
--l2rpcprovider Provider for interacting with L2 (env: FAULT_DETECTOR__L2_RPC_PROVIDER)
--startbatchindex Batch index to start checking from. Setting it to -1 will cause the fault detector to find the first state batch index that has not yet passed the fault proof window (env: FAULT_DETECTOR__START_BATCH_INDEX, default value: -1)
--loopintervalms Loop interval in milliseconds (env: FAULT_DETECTOR__LOOP_INTERVAL_MS)
--bedrock Whether or not the service is running against a Bedrock chain (env: FAULT_DETECTOR__BEDROCK, default value: false)
--optimismportaladdress [Custom Bedrock Chains] Deployed OptimismPortal contract address. Used to retrieve necessary info for ouput verification (env: FAULT_DETECTOR__OPTIMISM_PORTAL_ADDRESS, default 0x0)
--statecommitmentchainaddress [Custom Legacy Chains] Deployed StateCommitmentChain contract address. Used to fetch necessary info for output verification. (env: FAULT_DETECTOR__STATE_COMMITMENT_CHAIN_ADDRESS, default 0x0)
--optimismportaladdress [Custom OP Chains] Deployed OptimismPortal contract address. Used to retrieve necessary info for ouput verification (env: FAULT_DETECTOR__OPTIMISM_PORTAL_ADDRESS, default 0x0)

--port Port for the app server (env: FAULT_DETECTOR__PORT)
--hostname Hostname for the app server (env: FAULT_DETECTOR__HOSTNAME)
Expand Down
2 changes: 1 addition & 1 deletion packages/fault-detector/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
},
"dependencies": {
"@eth-optimism/common-ts": "^0.8.2",
"@eth-optimism/contracts": "^0.6.0",
"@eth-optimism/contracts-bedrock": "^0.14.0",
"@eth-optimism/core-utils": "^0.12.1",
"@eth-optimism/sdk": "^3.0.0",
"@ethersproject/abstract-provider": "^5.7.0"
Expand Down
41 changes: 16 additions & 25 deletions packages/fault-detector/src/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
import { Contract, BigNumber } from 'ethers'
import { Contract } from 'ethers'
import { Logger } from '@eth-optimism/common-ts'

export interface OutputOracle<TSubmissionEventArgs> {
contract: Contract
filter: any
getTotalElements: () => Promise<BigNumber>
getEventIndex: (args: TSubmissionEventArgs) => BigNumber
}

/**
* Partial event interface, meant to reduce the size of the event cache to avoid
* running out of memory.
Expand Down Expand Up @@ -54,12 +47,12 @@ const getCache = (
* @param contract Contract to update cache for.
* @param filter Event filter to use.
*/
export const updateOracleCache = async <TSubmissionEventArgs>(
oracle: OutputOracle<TSubmissionEventArgs>,
export const updateOracleCache = async (
oracle: Contract,
logger?: Logger
): Promise<void> => {
const cache = getCache(oracle.contract.address)
const endBlock = await oracle.contract.provider.getBlockNumber()
const cache = getCache(oracle.address)
const endBlock = await oracle.provider.getBlockNumber()
logger?.info('visiting uncached oracle events for range', {
node: 'l1',
cachedUntilBlock: cache.highestBlock,
Expand All @@ -77,17 +70,15 @@ export const updateOracleCache = async <TSubmissionEventArgs>(
blockRangeSize: step,
})

const events = await oracle.contract.queryFilter(
oracle.filter,
const events = await oracle.queryFilter(
oracle.filters.OutputProposed(),
currentBlock,
currentBlock + step
)

// Throw the events into the cache.
for (const event of events) {
cache.eventCache[
oracle.getEventIndex(event.args as TSubmissionEventArgs).toNumber()
] = {
cache.eventCache[event.args.l2OutputIndex.toNumber()] = {
blockNumber: event.blockNumber,
transactionHash: event.transactionHash,
args: event.args,
Expand Down Expand Up @@ -135,12 +126,12 @@ export const updateOracleCache = async <TSubmissionEventArgs>(
* @param index State batch index to search for.
* @returns Event corresponding to the batch.
*/
export const findEventForStateBatch = async <TSubmissionEventArgs>(
oracle: OutputOracle<TSubmissionEventArgs>,
export const findEventForStateBatch = async (
oracle: Contract,
index: number,
logger?: Logger
): Promise<PartialEvent> => {
const cache = getCache(oracle.contract.address)
const cache = getCache(oracle.address)

// Try to find the event in cache first.
if (cache.eventCache[index]) {
Expand All @@ -166,21 +157,21 @@ export const findEventForStateBatch = async <TSubmissionEventArgs>(
* @param oracle Output oracle contract.
* @returns Starting state root batch index.
*/
export const findFirstUnfinalizedStateBatchIndex = async <TSubmissionEventArgs>(
oracle: OutputOracle<TSubmissionEventArgs>,
export const findFirstUnfinalizedStateBatchIndex = async (
oracle: Contract,
fpw: number,
logger?: Logger
): Promise<number> => {
const latestBlock = await oracle.contract.provider.getBlock('latest')
const totalBatches = (await oracle.getTotalElements()).toNumber()
const latestBlock = await oracle.provider.getBlock('latest')
const totalBatches = (await oracle.nextOutputIndex()).toNumber()

// Perform a binary search to find the next batch that will pass the challenge period.
let lo = 0
let hi = totalBatches
while (lo !== hi) {
const mid = Math.floor((lo + hi) / 2)
const event = await findEventForStateBatch(oracle, mid, logger)
const block = await oracle.contract.provider.getBlock(event.blockNumber)
const block = await oracle.provider.getBlock(event.blockNumber)

if (block.timestamp + fpw < latestBlock.timestamp) {
lo = mid + 1
Expand Down
Loading