Commit ec087c2a authored by mergify[bot]'s avatar mergify[bot] Committed by GitHub

Merge branch 'develop' into aj/reqresp-metrics

parents c5451a59 2a516855
---
'@eth-optimism/fault-detector': minor
---
Remove pre-bedrock support from fault detector.
#!/bin/sh #!/bin/sh
. "$(dirname "$0")/_/husky.sh" . "$(dirname "$0")/_/husky.sh"
yarn lerna run --concurrency 1 --stream pre-commit --since HEAD --exclude-dependents yarn nx affected --target=pre-commit
{
"defaultBase": "develop",
"affected": {
"defaultBase": "develop"
},
"implicitDependencies": {
"nx.json": "*"
},
"tasksRunnerOptions": {
"default": {
"runner": "@nrwl/nx-cloud",
"options": {
"cacheableOperations": [
"lint",
"build",
"test",
"build:contracts",
"autogen:artifacts"
]
}
}
},
"namedInputs": {
"configsWorkspace": [
"{workspaceRoot}/package.json",
"{workspaceRoot}/tsconfig.json"
],
"configsProject": [
"{projectRoot}/foundry.toml",
"{projectRoot}/tsconfig.json",
"{projectRoot}/hardhat.config.ts",
"{projectRoot}/package.json"
],
"default": ["{projectRoot}/**/*", "configsWorkspace"],
"srcGenerated": [
"{projectRoot}/src/contract-artifacts.ts",
"{projectRoot}/src/contract-deployed-artifacts.ts"
],
"productionSrc": [
"{projectRoot}/src/**/*",
"!{projectRoot}/src/contract-artifacts.ts",
"!{projectRoot}/src/contract-deployed-artifacts.ts",
"!{projectRoot}/src/**/*.spec.ts"
],
"productionContracts": ["{projectRoot}/contracts/**/*"],
"production": ["productionSrc", "productionContracts"],
"testing": ["configsWorkspace", "default"]
},
"targetDefaults": {
"lint": {
"inputs": ["{workspaceRoot}/.markdownlint.json", "default"]
},
"test": {
"inputs": ["default", "testing", "^production"],
"dependsOn": ["^build"]
},
"build:contracts": {
"inputs": [
"configsProject",
"productionContracts",
"^productionContracts"
],
"dependsOn": ["^build"],
"outputs": ["{projectRoot}/artifacts", "{projectRoot}/forge-artifacts"]
},
"autogen:artifacts": {
"inputs": [
"configsWorkspace",
"configsProject",
"productionContracts",
"^productionContracts"
],
"dependsOn": ["^build", "build:contracts"],
"outputs": ["srcGenerated"]
},
"build": {
"inputs": [
"configsWorkspace",
"configsProject",
"production",
"^production"
],
"dependsOn": ["^build", "autogen:artifacts", "build:contracts"],
"outputs": ["{projectRoot}/dist"]
}
}
}
...@@ -24,14 +24,14 @@ ...@@ -24,14 +24,14 @@
}, },
"private": true, "private": true,
"scripts": { "scripts": {
"clean": "yarn lerna run clean --parallel", "clean": "npx nx run-many --target=clean",
"build": "yarn lerna run build", "build": "npx nx run-many --target=build",
"test": "yarn lerna run test --parallel", "test": "npx nx run-many --target=test",
"test:coverage": "yarn lerna run test:coverage --parallel", "lint": "npx nx run-many --target=lint",
"lint": "yarn lerna run lint", "test:coverage": "npx nx run-many --target=test:coverage",
"lint:ts:check": "yarn lerna run lint:ts:check", "lint:ts:check": "npx nx run-many --target=lint:ts:check",
"lint:check": "yarn lerna run lint:check", "lint:check": "npx nx run-many --target=lint:check",
"lint:fix": "yarn lerna run lint:fix --parallel", "lint:fix": "npx nx run-many --target=lint:fix",
"lint:specs:fix": "yarn run markdownlint-cli2-fix \"./specs/**/*.md\"", "lint:specs:fix": "yarn run markdownlint-cli2-fix \"./specs/**/*.md\"",
"lint:specs:check": "yarn run markdownlint-cli2 \"./specs/**/*.md\"", "lint:specs:check": "yarn run markdownlint-cli2 \"./specs/**/*.md\"",
"lint:specs:toc": "yarn run doctoc '--title=**Table of Contents**' ./specs", "lint:specs:toc": "yarn run doctoc '--title=**Table of Contents**' ./specs",
...@@ -43,6 +43,7 @@ ...@@ -43,6 +43,7 @@
}, },
"devDependencies": { "devDependencies": {
"@babel/eslint-parser": "^7.18.2", "@babel/eslint-parser": "^7.18.2",
"@nrwl/nx-cloud": "latest",
"@types/chai": "^4.2.18", "@types/chai": "^4.2.18",
"@types/chai-as-promised": "^7.1.4", "@types/chai-as-promised": "^7.1.4",
"@types/mocha": "^8.2.2", "@types/mocha": "^8.2.2",
...@@ -65,12 +66,12 @@ ...@@ -65,12 +66,12 @@
"eslint-plugin-react": "^7.24.0", "eslint-plugin-react": "^7.24.0",
"eslint-plugin-unicorn": "^42.0.0", "eslint-plugin-unicorn": "^42.0.0",
"husky": "^6.0.0", "husky": "^6.0.0",
"lerna": "^4.0.0",
"lint-staged": "11.0.0", "lint-staged": "11.0.0",
"markdownlint": "^0.24.0", "markdownlint": "^0.24.0",
"markdownlint-cli2": "0.4.0", "markdownlint-cli2": "0.4.0",
"mkdirp": "^1.0.4", "mkdirp": "^1.0.4",
"mocha": "^8.4.0", "mocha": "^8.4.0",
"nx": "15.6.0",
"nyc": "^15.1.0", "nyc": "^15.1.0",
"patch-package": "^6.4.7", "patch-package": "^6.4.7",
"prettier": "^2.8.0", "prettier": "^2.8.0",
......
...@@ -32,12 +32,12 @@ DisputeGameFactory_SetImplementation_Test:test_setImplementation_notOwner_revert ...@@ -32,12 +32,12 @@ DisputeGameFactory_SetImplementation_Test:test_setImplementation_notOwner_revert
DisputeGameFactory_SetImplementation_Test:test_setImplementation_succeeds() (gas: 44243) DisputeGameFactory_SetImplementation_Test:test_setImplementation_succeeds() (gas: 44243)
DisputeGameFactory_TransferOwnership_Test:test_transferOwnership_notOwner_reverts() (gas: 15950) DisputeGameFactory_TransferOwnership_Test:test_transferOwnership_notOwner_reverts() (gas: 15950)
DisputeGameFactory_TransferOwnership_Test:test_transferOwnership_succeeds() (gas: 18642) DisputeGameFactory_TransferOwnership_Test:test_transferOwnership_succeeds() (gas: 18642)
FaultDisputeGame_ResolvesCorrectly_CorrectRoot2:test_resolvesCorrectly_succeeds() (gas: 497198) FaultDisputeGame_ResolvesCorrectly_CorrectRoot2:test_resolvesCorrectly_succeeds() (gas: 502842)
FaultDisputeGame_ResolvesCorrectly_CorrectRoot4:test_resolvesCorrectly_succeeds() (gas: 499064) FaultDisputeGame_ResolvesCorrectly_CorrectRoot3:test_resolvesCorrectly_succeeds() (gas: 504699)
FaultDisputeGame_ResolvesCorrectly_CorrectRoot:test_resolvesCorrectly_succeeds() (gas: 489092) FaultDisputeGame_ResolvesCorrectly_CorrectRoot:test_resolvesCorrectly_succeeds() (gas: 492706)
FaultDisputeGame_ResolvesCorrectly_IncorrectRoot2:test_resolvesCorrectly_succeeds() (gas: 494067) FaultDisputeGame_ResolvesCorrectly_IncorrectRoot2:test_resolvesCorrectly_succeeds() (gas: 501717)
FaultDisputeGame_ResolvesCorrectly_IncorrectRoot3:test_resolvesCorrectly_succeeds() (gas: 495933) FaultDisputeGame_ResolvesCorrectly_IncorrectRoot3:test_resolvesCorrectly_succeeds() (gas: 503574)
FaultDisputeGame_ResolvesCorrectly_IncorrectRoot:test_resolvesCorrectly_succeeds() (gas: 485961) FaultDisputeGame_ResolvesCorrectly_IncorrectRoot:test_resolvesCorrectly_succeeds() (gas: 491581)
FaultDisputeGame_Test:test_defendRoot_invalidMove_reverts() (gas: 13250) FaultDisputeGame_Test:test_defendRoot_invalidMove_reverts() (gas: 13250)
FaultDisputeGame_Test:test_extraData_succeeds() (gas: 17409) FaultDisputeGame_Test:test_extraData_succeeds() (gas: 17409)
FaultDisputeGame_Test:test_gameData_succeeds() (gas: 17834) FaultDisputeGame_Test:test_gameData_succeeds() (gas: 17834)
...@@ -49,10 +49,10 @@ FaultDisputeGame_Test:test_move_duplicateClaim_reverts() (gas: 103231) ...@@ -49,10 +49,10 @@ FaultDisputeGame_Test:test_move_duplicateClaim_reverts() (gas: 103231)
FaultDisputeGame_Test:test_move_gameDepthExceeded_reverts() (gas: 407967) FaultDisputeGame_Test:test_move_gameDepthExceeded_reverts() (gas: 407967)
FaultDisputeGame_Test:test_move_gameNotInProgress_reverts() (gas: 10923) FaultDisputeGame_Test:test_move_gameNotInProgress_reverts() (gas: 10923)
FaultDisputeGame_Test:test_move_nonExistentParent_reverts() (gas: 24632) FaultDisputeGame_Test:test_move_nonExistentParent_reverts() (gas: 24632)
FaultDisputeGame_Test:test_resolve_challengeContested() (gas: 221074) FaultDisputeGame_Test:test_resolve_challengeContested() (gas: 221068)
FaultDisputeGame_Test:test_resolve_notInProgress_reverts() (gas: 9657) FaultDisputeGame_Test:test_resolve_notInProgress_reverts() (gas: 9657)
FaultDisputeGame_Test:test_resolve_rootContested() (gas: 106120) FaultDisputeGame_Test:test_resolve_rootContested() (gas: 106120)
FaultDisputeGame_Test:test_resolve_rootUncontested() (gas: 23630) FaultDisputeGame_Test:test_resolve_rootUncontested() (gas: 23624)
FaultDisputeGame_Test:test_resolve_teamDeathmatch() (gas: 391731) FaultDisputeGame_Test:test_resolve_teamDeathmatch() (gas: 391731)
FaultDisputeGame_Test:test_rootClaim_succeeds() (gas: 8203) FaultDisputeGame_Test:test_rootClaim_succeeds() (gas: 8203)
FaultDisputeGame_Test:test_simpleAttack_succeeds() (gas: 107322) FaultDisputeGame_Test:test_simpleAttack_succeeds() (gas: 107322)
......
...@@ -6,6 +6,7 @@ import { IVersioned } from "./interfaces/IVersioned.sol"; ...@@ -6,6 +6,7 @@ import { IVersioned } from "./interfaces/IVersioned.sol";
import { IFaultDisputeGame } from "./interfaces/IFaultDisputeGame.sol"; import { IFaultDisputeGame } from "./interfaces/IFaultDisputeGame.sol";
import { IInitializable } from "./interfaces/IInitializable.sol"; import { IInitializable } from "./interfaces/IInitializable.sol";
import { IBondManager } from "./interfaces/IBondManager.sol"; import { IBondManager } from "./interfaces/IBondManager.sol";
import { IBigStepper } from "./interfaces/IBigStepper.sol";
import { Clone } from "../libraries/Clone.sol"; import { Clone } from "../libraries/Clone.sol";
import { LibHashing } from "./lib/LibHashing.sol"; import { LibHashing } from "./lib/LibHashing.sol";
...@@ -29,6 +30,9 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone { ...@@ -29,6 +30,9 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone {
/// @notice The max depth of the game. /// @notice The max depth of the game.
uint256 public immutable MAX_GAME_DEPTH; uint256 public immutable MAX_GAME_DEPTH;
/// @notice A hypervisor that performs single instruction steps on a fault proof program trace.
IBigStepper public immutable VM;
/// @notice The duration of the game. /// @notice The duration of the game.
/// @dev TODO: Account for resolution buffer. (?) /// @dev TODO: Account for resolution buffer. (?)
Duration internal constant GAME_DURATION = Duration.wrap(7 days); Duration internal constant GAME_DURATION = Duration.wrap(7 days);
...@@ -55,9 +59,14 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone { ...@@ -55,9 +59,14 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone {
mapping(ClaimHash => bool) internal claims; mapping(ClaimHash => bool) internal claims;
/// @param _absolutePrestate The absolute prestate of the instruction trace. /// @param _absolutePrestate The absolute prestate of the instruction trace.
constructor(Claim _absolutePrestate, uint256 _maxGameDepth) { constructor(
Claim _absolutePrestate,
uint256 _maxGameDepth,
IBigStepper _vm
) {
ABSOLUTE_PRESTATE = _absolutePrestate; ABSOLUTE_PRESTATE = _absolutePrestate;
MAX_GAME_DEPTH = _maxGameDepth; MAX_GAME_DEPTH = _maxGameDepth;
VM = _vm;
} }
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
...@@ -79,8 +88,8 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone { ...@@ -79,8 +88,8 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone {
uint256 _stateIndex, uint256 _stateIndex,
uint256 _claimIndex, uint256 _claimIndex,
bool _isAttack, bool _isAttack,
bytes calldata, bytes calldata _stateData,
bytes calldata bytes calldata _proof
) external { ) external {
// Steps cannot be made unless the game is currently in progress. // Steps cannot be made unless the game is currently in progress.
if (status != GameStatus.IN_PROGRESS) { if (status != GameStatus.IN_PROGRESS) {
...@@ -127,19 +136,19 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone { ...@@ -127,19 +136,19 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone {
postStateClaim = claimData[_stateIndex].claim; postStateClaim = claimData[_stateIndex].claim;
} }
// Assert that the given prestate commits to the instruction at `gindex - 1`. // Assert that the given prestate commits to the instruction at `gindex - 1` and
// that the `_stateData` is the preimage for the prestate claim digest.
if ( if (
Position.unwrap(preStatePos.rightIndex(MAX_GAME_DEPTH)) != Position.unwrap(preStatePos.rightIndex(MAX_GAME_DEPTH)) !=
Position.unwrap(postStatePos.rightIndex(MAX_GAME_DEPTH)) - 1 Position.unwrap(postStatePos.rightIndex(MAX_GAME_DEPTH)) - 1 ||
keccak256(_stateData) != Claim.unwrap(preStateClaim)
) { ) {
revert InvalidPrestate(); revert InvalidPrestate();
} }
} }
// TODO: Call `MIPS.sol#step` to verify the step. // Perform the VM step and check to see if it is valid.
// For now, we just use a simple state transition function that increments the prestate, if (VM.step(_stateData, _proof) == Claim.unwrap(postStateClaim)) {
// `s_p`, by 1.
if (uint256(Claim.unwrap(preStateClaim)) + 1 == uint256(Claim.unwrap(postStateClaim))) {
revert ValidStep(); revert ValidStep();
} }
...@@ -274,7 +283,7 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone { ...@@ -274,7 +283,7 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone {
// Search for the left-most dangling non-bottom node // Search for the left-most dangling non-bottom node
// The most recent claim is always a dangling, non-bottom node so we start with that // The most recent claim is always a dangling, non-bottom node so we start with that
uint256 leftMostIndex = claimData.length - 1; uint256 leftMostIndex = claimData.length - 1;
Position leftMostTraceIndex = Position.wrap(type(uint128).max); uint256 leftMostTraceIndex = type(uint128).max;
for (uint256 i = leftMostIndex; i < type(uint64).max; ) { for (uint256 i = leftMostIndex; i < type(uint64).max; ) {
// Fetch the claim at the current index. // Fetch the claim at the current index.
ClaimData storage claim = claimData[i]; ClaimData storage claim = claimData[i];
...@@ -295,8 +304,8 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone { ...@@ -295,8 +304,8 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone {
// If the claim is a dangling node, we can check if it is the left-most // If the claim is a dangling node, we can check if it is the left-most
// dangling node we've come across so far. If it is, we can update the // dangling node we've come across so far. If it is, we can update the
// left-most trace index. // left-most trace index.
Position traceIndex = claimPos.rightIndex(MAX_GAME_DEPTH); uint256 traceIndex = claimPos.traceIndex(MAX_GAME_DEPTH);
if (Position.unwrap(traceIndex) < Position.unwrap(leftMostTraceIndex)) { if (traceIndex < leftMostTraceIndex) {
leftMostTraceIndex = traceIndex; leftMostTraceIndex = traceIndex;
unchecked { unchecked {
leftMostIndex = i + 1; leftMostIndex = i + 1;
...@@ -309,7 +318,7 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone { ...@@ -309,7 +318,7 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone {
if ( if (
// slither-disable-next-line weak-prng // slither-disable-next-line weak-prng
claimData[leftMostIndex].position.depth() % 2 == 0 && claimData[leftMostIndex].position.depth() % 2 == 0 &&
Position.unwrap(leftMostTraceIndex) != type(uint128).max leftMostTraceIndex != type(uint128).max
) { ) {
status_ = GameStatus.DEFENDER_WINS; status_ = GameStatus.DEFENDER_WINS;
} else { } else {
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
/// @title IBigStepper
/// @notice An interface for a contract with a state transition function that
/// will accept a pre state and return a post state.
/// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
/// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣼⠶⢅⠒⢄⢔⣶⡦⣤⡤⠄⣀⠀⠀⠀⠀⠀⠀⠀
/// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠨⡏⠀⠀⠈⠢⣙⢯⣄⠀⢨⠯⡺⡘⢄⠀⠀⠀⠀⠀
/// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣶⡆⠀⠀⠀⠀⠈⠓⠬⡒⠡⣀⢙⡜⡀⠓⠄⠀⠀⠀
/// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⡷⠿⣧⣀⡀⠀⠀⠀⠀⠀⠀⠉⠣⣞⠩⠥⠀⠼⢄⠀⠀
/// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⡇⠀⠀⠀⠉⢹⣶⠒⠒⠂⠈⠉⠁⠘⡆⠀⣿⣿⠫⡄⠀
/// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⢶⣤⣀⡀⠀⠀⢸⡿⠀⠀⠀⠀⠀⢀⠞⠀⠀⢡⢨⢀⡄⠀
/// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⡒⣿⢿⡤⠝⡣⠉⠁⠚⠛⠀⠤⠤⣄⡰⠁⠀⠀⠀⠉⠙⢸⠀⠀
/// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡤⢯⡌⡿⡇⠘⡷⠀⠁⠀⠀⢀⣰⠢⠲⠛⣈⣸⠦⠤⠶⠴⢬⣐⣊⡂⠀
/// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣤⡪⡗⢫⠞⠀⠆⣀⠻⠤⠴⠐⠚⣉⢀⠦⠂⠋⠁⠀⠁⠀⠀⠀⠀⢋⠉⠇⠀
/// ⠀⠀⠀⠀⣀⡤⠐⠒⠘⡹⠉⢸⠇⠸⠀⠀⠀⠀⣀⣤⠴⠚⠉⠈⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠼⠀⣾⠀
/// ⠀⠀⠀⡰⠀⠉⠉⠀⠁⠀⠀⠈⢇⠈⠒⠒⠘⠈⢀⢡⡂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⠀⢸⡄
/// ⠀⠀⠸⣿⣆⠤⢀⡀⠀⠀⠀⠀⢘⡌⠀⠀⣀⣀⣀⡈⣤⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⢸⡇
/// ⠀⠀⢸⣀⠀⠉⠒⠐⠛⠋⠭⠭⠍⠉⠛⠒⠒⠒⠀⠒⠚⠛⠛⠛⠩⠭⠭⠭⠭⠤⠤⠤⠤⠤⠭⠭⠉⠓⡆
/// ⠀⠀⠘⠿⣷⣶⣤⣤⣀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣤⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇
/// ⠀⠀⠀⠀⠀⠉⠙⠛⠛⠻⠿⢿⣿⣿⣷⣶⣶⣶⣤⣤⣀⣁⣛⣃⣒⠿⠿⠿⠤⠠⠄⠤⠤⢤⣛⣓⣂⣻⡇
/// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠉⠉⠙⠛⠻⠿⠿⠿⢿⣿⣿⣿⣷⣶⣶⣾⣿⣿⣿⣿⠿⠟⠁
/// ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠈⠉⠉⠉⠉⠁⠀⠀⠀⠀⠀
interface IBigStepper {
/// @notice Performs a single instruction step from a given prestate and returns the poststate
/// hash.
/// @param _stateData The preimage of the prestate hash.
/// @param _proof A proof for the inclusion of the prestate's memory in the merkle tree.
/// @return postState_ The poststate hash after the instruction step.
function step(bytes calldata _stateData, bytes calldata _proof)
external
returns (bytes32 postState_);
}
...@@ -104,6 +104,26 @@ library LibPosition { ...@@ -104,6 +104,26 @@ library LibPosition {
} }
} }
/// @notice Get the deepest, right most trace index relative to the `position`. This is
/// equivalent to calling `right` on a position until the maximum depth is reached and
/// then finding its index at depth.
/// @param _position The position to get the relative trace index of.
/// @param _maxDepth The maximum depth of the game.
/// @return traceIndex_ The trace index relative to the `position`.
function traceIndex(
Position _position,
uint256 _maxDepth
) internal pure returns (uint256 traceIndex_) {
uint256 msb = depth(_position);
assembly {
let remaining := sub(_maxDepth, msb)
traceIndex_ := sub(
or(shl(remaining, _position), sub(shl(remaining, 1), 1)),
shl(_maxDepth, 1)
)
}
}
/// @notice Get the move position of `_position`, which is the left child of: /// @notice Get the move position of `_position`, which is the left child of:
/// 1. `_position + 1` if `_isAttack` is true. /// 1. `_position + 1` if `_isAttack` is true.
/// 1. `_position` if `_isAttack` is false. /// 1. `_position` if `_isAttack` is false.
......
...@@ -15,13 +15,15 @@ ...@@ -15,13 +15,15 @@
], ],
"scripts": { "scripts": {
"bindings": "cd ../../op-bindings && make", "bindings": "cd ../../op-bindings && make",
"build:forge": "forge build",
"build:with-metadata": "FOUNDRY_PROFILE=echidna yarn build:forge", "build:with-metadata": "FOUNDRY_PROFILE=echidna yarn build:forge",
"build": "npx nx build:ts && npx nx typechain",
"prebuild:contracts": "yarn ts-node scripts/verify-foundry-install.ts",
"build:contracts": "yarn build:hardhat",
"build:forge": "forge build",
"build:hardhat": "hardhat compile",
"build:ts": "tsc -p tsconfig.build.json",
"build:differential": "go build -o ./scripts/differential-testing/differential-testing ./scripts/differential-testing", "build:differential": "go build -o ./scripts/differential-testing/differential-testing ./scripts/differential-testing",
"build:fuzz": "(cd test-case-generator && go build ./cmd/fuzz.go)", "build:fuzz": "(cd test-case-generator && go build ./cmd/fuzz.go)",
"prebuild": "yarn ts-node scripts/verify-foundry-install.ts",
"build": "hardhat compile && yarn autogen:artifacts && yarn build:ts && yarn typechain",
"build:ts": "tsc -p tsconfig.build.json",
"autogen:artifacts": "ts-node scripts/generate-artifacts.ts", "autogen:artifacts": "ts-node scripts/generate-artifacts.ts",
"autogen:invariant-docs": "ts-node scripts/invariant-doc-gen.ts", "autogen:invariant-docs": "ts-node scripts/invariant-doc-gen.ts",
"deploy": "hardhat deploy", "deploy": "hardhat deploy",
......
...@@ -17,10 +17,7 @@ yarn build ...@@ -17,10 +17,7 @@ yarn build
## Running the service ## 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 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. a custom op chain, the `OptimismPortal` contract addresses must also be set associated with the op-chain.
- Bedrock: `OptimismPortal`
- Legacy: `StateCommitmentChain`
Once your environment variables or flags have been set, run the service via: Once your environment variables or flags have been set, run the service via:
...@@ -40,13 +37,11 @@ yarn start ...@@ -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. 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). 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` - ***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. 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. 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. 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`: Check the list of available metrics via `yarn start --help`:
...@@ -62,9 +57,7 @@ Options: ...@@ -62,9 +57,7 @@ Options:
--l2rpcprovider Provider for interacting with L2 (env: FAULT_DETECTOR__L2_RPC_PROVIDER) --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) --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) --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 OP Chains] Deployed OptimismPortal contract address. Used to retrieve necessary info for ouput verification (env: FAULT_DETECTOR__OPTIMISM_PORTAL_ADDRESS, default 0x0)
--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)
--port Port for the app server (env: FAULT_DETECTOR__PORT) --port Port for the app server (env: FAULT_DETECTOR__PORT)
--hostname Hostname for the app server (env: FAULT_DETECTOR__HOSTNAME) --hostname Hostname for the app server (env: FAULT_DETECTOR__HOSTNAME)
......
...@@ -51,7 +51,7 @@ ...@@ -51,7 +51,7 @@
}, },
"dependencies": { "dependencies": {
"@eth-optimism/common-ts": "^0.8.2", "@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/core-utils": "^0.12.1",
"@eth-optimism/sdk": "^3.0.0", "@eth-optimism/sdk": "^3.0.0",
"@ethersproject/abstract-provider": "^5.7.0" "@ethersproject/abstract-provider": "^5.7.0"
......
import { Contract, BigNumber } from 'ethers' import { Contract } from 'ethers'
import { Logger } from '@eth-optimism/common-ts' 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 * Partial event interface, meant to reduce the size of the event cache to avoid
* running out of memory. * running out of memory.
...@@ -54,12 +47,12 @@ const getCache = ( ...@@ -54,12 +47,12 @@ const getCache = (
* @param contract Contract to update cache for. * @param contract Contract to update cache for.
* @param filter Event filter to use. * @param filter Event filter to use.
*/ */
export const updateOracleCache = async <TSubmissionEventArgs>( export const updateOracleCache = async (
oracle: OutputOracle<TSubmissionEventArgs>, oracle: Contract,
logger?: Logger logger?: Logger
): Promise<void> => { ): Promise<void> => {
const cache = getCache(oracle.contract.address) const cache = getCache(oracle.address)
const endBlock = await oracle.contract.provider.getBlockNumber() const endBlock = await oracle.provider.getBlockNumber()
logger?.info('visiting uncached oracle events for range', { logger?.info('visiting uncached oracle events for range', {
node: 'l1', node: 'l1',
cachedUntilBlock: cache.highestBlock, cachedUntilBlock: cache.highestBlock,
...@@ -77,17 +70,15 @@ export const updateOracleCache = async <TSubmissionEventArgs>( ...@@ -77,17 +70,15 @@ export const updateOracleCache = async <TSubmissionEventArgs>(
blockRangeSize: step, blockRangeSize: step,
}) })
const events = await oracle.contract.queryFilter( const events = await oracle.queryFilter(
oracle.filter, oracle.filters.OutputProposed(),
currentBlock, currentBlock,
currentBlock + step currentBlock + step
) )
// Throw the events into the cache. // Throw the events into the cache.
for (const event of events) { for (const event of events) {
cache.eventCache[ cache.eventCache[event.args.l2OutputIndex.toNumber()] = {
oracle.getEventIndex(event.args as TSubmissionEventArgs).toNumber()
] = {
blockNumber: event.blockNumber, blockNumber: event.blockNumber,
transactionHash: event.transactionHash, transactionHash: event.transactionHash,
args: event.args, args: event.args,
...@@ -135,12 +126,12 @@ export const updateOracleCache = async <TSubmissionEventArgs>( ...@@ -135,12 +126,12 @@ export const updateOracleCache = async <TSubmissionEventArgs>(
* @param index State batch index to search for. * @param index State batch index to search for.
* @returns Event corresponding to the batch. * @returns Event corresponding to the batch.
*/ */
export const findEventForStateBatch = async <TSubmissionEventArgs>( export const findEventForStateBatch = async (
oracle: OutputOracle<TSubmissionEventArgs>, oracle: Contract,
index: number, index: number,
logger?: Logger logger?: Logger
): Promise<PartialEvent> => { ): Promise<PartialEvent> => {
const cache = getCache(oracle.contract.address) const cache = getCache(oracle.address)
// Try to find the event in cache first. // Try to find the event in cache first.
if (cache.eventCache[index]) { if (cache.eventCache[index]) {
...@@ -166,13 +157,13 @@ export const findEventForStateBatch = async <TSubmissionEventArgs>( ...@@ -166,13 +157,13 @@ export const findEventForStateBatch = async <TSubmissionEventArgs>(
* @param oracle Output oracle contract. * @param oracle Output oracle contract.
* @returns Starting state root batch index. * @returns Starting state root batch index.
*/ */
export const findFirstUnfinalizedStateBatchIndex = async <TSubmissionEventArgs>( export const findFirstUnfinalizedStateBatchIndex = async (
oracle: OutputOracle<TSubmissionEventArgs>, oracle: Contract,
fpw: number, fpw: number,
logger?: Logger logger?: Logger
): Promise<number> => { ): Promise<number> => {
const latestBlock = await oracle.contract.provider.getBlock('latest') const latestBlock = await oracle.provider.getBlock('latest')
const totalBatches = (await oracle.getTotalElements()).toNumber() const totalBatches = (await oracle.nextOutputIndex()).toNumber()
// Perform a binary search to find the next batch that will pass the challenge period. // Perform a binary search to find the next batch that will pass the challenge period.
let lo = 0 let lo = 0
...@@ -180,7 +171,7 @@ export const findFirstUnfinalizedStateBatchIndex = async <TSubmissionEventArgs>( ...@@ -180,7 +171,7 @@ export const findFirstUnfinalizedStateBatchIndex = async <TSubmissionEventArgs>(
while (lo !== hi) { while (lo !== hi) {
const mid = Math.floor((lo + hi) / 2) const mid = Math.floor((lo + hi) / 2)
const event = await findEventForStateBatch(oracle, mid, logger) 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) { if (block.timestamp + fpw < latestBlock.timestamp) {
lo = mid + 1 lo = mid + 1
......
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment