Commit 69e67474 authored by protolambda's avatar protolambda

Merge branch 'develop' into cannon

parents d35fa879 0c1a113d
---
'@eth-optimism/fault-detector': patch
---
Fix false error to warning
...@@ -24,6 +24,7 @@ on: ...@@ -24,6 +24,7 @@ on:
- proxyd - proxyd
- indexer - indexer
- fault-detector - fault-detector
- ci-builder
prerelease: prerelease:
description: Increment major/minor/patch as prerelease? description: Increment major/minor/patch as prerelease?
required: false required: false
......
...@@ -32,6 +32,8 @@ bindings: l1block-bindings \ ...@@ -32,6 +32,8 @@ bindings: l1block-bindings \
basefee-vault-bindings \ basefee-vault-bindings \
legacy-erc20-eth-bindings \ legacy-erc20-eth-bindings \
dispute-game-factory-bindings \ dispute-game-factory-bindings \
standard-bridge-bindings \
cross-domain-messenger-bindings \
cannon-mips \ cannon-mips \
cannon-oracle cannon-oracle
...@@ -128,12 +130,19 @@ l1-blocknumber-bindings: compile ...@@ -128,12 +130,19 @@ l1-blocknumber-bindings: compile
dispute-game-factory-bindings: compile dispute-game-factory-bindings: compile
./gen_bindings.sh contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory $(pkg) ./gen_bindings.sh contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory $(pkg)
standard-bridge-bindings: compile
./gen_bindings.sh contracts/universal/StandardBridge.sol:StandardBridge $(pkg)
cross-domain-messenger-bindings: compile
./gen_bindings.sh contracts/universal/CrossDomainMessenger.sol:CrossDomainMessenger $(pkg)
cannon-mips: compile cannon-mips: compile
./gen_bindings.sh contracts/cannon/MIPS.sol:MIPS $(pkg) ./gen_bindings.sh contracts/cannon/MIPS.sol:MIPS $(pkg)
cannon-oracle: compile cannon-oracle: compile
./gen_bindings.sh contracts/cannon/Oracle.sol:Oracle $(pkg) ./gen_bindings.sh contracts/cannon/Oracle.sol:Oracle $(pkg)
more: more:
go run ./gen/main.go \ go run ./gen/main.go \
-artifacts ../packages/contracts-bedrock/artifacts \ -artifacts ../packages/contracts-bedrock/artifacts \
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -27,11 +27,11 @@ CrossDomainOwnable_Test:test_onlyOwner_notOwner_reverts() (gas: 10597) ...@@ -27,11 +27,11 @@ CrossDomainOwnable_Test:test_onlyOwner_notOwner_reverts() (gas: 10597)
CrossDomainOwnable_Test:test_onlyOwner_succeeds() (gas: 34883) CrossDomainOwnable_Test:test_onlyOwner_succeeds() (gas: 34883)
DeployerWhitelist_Test:test_owner_succeeds() (gas: 7582) DeployerWhitelist_Test:test_owner_succeeds() (gas: 7582)
DeployerWhitelist_Test:test_storageSlots_succeeds() (gas: 33395) DeployerWhitelist_Test:test_storageSlots_succeeds() (gas: 33395)
DisputeGameFactory_Test:test_owner_succeeds() (gas: 12610) DisputeGameFactory_Owner_Test:test_owner_succeeds() (gas: 12559)
DisputeGameFactory_Test:test_setImplementation_notOwner_reverts() (gas: 16099) DisputeGameFactory_SetImplementation_Test:test_setImplementation_notOwner_reverts() (gas: 16042)
DisputeGameFactory_Test:test_setImplementation_succeeds() (gas: 44302) DisputeGameFactory_SetImplementation_Test:test_setImplementation_succeeds() (gas: 44243)
DisputeGameFactory_Test:test_transferOwnership_notOwner_reverts() (gas: 15974) DisputeGameFactory_TransferOwnership_Test:test_transferOwnership_notOwner_reverts() (gas: 15950)
DisputeGameFactory_Test:test_transferOwnership_succeeds() (gas: 18694) DisputeGameFactory_TransferOwnership_Test:test_transferOwnership_succeeds() (gas: 18642)
FaultDisputeGame_Test:test_clockTimeExceeded_reverts() (gas: 26413) FaultDisputeGame_Test:test_clockTimeExceeded_reverts() (gas: 26413)
FaultDisputeGame_Test:test_defendRoot_reverts() (gas: 13258) FaultDisputeGame_Test:test_defendRoot_reverts() (gas: 13258)
FaultDisputeGame_Test:test_duplicateClaim_reverts() (gas: 103259) FaultDisputeGame_Test:test_duplicateClaim_reverts() (gas: 103259)
......
...@@ -253,13 +253,13 @@ ...@@ -253,13 +253,13 @@
➡ contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory ➡ contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory
======================= =======================
| Name | Type | Slot | Offset | Bytes | Contract | | Name | Type | Slot | Offset | Bytes | Contract |
|-----------------|--------------------------------------------|------|--------|-------|-------------------------------------------------------------| |------------------|--------------------------------------------|------|--------|-------|-------------------------------------------------------------|
| _initialized | uint8 | 0 | 0 | 1 | contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory | | _initialized | uint8 | 0 | 0 | 1 | contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory |
| _initializing | bool | 0 | 1 | 1 | contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory | | _initializing | bool | 0 | 1 | 1 | contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory |
| __gap | uint256[50] | 1 | 0 | 1600 | contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory | | __gap | uint256[50] | 1 | 0 | 1600 | contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory |
| _owner | address | 51 | 0 | 20 | contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory | | _owner | address | 51 | 0 | 20 | contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory |
| __gap | uint256[49] | 52 | 0 | 1568 | contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory | | __gap | uint256[49] | 52 | 0 | 1568 | contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory |
| gameImpls | mapping(GameType => contract IDisputeGame) | 101 | 0 | 32 | contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory | | gameImpls | mapping(GameType => contract IDisputeGame) | 101 | 0 | 32 | contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory |
| disputeGames | mapping(Hash => contract IDisputeGame) | 102 | 0 | 32 | contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory | | _disputeGames | mapping(Hash => GameId) | 102 | 0 | 32 | contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory |
| disputeGameList | contract IDisputeGame[] | 103 | 0 | 32 | contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory | | _disputeGameList | GameId[] | 103 | 0 | 32 | contracts/dispute/DisputeGameFactory.sol:DisputeGameFactory |
...@@ -15,7 +15,11 @@ import { IVersioned } from "./interfaces/IVersioned.sol"; ...@@ -15,7 +15,11 @@ import { IVersioned } from "./interfaces/IVersioned.sol";
/** /**
* @title DisputeGameFactory * @title DisputeGameFactory
* @notice A factory contract for creating `IDisputeGame` contracts. * @notice A factory contract for creating `IDisputeGame` contracts. All created dispute games
* are stored in both a mapping and an append only array. The timestamp of the creation
* time of the dispute game is packed tightly into the storage slot with the address of
* the dispute game. This is to make offchain discoverability of playable dispute games
* easier.
*/ */
contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, IVersioned { contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, IVersioned {
/** /**
...@@ -29,18 +33,18 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, IVersion ...@@ -29,18 +33,18 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, IVersion
mapping(GameType => IDisputeGame) public gameImpls; mapping(GameType => IDisputeGame) public gameImpls;
/** /**
* @notice Mapping of a hash of `gameType . rootClaim . extraData` to * @notice Mapping of a hash of `gameType || rootClaim || extraData` to
* the deployed `IDisputeGame` clone. * the deployed `IDisputeGame` clone.
* @dev Note: `.` denotes concatenation. * @dev Note: `||` denotes concatenation.
*/ */
mapping(Hash => IDisputeGame) internal disputeGames; mapping(Hash => GameId) internal _disputeGames;
/** /**
* @notice An append-only array of disputeGames that have been created. * @notice An append-only array of disputeGames that have been created.
* @dev This accessor is used by offchain game solvers to efficiently * @dev This accessor is used by offchain game solvers to efficiently
* track dispute games * track dispute games
*/ */
IDisputeGame[] public disputeGameList; GameId[] internal _disputeGameList;
/** /**
* @notice Constructs a new DisputeGameFactory contract. * @notice Constructs a new DisputeGameFactory contract.
...@@ -60,27 +64,46 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, IVersion ...@@ -60,27 +64,46 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, IVersion
/** /**
* @inheritdoc IVersioned * @inheritdoc IVersioned
* @custom:semver 0.0.2
*/ */
function version() external pure returns (string memory) { function version() external pure returns (string memory) {
return "0.0.1"; return "0.0.2";
} }
/** /**
* @inheritdoc IDisputeGameFactory * @inheritdoc IDisputeGameFactory
*/ */
function gameCount() external view returns (uint256 _gameCount) { function gameCount() external view returns (uint256 gameCount_) {
_gameCount = disputeGameList.length; gameCount_ = _disputeGameList.length;
} }
/** /**
* @inheritdoc IDisputeGameFactory * @inheritdoc IDisputeGameFactory
*/ */
function games( function games(
GameType gameType, GameType _gameType,
Claim rootClaim, Claim _rootClaim,
bytes calldata extraData bytes calldata _extraData
) external view returns (IDisputeGame _proxy) { ) external view returns (IDisputeGame proxy_, uint256 timestamp_) {
return disputeGames[getGameUUID(gameType, rootClaim, extraData)]; Hash uuid = getGameUUID(_gameType, _rootClaim, _extraData);
GameId slot = _disputeGames[uuid];
(address addr, uint256 timestamp) = _unpackSlot(slot);
proxy_ = IDisputeGame(addr);
timestamp_ = timestamp;
}
/**
* @inheritdoc IDisputeGameFactory
*/
function gameAtIndex(uint256 _index)
external
view
returns (IDisputeGame proxy_, uint256 timestamp_)
{
GameId slot = _disputeGameList[_index];
(address addr, uint256 timestamp) = _unpackSlot(slot);
proxy_ = IDisputeGame(addr);
timestamp_ = timestamp;
} }
/** /**
...@@ -107,13 +130,15 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, IVersion ...@@ -107,13 +130,15 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, IVersion
Hash uuid = getGameUUID(gameType, rootClaim, extraData); Hash uuid = getGameUUID(gameType, rootClaim, extraData);
// If a dispute game with the same UUID already exists, revert. // If a dispute game with the same UUID already exists, revert.
if (address(disputeGames[uuid]) != address(0)) { if (GameId.unwrap(_disputeGames[uuid]) != bytes32(0)) {
revert GameAlreadyExists(uuid); revert GameAlreadyExists(uuid);
} }
GameId slot = _packSlot(address(proxy), block.timestamp);
// Store the dispute game in the mapping & emit the `DisputeGameCreated` event. // Store the dispute game in the mapping & emit the `DisputeGameCreated` event.
disputeGames[uuid] = proxy; _disputeGames[uuid] = slot;
disputeGameList.push(proxy); _disputeGameList.push(slot);
emit DisputeGameCreated(address(proxy), gameType, rootClaim); emit DisputeGameCreated(address(proxy), gameType, rootClaim);
} }
...@@ -163,4 +188,24 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, IVersion ...@@ -163,4 +188,24 @@ contract DisputeGameFactory is OwnableUpgradeable, IDisputeGameFactory, IVersion
gameImpls[gameType] = impl; gameImpls[gameType] = impl;
emit ImplementationSet(address(impl), gameType); emit ImplementationSet(address(impl), gameType);
} }
/**
* @dev Packs an address and a uint256 into a single bytes32 slot. This
* is only safe for up to uint96 values.
*/
function _packSlot(address _addr, uint256 _num) internal pure returns (GameId slot_) {
assembly {
slot_ := or(shl(0xa0, _num), _addr)
}
}
/**
* @dev Unpacks an address and a uint256 from a single bytes32 slot.
*/
function _unpackSlot(GameId _slot) internal pure returns (address addr_, uint256 num_) {
assembly {
addr_ := and(_slot, 0xffffffffffffffffffffffffffffffffffffffff)
num_ := shr(0xa0, _slot)
}
}
} }
...@@ -44,12 +44,26 @@ interface IDisputeGameFactory { ...@@ -44,12 +44,26 @@ interface IDisputeGameFactory {
* @param extraData Any extra data that should be provided to the created dispute game. * @param extraData Any extra data that should be provided to the created dispute game.
* @return _proxy The clone of the `DisputeGame` created with the given parameters. * @return _proxy The clone of the `DisputeGame` created with the given parameters.
* Returns `address(0)` if nonexistent. * Returns `address(0)` if nonexistent.
* @return _timestamp The timestamp of the creation of the dispute game.
*/ */
function games( function games(
GameType gameType, GameType gameType,
Claim rootClaim, Claim rootClaim,
bytes calldata extraData bytes calldata extraData
) external view returns (IDisputeGame _proxy); ) external view returns (IDisputeGame _proxy, uint256 _timestamp);
/**
* @notice `gameAtIndex` returns the dispute game contract address and its creation timestamp
* at the given index. Each created dispute game increments the underlying index.
* @param _index The index of the dispute game.
* @return _proxy The clone of the `DisputeGame` created with the given parameters.
* Returns `address(0)` if nonexistent.
* @return _timestamp The timestamp of the creation of the dispute game.
*/
function gameAtIndex(uint256 _index)
external
view
returns (IDisputeGame _proxy, uint256 _timestamp);
/** /**
* @notice `gameImpls` is a mapping that maps `GameType`s to their respective * @notice `gameImpls` is a mapping that maps `GameType`s to their respective
......
...@@ -33,6 +33,18 @@ type Timestamp is uint64; ...@@ -33,6 +33,18 @@ type Timestamp is uint64;
*/ */
type Duration is uint64; type Duration is uint64;
/**
* @notice A `GameId` represents a packed 12 byte timestamp and a 20 byte address.
* @dev The packed layout of this type is as follows:
* ┌────────────┬────────────────┐
* │ Bits │ Value │
* ├────────────┼────────────────┤
* │ [0, 96) │ Timestamp │
* │ [96, 256) │ Address │
* └────────────┴────────────────┘
*/
type GameId is bytes32;
/** /**
* @notice A `Clock` represents a packed `Duration` and `Timestamp` * @notice A `Clock` represents a packed `Duration` and `Timestamp`
* @dev The packed layout of this type is as follows: * @dev The packed layout of this type is as follows:
......
...@@ -9,8 +9,9 @@ import { DisputeGameFactory } from "../dispute/DisputeGameFactory.sol"; ...@@ -9,8 +9,9 @@ import { DisputeGameFactory } from "../dispute/DisputeGameFactory.sol";
import { IDisputeGame } from "../dispute/interfaces/IDisputeGame.sol"; import { IDisputeGame } from "../dispute/interfaces/IDisputeGame.sol";
import { Proxy } from "../universal/Proxy.sol"; import { Proxy } from "../universal/Proxy.sol";
contract DisputeGameFactory_Initializer is Test { contract DisputeGameFactory_Init is Test {
DisputeGameFactory factory; DisputeGameFactory factory;
FakeClone fakeClone;
event DisputeGameCreated( event DisputeGameCreated(
address indexed disputeProxy, address indexed disputeProxy,
...@@ -30,17 +31,12 @@ contract DisputeGameFactory_Initializer is Test { ...@@ -30,17 +31,12 @@ contract DisputeGameFactory_Initializer is Test {
}); });
factory = DisputeGameFactory(address(proxy)); factory = DisputeGameFactory(address(proxy));
vm.label(address(factory), "DisputeGameFactoryProxy"); vm.label(address(factory), "DisputeGameFactoryProxy");
}
}
contract DisputeGameFactory_Test is DisputeGameFactory_Initializer {
FakeClone fakeClone;
function setUp() public override {
super.setUp();
fakeClone = new FakeClone(); fakeClone = new FakeClone();
} }
}
contract DisputeGameFactory_Create_Test is DisputeGameFactory_Init {
/** /**
* @dev Tests that the `create` function succeeds when creating a new dispute game * @dev Tests that the `create` function succeeds when creating a new dispute game
* with a `GameType` that has an implementation set. * with a `GameType` that has an implementation set.
...@@ -62,10 +58,16 @@ contract DisputeGameFactory_Test is DisputeGameFactory_Initializer { ...@@ -62,10 +58,16 @@ contract DisputeGameFactory_Test is DisputeGameFactory_Initializer {
emit DisputeGameCreated(address(0), gt, rootClaim); emit DisputeGameCreated(address(0), gt, rootClaim);
IDisputeGame proxy = factory.create(gt, rootClaim, extraData); IDisputeGame proxy = factory.create(gt, rootClaim, extraData);
(IDisputeGame game, uint256 timestamp) = factory.games(gt, rootClaim, extraData);
// Ensure that the dispute game was assigned to the `disputeGames` mapping. // Ensure that the dispute game was assigned to the `disputeGames` mapping.
assertEq(address(factory.games(gt, rootClaim, extraData)), address(proxy)); assertEq(address(game), address(proxy));
assertEq(timestamp, block.timestamp);
assertEq(factory.gameCount(), 1); assertEq(factory.gameCount(), 1);
assertEq(address(factory.disputeGameList(0)), address(proxy));
(IDisputeGame game2, uint256 timestamp2) = factory.gameAtIndex(0);
assertEq(address(game2), address(proxy));
assertEq(timestamp2, block.timestamp);
} }
/** /**
...@@ -105,8 +107,10 @@ contract DisputeGameFactory_Test is DisputeGameFactory_Initializer { ...@@ -105,8 +107,10 @@ contract DisputeGameFactory_Test is DisputeGameFactory_Initializer {
emit DisputeGameCreated(address(0), gt, rootClaim); emit DisputeGameCreated(address(0), gt, rootClaim);
IDisputeGame proxy = factory.create(gt, rootClaim, extraData); IDisputeGame proxy = factory.create(gt, rootClaim, extraData);
(IDisputeGame game, uint256 timestamp) = factory.games(gt, rootClaim, extraData);
// Ensure that the dispute game was assigned to the `disputeGames` mapping. // Ensure that the dispute game was assigned to the `disputeGames` mapping.
assertEq(address(factory.games(gt, rootClaim, extraData)), address(proxy)); assertEq(address(game), address(proxy));
assertEq(timestamp, block.timestamp);
// Ensure that the `create` function reverts when called with parameters that would result in the same UUID. // Ensure that the `create` function reverts when called with parameters that would result in the same UUID.
vm.expectRevert( vm.expectRevert(
...@@ -117,7 +121,9 @@ contract DisputeGameFactory_Test is DisputeGameFactory_Initializer { ...@@ -117,7 +121,9 @@ contract DisputeGameFactory_Test is DisputeGameFactory_Initializer {
); );
factory.create(gt, rootClaim, extraData); factory.create(gt, rootClaim, extraData);
} }
}
contract DisputeGameFactory_SetImplementation_Test is DisputeGameFactory_Init {
/** /**
* @dev Tests that the `setImplementation` function properly sets the implementation for a given `GameType`. * @dev Tests that the `setImplementation` function properly sets the implementation for a given `GameType`.
*/ */
...@@ -144,7 +150,9 @@ contract DisputeGameFactory_Test is DisputeGameFactory_Initializer { ...@@ -144,7 +150,9 @@ contract DisputeGameFactory_Test is DisputeGameFactory_Initializer {
vm.expectRevert("Ownable: caller is not the owner"); vm.expectRevert("Ownable: caller is not the owner");
factory.setImplementation(GameTypes.FAULT, IDisputeGame(address(1))); factory.setImplementation(GameTypes.FAULT, IDisputeGame(address(1)));
} }
}
contract DisputeGameFactory_GetGameUUID_Test is DisputeGameFactory_Init {
/** /**
* @dev Tests that the `getGameUUID` function returns the correct hash when comparing * @dev Tests that the `getGameUUID` function returns the correct hash when comparing
* against the keccak256 hash of the abi-encoded parameters. * against the keccak256 hash of the abi-encoded parameters.
...@@ -162,14 +170,18 @@ contract DisputeGameFactory_Test is DisputeGameFactory_Initializer { ...@@ -162,14 +170,18 @@ contract DisputeGameFactory_Test is DisputeGameFactory_Initializer {
keccak256(abi.encode(gt, rootClaim, extraData)) keccak256(abi.encode(gt, rootClaim, extraData))
); );
} }
}
contract DisputeGameFactory_Owner_Test is DisputeGameFactory_Init {
/** /**
* @dev Tests that the `owner` function returns the correct address after deployment. * @dev Tests that the `owner` function returns the correct address after deployment.
*/ */
function test_owner_succeeds() public { function test_owner_succeeds() public {
assertEq(factory.owner(), address(this)); assertEq(factory.owner(), address(this));
} }
}
contract DisputeGameFactory_TransferOwnership_Test is DisputeGameFactory_Init {
/** /**
* @dev Tests that the `transferOwnership` function succeeds when called by the owner. * @dev Tests that the `transferOwnership` function succeeds when called by the owner.
*/ */
...@@ -188,6 +200,43 @@ contract DisputeGameFactory_Test is DisputeGameFactory_Initializer { ...@@ -188,6 +200,43 @@ contract DisputeGameFactory_Test is DisputeGameFactory_Initializer {
} }
} }
/**
* @title PackingTester
* @notice Exposes the internal packing functions so that they can be fuzzed
* in a roundtrip manner.
*/
contract PackingTester is DisputeGameFactory {
function packSlot(address _addr, uint256 _num) external pure returns (GameId) {
return _packSlot(_addr, _num);
}
function unpackSlot(GameId _slot) external pure returns (address, uint256) {
return _unpackSlot(_slot);
}
}
/**
* @title DisputeGameFactory_PackSlot_Test
* @notice Fuzzes the PackingTester contract
*/
contract DisputeGameFactory_PackSlot_Test is Test {
PackingTester tester;
function setUp() public {
tester = new PackingTester();
}
/**
* @dev Tests that the `packSlot` and `unpackSlot` functions roundtrip correctly.
*/
function testFuzz_packSlot_succeeds(address _addr, uint96 _num) public {
GameId slot = tester.packSlot(_addr, uint256(_num));
(address addr, uint256 num) = tester.unpackSlot(slot);
assertEq(addr, _addr);
assertEq(num, _num);
}
}
/** /**
* @dev A fake clone used for testing the `DisputeGameFactory` contract's `create` function. * @dev A fake clone used for testing the `DisputeGameFactory` contract's `create` function.
*/ */
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
pragma solidity ^0.8.15; pragma solidity ^0.8.15;
import { Test } from "forge-std/Test.sol"; import { Test } from "forge-std/Test.sol";
import { DisputeGameFactory_Initializer } from "./DisputeGameFactory.t.sol"; import { DisputeGameFactory_Init } from "./DisputeGameFactory.t.sol";
import { DisputeGameFactory } from "../dispute/DisputeGameFactory.sol"; import { DisputeGameFactory } from "../dispute/DisputeGameFactory.sol";
import { FaultDisputeGame } from "../dispute/FaultDisputeGame.sol"; import { FaultDisputeGame } from "../dispute/FaultDisputeGame.sol";
...@@ -11,7 +11,7 @@ import "../libraries/DisputeErrors.sol"; ...@@ -11,7 +11,7 @@ import "../libraries/DisputeErrors.sol";
import { LibClock } from "../dispute/lib/LibClock.sol"; import { LibClock } from "../dispute/lib/LibClock.sol";
import { LibPosition } from "../dispute/lib/LibPosition.sol"; import { LibPosition } from "../dispute/lib/LibPosition.sol";
contract FaultDisputeGame_Test is DisputeGameFactory_Initializer { contract FaultDisputeGame_Test is DisputeGameFactory_Init {
/** /**
* @dev The root claim of the game. * @dev The root claim of the game.
*/ */
......
...@@ -4,7 +4,7 @@ import { URLSearchParams } from 'url' ...@@ -4,7 +4,7 @@ import { URLSearchParams } from 'url'
import { ethers, Contract } from 'ethers' import { ethers, Contract } from 'ethers'
import { Provider } from '@ethersproject/abstract-provider' import { Provider } from '@ethersproject/abstract-provider'
import { Signer } from '@ethersproject/abstract-signer' import { Signer } from '@ethersproject/abstract-signer'
import { awaitCondition, sleep } from '@eth-optimism/core-utils' import { sleep } from '@eth-optimism/core-utils'
import { HardhatRuntimeEnvironment } from 'hardhat/types' import { HardhatRuntimeEnvironment } from 'hardhat/types'
import { Deployment, DeployResult } from 'hardhat-deploy/dist/types' import { Deployment, DeployResult } from 'hardhat-deploy/dist/types'
import 'hardhat-deploy' import 'hardhat-deploy'
...@@ -323,35 +323,35 @@ export const printJsonTransaction = (tx: ethers.PopulatedTransaction): void => { ...@@ -323,35 +323,35 @@ export const printJsonTransaction = (tx: ethers.PopulatedTransaction): void => {
} }
/** /**
* Mini helper for transferring a Proxy to the MSD * Helper for transferring a Proxy to a target contract.
* *
* @param opts Options for executing the step. * @param opts Options for executing the step.
* @param opts.isLiveDeployer True if the deployer is live. * @param opts.isLiveDeployer True if the deployer is live.
* @param opts.proxy proxy contract. * @param opts.proxy proxy contract.
* @param opts.dictator dictator contract. * @param opts.target target contract.
*/ */
export const doOwnershipTransfer = async (opts: { export const doOwnershipTransfer = async (opts: {
isLiveDeployer?: boolean isLiveDeployer?: boolean
proxy: ethers.Contract proxy: ethers.Contract
name: string name: string
transferFunc: string transferFunc: string
dictator: ethers.Contract target: ethers.Contract
}): Promise<void> => { }): Promise<void> => {
if (opts.isLiveDeployer) { if (opts.isLiveDeployer) {
console.log(`Setting ${opts.name} owner to MSD`) console.log(`Setting ${opts.name} owner to target ${opts.target.address}`)
await opts.proxy[opts.transferFunc](opts.dictator.address) await opts.proxy[opts.transferFunc](opts.target.address)
} else { } else {
const tx = await opts.proxy.populateTransaction[opts.transferFunc]( const tx = await opts.proxy.populateTransaction[opts.transferFunc](
opts.dictator.address opts.target.address
) )
console.log(` console.log(`
Please transfer ${opts.name} (proxy) owner to MSD Please transfer ${opts.name} (proxy) owner to MSD
- ${opts.name} address: ${opts.proxy.address} - ${opts.name} address: ${opts.proxy.address}
- MSD address: ${opts.dictator.address} - target address: ${opts.target.address}
`) `)
printJsonTransaction(tx) printJsonTransaction(tx)
printCastCommand(tx) printCastCommand(tx)
await printTenderlySimulationLink(opts.dictator.provider, tx) await printTenderlySimulationLink(opts.target.provider, tx)
} }
} }
...@@ -377,146 +377,6 @@ export const liveDeployer = async (opts: { ...@@ -377,146 +377,6 @@ export const liveDeployer = async (opts: {
return ret return ret
} }
/**
* Mini helper for checking if the current step is a target step.
*
* @param dictator SystemDictator contract.
* @param step Target step.
* @returns True if the current step is the target step.
*/
export const isStep = async (
dictator: ethers.Contract,
step: number
): Promise<boolean> => {
return (await dictator.currentStep()) === step
}
/**
* Mini helper for checking if the current step is the first step in target phase.
*
* @param dictator SystemDictator contract.
* @param phase Target phase.
* @returns True if the current step is the first step in target phase.
*/
export const isStartOfPhase = async (
dictator: ethers.Contract,
phase: number
): Promise<boolean> => {
const phaseToStep = {
1: 1,
2: 3,
3: 6,
}
return (await dictator.currentStep()) === phaseToStep[phase]
}
/**
* Mini helper for executing a given step.
*
* @param opts Options for executing the step.
* @param opts.isLiveDeployer True if the deployer is live.
* @param opts.SystemDictator SystemDictator contract.
* @param opts.step Step to execute.
* @param opts.message Message to print before executing the step.
* @param opts.checks Checks to perform after executing the step.
*/
export const doStep = async (opts: {
isLiveDeployer?: boolean
SystemDictator: ethers.Contract
step: number
message: string
checks: () => Promise<void>
}): Promise<void> => {
const isStepVal = await isStep(opts.SystemDictator, opts.step)
if (!isStepVal) {
console.log(`Step already completed: ${opts.step}`)
return
}
// Extra message to help the user understand what's going on.
console.log(opts.message)
// Either automatically or manually execute the step.
if (opts.isLiveDeployer) {
console.log(`Executing step ${opts.step}...`)
await opts.SystemDictator[`step${opts.step}`]()
} else {
const tx = await opts.SystemDictator.populateTransaction[
`step${opts.step}`
]()
console.log(`Please execute step ${opts.step}...`)
console.log(`MSD address: ${opts.SystemDictator.address}`)
printJsonTransaction(tx)
printCastCommand(tx)
await printTenderlySimulationLink(opts.SystemDictator.provider, tx)
}
// Wait for the step to complete.
await awaitCondition(
async () => {
return isStep(opts.SystemDictator, opts.step + 1)
},
30000,
1000
)
// Perform post-step checks.
await opts.checks()
}
/**
* Mini helper for executing a given phase.
*
* @param opts Options for executing the step.
* @param opts.isLiveDeployer True if the deployer is live.
* @param opts.SystemDictator SystemDictator contract.
* @param opts.step Step to execute.
* @param opts.message Message to print before executing the step.
* @param opts.checks Checks to perform after executing the step.
*/
export const doPhase = async (opts: {
isLiveDeployer?: boolean
SystemDictator: ethers.Contract
phase: number
message: string
checks: () => Promise<void>
}): Promise<void> => {
const isStart = await isStartOfPhase(opts.SystemDictator, opts.phase)
if (!isStart) {
console.log(`Start of phase ${opts.phase} already completed`)
return
}
// Extra message to help the user understand what's going on.
console.log(opts.message)
// Either automatically or manually execute the step.
if (opts.isLiveDeployer) {
console.log(`Executing phase ${opts.phase}...`)
await opts.SystemDictator[`phase${opts.phase}`]()
} else {
const tx = await opts.SystemDictator.populateTransaction[
`phase${opts.phase}`
]()
console.log(`Please execute phase ${opts.phase}...`)
console.log(`MSD address: ${opts.SystemDictator.address}`)
printJsonTransaction(tx)
await printTenderlySimulationLink(opts.SystemDictator.provider, tx)
}
// Wait for the step to complete.
await awaitCondition(
async () => {
return isStartOfPhase(opts.SystemDictator, opts.phase + 1)
},
30000,
1000
)
// Perform post-step checks.
await opts.checks()
}
/** /**
* Prints a direct link to a Tenderly simulation. * Prints a direct link to a Tenderly simulation.
* *
......
...@@ -66,7 +66,7 @@ export const updateOracleCache = async <TSubmissionEventArgs>( ...@@ -66,7 +66,7 @@ export const updateOracleCache = async <TSubmissionEventArgs>(
latestBlock: endBlock, latestBlock: endBlock,
}) })
let failures = 0 let failures = []
let currentBlock = cache.highestBlock + 1 let currentBlock = cache.highestBlock + 1
let step = endBlock - currentBlock let step = endBlock - currentBlock
while (currentBlock < endBlock) { while (currentBlock < endBlock) {
...@@ -98,7 +98,7 @@ export const updateOracleCache = async <TSubmissionEventArgs>( ...@@ -98,7 +98,7 @@ export const updateOracleCache = async <TSubmissionEventArgs>(
currentBlock += step currentBlock += step
step = Math.ceil(step * 2) step = Math.ceil(step * 2)
} catch (err) { } catch (err) {
logger?.error('error fetching events', { logger?.warn('error fetching events', {
err, err,
node: 'l1', node: 'l1',
section: 'getLogs', section: 'getLogs',
...@@ -110,14 +110,14 @@ export const updateOracleCache = async <TSubmissionEventArgs>( ...@@ -110,14 +110,14 @@ export const updateOracleCache = async <TSubmissionEventArgs>(
// When the step gets down to zero, we're pretty much guaranteed that range size isn't the // When the step gets down to zero, we're pretty much guaranteed that range size isn't the
// problem. If we get three failures like this in a row then we should just give up. // problem. If we get three failures like this in a row then we should just give up.
if (step === 0) { if (step === 0) {
failures++ failures.push(err)
} else { } else {
failures = 0 failures = []
} }
// We've failed 3 times in a row, we're probably stuck. // We've failed 5 times in a row, we're probably stuck.
if (failures >= 3) { if (failures.length >= 5) {
logger?.fatal('unable to fetch oracle events', { err }) logger?.fatal('unable to fetch oracle events', { errors: failures })
throw new Error('failed to update event cache') throw new Error('failed to update event cache')
} }
} }
......
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