Commit 984bae91 authored by smartcontracts's avatar smartcontracts Committed by GitHub

feat: incident response improvements (#13711)

* feat: incident response improvements

First half of the original incident response improvements PR.
Co-authored-by: default avatarwildmolasses <changes@gmail.com>

* fix tests and add specs

* misc fixes

* more fixes

* emit event on setRespectedGameTypeUpdatedAt, and test wasRespectedGameType as withdrawal finality condition

* withdrawal when gameWasNotRespectedGameType reverts

* anchor game blacklisted and getAnchorGame tests

* isGameAirgapped

* tiny specs change

* add snapshots

* fix specs test and ASR snapshot

* update semver

* no compilation rrestrictions when optimizer is off

* interop portal semver

* justfile ignore, semver

* minor tweaks

* expanded test coverage

* various logical tweaks

* test fix

* clearer error

* fix test flake in go tests

* add portal tests

* portal2 tests: encodeCall

* FDG test: recipient can't receive value reverts

* various final tweaks

* regenerate snapshots

* fix specs tests

* final test fixes

---------
Co-authored-by: default avatarwildmolasses <changes@gmail.com>
parent 10defcd1
...@@ -31,9 +31,9 @@ just = "1.37.0" ...@@ -31,9 +31,9 @@ just = "1.37.0"
# Foundry dependencies # Foundry dependencies
# Foundry is a special case because it supplies multiple binaries at the same # Foundry is a special case because it supplies multiple binaries at the same
# GitHub release, so we need to use the aliasing trick to get mise to not error # GitHub release, so we need to use the aliasing trick to get mise to not error
forge = "nightly-59f354c179f4e7f6d7292acb3d068815c79286d1" forge = "nightly-017c59d6806ce11f1dc131f8607178efad79d84a"
cast = "nightly-59f354c179f4e7f6d7292acb3d068815c79286d1" cast = "nightly-017c59d6806ce11f1dc131f8607178efad79d84a"
anvil = "nightly-59f354c179f4e7f6d7292acb3d068815c79286d1" anvil = "nightly-017c59d6806ce11f1dc131f8607178efad79d84a"
# Fake dependencies # Fake dependencies
# Put things here if you need to track versions of tools or projects that can't # Put things here if you need to track versions of tools or projects that can't
......
...@@ -71,7 +71,7 @@ func (r *InteropDevRecipe) Build(addrs devkeys.Addresses) (*WorldConfig, error) ...@@ -71,7 +71,7 @@ func (r *InteropDevRecipe) Build(addrs devkeys.Addresses) (*WorldConfig, error)
Implementations: OPCMImplementationsConfig{ Implementations: OPCMImplementationsConfig{
L1ContractsRelease: "dev", L1ContractsRelease: "dev",
FaultProof: SuperFaultProofConfig{ FaultProof: SuperFaultProofConfig{
WithdrawalDelaySeconds: big.NewInt(604800), WithdrawalDelaySeconds: big.NewInt(302400),
MinProposalSizeBytes: big.NewInt(10000), MinProposalSizeBytes: big.NewInt(10000),
ChallengePeriodSeconds: big.NewInt(120), ChallengePeriodSeconds: big.NewInt(120),
ProofMaturityDelaySeconds: big.NewInt(12), ProofMaturityDelaySeconds: big.NewInt(12),
......
...@@ -53,6 +53,7 @@ var ( ...@@ -53,6 +53,7 @@ var (
methodL2BlockNumberChallenged = "l2BlockNumberChallenged" methodL2BlockNumberChallenged = "l2BlockNumberChallenged"
methodL2BlockNumberChallenger = "l2BlockNumberChallenger" methodL2BlockNumberChallenger = "l2BlockNumberChallenger"
methodChallengeRootL2Block = "challengeRootL2Block" methodChallengeRootL2Block = "challengeRootL2Block"
methodBondDistributionMode = "bondDistributionMode"
) )
var ( var (
...@@ -455,6 +456,14 @@ func (f *FaultDisputeGameContractLatest) GetAllClaims(ctx context.Context, block ...@@ -455,6 +456,14 @@ func (f *FaultDisputeGameContractLatest) GetAllClaims(ctx context.Context, block
return claims, nil return claims, nil
} }
func (f *FaultDisputeGameContractLatest) BondDistributionMode(ctx context.Context) (uint8, error) {
result, err := f.multiCaller.SingleCall(ctx, rpcblock.Latest, f.contract.Call(methodBondDistributionMode))
if err != nil {
return 0, fmt.Errorf("failed to fetch bond mode: %w", err)
}
return result.GetUint8(0), nil
}
func (f *FaultDisputeGameContractLatest) IsResolved(ctx context.Context, block rpcblock.Block, claims ...types.Claim) ([]bool, error) { func (f *FaultDisputeGameContractLatest) IsResolved(ctx context.Context, block rpcblock.Block, claims ...types.Claim) ([]bool, error) {
defer f.metrics.StartContractRequest("IsResolved")() defer f.metrics.StartContractRequest("IsResolved")()
calls := make([]batching.Call, 0, len(claims)) calls := make([]batching.Call, 0, len(claims))
...@@ -639,4 +648,5 @@ type FaultDisputeGameContract interface { ...@@ -639,4 +648,5 @@ type FaultDisputeGameContract interface {
CallResolve(ctx context.Context) (gameTypes.GameStatus, error) CallResolve(ctx context.Context) (gameTypes.GameStatus, error)
ResolveTx() (txmgr.TxCandidate, error) ResolveTx() (txmgr.TxCandidate, error)
Vm(ctx context.Context) (*VMContract, error) Vm(ctx context.Context) (*VMContract, error)
BondDistributionMode(ctx context.Context) (uint8, error)
} }
...@@ -15,7 +15,7 @@ const ( ...@@ -15,7 +15,7 @@ const (
GasLimit uint64 = 60_000_000 GasLimit uint64 = 60_000_000
BasefeeScalar uint32 = 1368 BasefeeScalar uint32 = 1368
BlobBaseFeeScalar uint32 = 801949 BlobBaseFeeScalar uint32 = 801949
WithdrawalDelaySeconds uint64 = 604800 WithdrawalDelaySeconds uint64 = 302400
MinProposalSizeBytes uint64 = 126000 MinProposalSizeBytes uint64 = 126000
ChallengePeriodSeconds uint64 = 86400 ChallengePeriodSeconds uint64 = 86400
ProofMaturityDelaySeconds uint64 = 604800 ProofMaturityDelaySeconds uint64 = 604800
......
...@@ -368,6 +368,18 @@ func (g *OutputGameHelper) Status(ctx context.Context) gameTypes.GameStatus { ...@@ -368,6 +368,18 @@ func (g *OutputGameHelper) Status(ctx context.Context) gameTypes.GameStatus {
return status return status
} }
func (g *OutputGameHelper) WaitForBondModeSet(ctx context.Context) {
g.T.Logf("Waiting for game %v to have bond mode set", g.Addr)
timedCtx, cancel := context.WithTimeout(ctx, defaultTimeout)
defer cancel()
err := wait.For(timedCtx, time.Second, func() (bool, error) {
bondMode, err := g.Game.BondDistributionMode(ctx)
g.Require.NoError(err)
return bondMode != 0, nil
})
g.Require.NoError(err, "Failed to wait for bond mode to be set")
}
func (g *OutputGameHelper) WaitForGameStatus(ctx context.Context, expected gameTypes.GameStatus) { func (g *OutputGameHelper) WaitForGameStatus(ctx context.Context, expected gameTypes.GameStatus) {
g.T.Logf("Waiting for game %v to have status %v", g.Addr, expected) g.T.Logf("Waiting for game %v to have status %v", g.Addr, expected)
timedCtx, cancel := context.WithTimeout(ctx, defaultTimeout) timedCtx, cancel := context.WithTimeout(ctx, defaultTimeout)
......
...@@ -115,13 +115,22 @@ func TestOutputAlphabetGame_ReclaimBond(t *testing.T) { ...@@ -115,13 +115,22 @@ func TestOutputAlphabetGame_ReclaimBond(t *testing.T) {
game.WaitForGameStatus(ctx, types.GameStatusChallengerWon) game.WaitForGameStatus(ctx, types.GameStatusChallengerWon)
game.LogGameData(ctx) game.LogGameData(ctx)
// Advance the time past the finalization delay
// Finalization delay is the same as the credit unlock delay
// But just warp way into the future to be safe
sys.TimeTravelClock.AdvanceTime(game.CreditUnlockDuration(ctx) * 2)
require.NoError(t, wait.ForNextBlock(ctx, l1Client))
// Wait for the game to have bond mode set
game.WaitForBondModeSet(ctx)
// Expect Alice's credit to be non-zero // Expect Alice's credit to be non-zero
// But it can't be claimed right now since there is a delay on the weth unlock // But it can't be claimed right now since there is a delay on the weth unlock
require.Truef(t, game.AvailableCredit(ctx, alice).Cmp(big.NewInt(0)) > 0, "Expected alice credit to be above zero") require.Truef(t, game.AvailableCredit(ctx, alice).Cmp(big.NewInt(0)) > 0, "Expected alice credit to be above zero")
// The actor should have no credit available because all its bonds were paid to Alice. // The actor should have no credit available because all its bonds were paid to Alice.
actorCredit := game.AvailableCredit(ctx, disputegame.TestAddress) actorCredit := game.AvailableCredit(ctx, disputegame.TestAddress)
require.True(t, actorCredit.Cmp(big.NewInt(0)) == 0, "Expected alice available credit to be zero") require.True(t, actorCredit.Cmp(big.NewInt(0)) == 0, "Expected actor available credit to be zero")
// Advance the time past the weth unlock delay // Advance the time past the weth unlock delay
sys.TimeTravelClock.AdvanceTime(game.CreditUnlockDuration(ctx)) sys.TimeTravelClock.AdvanceTime(game.CreditUnlockDuration(ctx))
......
...@@ -13,6 +13,14 @@ snapshots = 'notarealpath' # workaround for foundry#9477 ...@@ -13,6 +13,14 @@ snapshots = 'notarealpath' # workaround for foundry#9477
optimizer = true optimizer = true
optimizer_runs = 999999 optimizer_runs = 999999
additional_compiler_profiles = [
{ name = "dispute", optimizer_runs = 5000 },
]
compilation_restrictions = [
{ paths = "src/dispute/FaultDisputeGame.sol", optimizer_runs = 5000 },
{ paths = "src/dispute/PermissionedDisputeGame.sol", optimizer_runs = 5000 },
]
extra_output = ['devdoc', 'userdoc', 'metadata', 'storageLayout'] extra_output = ['devdoc', 'userdoc', 'metadata', 'storageLayout']
bytecode_hash = 'none' bytecode_hash = 'none'
ast = true ast = true
...@@ -85,6 +93,7 @@ depth = 32 ...@@ -85,6 +93,7 @@ depth = 32
[profile.cicoverage] [profile.cicoverage]
optimizer = false optimizer = false
compilation_restrictions = []
[profile.cicoverage.fuzz] [profile.cicoverage.fuzz]
runs = 1 runs = 1
...@@ -112,6 +121,8 @@ timeout = 300 ...@@ -112,6 +121,8 @@ timeout = 300
[profile.lite] [profile.lite]
optimizer = false optimizer = false
compilation_restrictions = []
################################################################ ################################################################
# PROFILE: KONTROL # # PROFILE: KONTROL #
......
...@@ -31,6 +31,7 @@ interface IOptimismPortal2 { ...@@ -31,6 +31,7 @@ interface IOptimismPortal2 {
error UnexpectedList(); error UnexpectedList();
error UnexpectedString(); error UnexpectedString();
error Unproven(); error Unproven();
error LegacyGame();
event DisputeGameBlacklisted(IDisputeGame indexed disputeGame); event DisputeGameBlacklisted(IDisputeGame indexed disputeGame);
event Initialized(uint8 version); event Initialized(uint8 version);
......
...@@ -33,6 +33,7 @@ interface IOptimismPortalInterop { ...@@ -33,6 +33,7 @@ interface IOptimismPortalInterop {
error UnexpectedList(); error UnexpectedList();
error UnexpectedString(); error UnexpectedString();
error Unproven(); error Unproven();
error LegacyGame();
event DisputeGameBlacklisted(IDisputeGame indexed disputeGame); event DisputeGameBlacklisted(IDisputeGame indexed disputeGame);
event Initialized(uint8 version); event Initialized(uint8 version);
......
...@@ -10,8 +10,8 @@ import { GameType, Hash, OutputRoot } from "src/dispute/lib/Types.sol"; ...@@ -10,8 +10,8 @@ import { GameType, Hash, OutputRoot } from "src/dispute/lib/Types.sol";
interface IAnchorStateRegistry { interface IAnchorStateRegistry {
error AnchorStateRegistry_Unauthorized(); error AnchorStateRegistry_Unauthorized();
error AnchorStateRegistry_ImproperAnchorGame();
error AnchorStateRegistry_InvalidAnchorGame(); error AnchorStateRegistry_InvalidAnchorGame();
error AnchorStateRegistry_AnchorGameBlacklisted();
event AnchorNotUpdated(IFaultDisputeGame indexed game); event AnchorNotUpdated(IFaultDisputeGame indexed game);
event AnchorUpdated(IFaultDisputeGame indexed game); event AnchorUpdated(IFaultDisputeGame indexed game);
...@@ -21,16 +21,27 @@ interface IAnchorStateRegistry { ...@@ -21,16 +21,27 @@ interface IAnchorStateRegistry {
function anchors(GameType) external view returns (Hash, uint256); function anchors(GameType) external view returns (Hash, uint256);
function getAnchorRoot() external view returns (Hash, uint256); function getAnchorRoot() external view returns (Hash, uint256);
function disputeGameFactory() external view returns (IDisputeGameFactory); function disputeGameFactory() external view returns (IDisputeGameFactory);
function initialize(ISuperchainConfig _superchainConfig, IDisputeGameFactory _disputeGameFactory, IOptimismPortal2 _portal, OutputRoot memory _startingAnchorRoot) external; function initialize(
function isGameRegistered(IDisputeGame _game) external view returns (bool); ISuperchainConfig _superchainConfig,
IDisputeGameFactory _disputeGameFactory,
IOptimismPortal2 _portal,
OutputRoot memory _startingAnchorRoot
)
external;
function isGameAirgapped(IDisputeGame _game) external view returns (bool);
function isGameBlacklisted(IDisputeGame _game) external view returns (bool); function isGameBlacklisted(IDisputeGame _game) external view returns (bool);
function isGameProper(IDisputeGame _game) external view returns (bool);
function isGameRegistered(IDisputeGame _game) external view returns (bool);
function isGameResolved(IDisputeGame _game) external view returns (bool);
function isGameRespected(IDisputeGame _game) external view returns (bool); function isGameRespected(IDisputeGame _game) external view returns (bool);
function isGameRetired(IDisputeGame _game) external view returns (bool); function isGameRetired(IDisputeGame _game) external view returns (bool);
function isGameProper(IDisputeGame _game) external view returns (bool); function isGameFinalized(IDisputeGame _game) external view returns (bool);
function isGameClaimValid(IDisputeGame _game) external view returns (bool);
function portal() external view returns (IOptimismPortal2); function portal() external view returns (IOptimismPortal2);
function setAnchorState(IFaultDisputeGame _game) external; function respectedGameType() external view returns (GameType);
function setAnchorState(IDisputeGame _game) external;
function superchainConfig() external view returns (ISuperchainConfig); function superchainConfig() external view returns (ISuperchainConfig);
function tryUpdateAnchorState() external;
function version() external view returns (string memory); function version() external view returns (string memory);
function __constructor__() external; function __constructor__() external;
......
...@@ -11,13 +11,13 @@ interface IDelayedWETH { ...@@ -11,13 +11,13 @@ interface IDelayedWETH {
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
event Initialized(uint8 version); event Initialized(uint8 version);
event Unwrap(address indexed src, uint256 wad);
fallback() external payable; fallback() external payable;
receive() external payable; receive() external payable;
function config() external view returns (ISuperchainConfig); function config() external view returns (ISuperchainConfig);
function delay() external view returns (uint256); function delay() external view returns (uint256);
function hold(address _guy) external;
function hold(address _guy, uint256 _wad) external; function hold(address _guy, uint256 _wad) external;
function initialize(address _owner, ISuperchainConfig _config) external; function initialize(address _owner, ISuperchainConfig _config) external;
function owner() external view returns (address); function owner() external view returns (address);
......
...@@ -14,7 +14,9 @@ interface IDisputeGame is IInitializable { ...@@ -14,7 +14,9 @@ interface IDisputeGame is IInitializable {
function gameCreator() external pure returns (address creator_); function gameCreator() external pure returns (address creator_);
function rootClaim() external pure returns (Claim rootClaim_); function rootClaim() external pure returns (Claim rootClaim_);
function l1Head() external pure returns (Hash l1Head_); function l1Head() external pure returns (Hash l1Head_);
function l2BlockNumber() external pure returns (uint256 l2BlockNumber_);
function extraData() external pure returns (bytes memory extraData_); function extraData() external pure returns (bytes memory extraData_);
function resolve() external returns (GameStatus status_); function resolve() external returns (GameStatus status_);
function gameData() external view returns (GameType gameType_, Claim rootClaim_, bytes memory extraData_); function gameData() external view returns (GameType gameType_, Claim rootClaim_, bytes memory extraData_);
function wasRespectedGameTypeWhenCreated() external view returns (bool);
} }
...@@ -6,7 +6,7 @@ import { IDelayedWETH } from "interfaces/dispute/IDelayedWETH.sol"; ...@@ -6,7 +6,7 @@ import { IDelayedWETH } from "interfaces/dispute/IDelayedWETH.sol";
import { IAnchorStateRegistry } from "interfaces/dispute/IAnchorStateRegistry.sol"; import { IAnchorStateRegistry } from "interfaces/dispute/IAnchorStateRegistry.sol";
import { IBigStepper } from "interfaces/dispute/IBigStepper.sol"; import { IBigStepper } from "interfaces/dispute/IBigStepper.sol";
import { Types } from "src/libraries/Types.sol"; import { Types } from "src/libraries/Types.sol";
import { GameType, Claim, Position, Clock, Hash, Duration } from "src/dispute/lib/Types.sol"; import { GameType, Claim, Position, Clock, Hash, Duration, BondDistributionMode } from "src/dispute/lib/Types.sol";
interface IFaultDisputeGame is IDisputeGame { interface IFaultDisputeGame is IDisputeGame {
struct ClaimData { struct ClaimData {
...@@ -74,13 +74,19 @@ interface IFaultDisputeGame is IDisputeGame { ...@@ -74,13 +74,19 @@ interface IFaultDisputeGame is IDisputeGame {
error UnexpectedRootClaim(Claim rootClaim); error UnexpectedRootClaim(Claim rootClaim);
error UnexpectedString(); error UnexpectedString();
error ValidStep(); error ValidStep();
error InvalidBondDistributionMode();
error GameNotFinalized();
error GameNotResolved();
error ReservedGameType();
event Move(uint256 indexed parentIndex, Claim indexed claim, address indexed claimant); event Move(uint256 indexed parentIndex, Claim indexed claim, address indexed claimant);
event GameClosed(BondDistributionMode bondDistributionMode);
function absolutePrestate() external view returns (Claim absolutePrestate_); function absolutePrestate() external view returns (Claim absolutePrestate_);
function addLocalData(uint256 _ident, uint256 _execLeafIdx, uint256 _partOffset) external; function addLocalData(uint256 _ident, uint256 _execLeafIdx, uint256 _partOffset) external;
function anchorStateRegistry() external view returns (IAnchorStateRegistry registry_); function anchorStateRegistry() external view returns (IAnchorStateRegistry registry_);
function attack(Claim _disputed, uint256 _parentIndex, Claim _claim) external payable; function attack(Claim _disputed, uint256 _parentIndex, Claim _claim) external payable;
function bondDistributionMode() external view returns (BondDistributionMode);
function challengeRootL2Block(Types.OutputRootProof memory _outputRootProof, bytes memory _headerRLP) external; function challengeRootL2Block(Types.OutputRootProof memory _outputRootProof, bytes memory _headerRLP) external;
function claimCredit(address _recipient) external; function claimCredit(address _recipient) external;
function claimData(uint256) function claimData(uint256)
...@@ -98,11 +104,13 @@ interface IFaultDisputeGame is IDisputeGame { ...@@ -98,11 +104,13 @@ interface IFaultDisputeGame is IDisputeGame {
function claimDataLen() external view returns (uint256 len_); function claimDataLen() external view returns (uint256 len_);
function claims(Hash) external view returns (bool); function claims(Hash) external view returns (bool);
function clockExtension() external view returns (Duration clockExtension_); function clockExtension() external view returns (Duration clockExtension_);
function credit(address) external view returns (uint256); function closeGame() external;
function credit(address _recipient) external view returns (uint256 credit_);
function defend(Claim _disputed, uint256 _parentIndex, Claim _claim) external payable; function defend(Claim _disputed, uint256 _parentIndex, Claim _claim) external payable;
function getChallengerDuration(uint256 _claimIndex) external view returns (Duration duration_); function getChallengerDuration(uint256 _claimIndex) external view returns (Duration duration_);
function getNumToResolve(uint256 _claimIndex) external view returns (uint256 numRemainingChildren_); function getNumToResolve(uint256 _claimIndex) external view returns (uint256 numRemainingChildren_);
function getRequiredBond(Position _position) external view returns (uint256 requiredBond_); function getRequiredBond(Position _position) external view returns (uint256 requiredBond_);
function hasUnlockedCredit(address) external view returns (bool);
function l2BlockNumber() external pure returns (uint256 l2BlockNumber_); function l2BlockNumber() external pure returns (uint256 l2BlockNumber_);
function l2BlockNumberChallenged() external view returns (bool); function l2BlockNumberChallenged() external view returns (bool);
function l2BlockNumberChallenger() external view returns (address); function l2BlockNumberChallenger() external view returns (address);
...@@ -110,6 +118,8 @@ interface IFaultDisputeGame is IDisputeGame { ...@@ -110,6 +118,8 @@ interface IFaultDisputeGame is IDisputeGame {
function maxClockDuration() external view returns (Duration maxClockDuration_); function maxClockDuration() external view returns (Duration maxClockDuration_);
function maxGameDepth() external view returns (uint256 maxGameDepth_); function maxGameDepth() external view returns (uint256 maxGameDepth_);
function move(Claim _disputed, uint256 _challengeIndex, Claim _claim, bool _isAttack) external payable; function move(Claim _disputed, uint256 _challengeIndex, Claim _claim, bool _isAttack) external payable;
function normalModeCredit(address) external view returns (uint256);
function refundModeCredit(address) external view returns (uint256);
function resolutionCheckpoints(uint256) function resolutionCheckpoints(uint256)
external external
view view
...@@ -124,6 +134,7 @@ interface IFaultDisputeGame is IDisputeGame { ...@@ -124,6 +134,7 @@ interface IFaultDisputeGame is IDisputeGame {
function subgames(uint256, uint256) external view returns (uint256); function subgames(uint256, uint256) external view returns (uint256);
function version() external view returns (string memory); function version() external view returns (string memory);
function vm() external view returns (IBigStepper vm_); function vm() external view returns (IBigStepper vm_);
function wasRespectedGameTypeWhenCreated() external view returns (bool);
function weth() external view returns (IDelayedWETH weth_); function weth() external view returns (IDelayedWETH weth_);
function __constructor__(GameConstructorParams memory _params) external; function __constructor__(GameConstructorParams memory _params) external;
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
pragma solidity ^0.8.0; pragma solidity ^0.8.0;
import { Types } from "src/libraries/Types.sol"; import { Types } from "src/libraries/Types.sol";
import { Claim, Position, Clock, Hash, Duration } from "src/dispute/lib/Types.sol"; import { Claim, Position, Clock, Hash, Duration, BondDistributionMode } from "src/dispute/lib/Types.sol";
import { IAnchorStateRegistry } from "interfaces/dispute/IAnchorStateRegistry.sol"; import { IAnchorStateRegistry } from "interfaces/dispute/IAnchorStateRegistry.sol";
import { IDelayedWETH } from "interfaces/dispute/IDelayedWETH.sol"; import { IDelayedWETH } from "interfaces/dispute/IDelayedWETH.sol";
...@@ -63,13 +63,19 @@ interface IPermissionedDisputeGame is IDisputeGame { ...@@ -63,13 +63,19 @@ interface IPermissionedDisputeGame is IDisputeGame {
error UnexpectedRootClaim(Claim rootClaim); error UnexpectedRootClaim(Claim rootClaim);
error UnexpectedString(); error UnexpectedString();
error ValidStep(); error ValidStep();
error InvalidBondDistributionMode();
error GameNotFinalized();
error GameNotResolved();
error ReservedGameType();
event Move(uint256 indexed parentIndex, Claim indexed claim, address indexed claimant); event Move(uint256 indexed parentIndex, Claim indexed claim, address indexed claimant);
event GameClosed(BondDistributionMode bondDistributionMode);
function absolutePrestate() external view returns (Claim absolutePrestate_); function absolutePrestate() external view returns (Claim absolutePrestate_);
function addLocalData(uint256 _ident, uint256 _execLeafIdx, uint256 _partOffset) external; function addLocalData(uint256 _ident, uint256 _execLeafIdx, uint256 _partOffset) external;
function anchorStateRegistry() external view returns (IAnchorStateRegistry registry_); function anchorStateRegistry() external view returns (IAnchorStateRegistry registry_);
function attack(Claim _disputed, uint256 _parentIndex, Claim _claim) external payable; function attack(Claim _disputed, uint256 _parentIndex, Claim _claim) external payable;
function bondDistributionMode() external view returns (BondDistributionMode);
function challengeRootL2Block(Types.OutputRootProof memory _outputRootProof, bytes memory _headerRLP) external; function challengeRootL2Block(Types.OutputRootProof memory _outputRootProof, bytes memory _headerRLP) external;
function claimCredit(address _recipient) external; function claimCredit(address _recipient) external;
function claimData(uint256) function claimData(uint256)
...@@ -87,11 +93,14 @@ interface IPermissionedDisputeGame is IDisputeGame { ...@@ -87,11 +93,14 @@ interface IPermissionedDisputeGame is IDisputeGame {
function claimDataLen() external view returns (uint256 len_); function claimDataLen() external view returns (uint256 len_);
function claims(Hash) external view returns (bool); function claims(Hash) external view returns (bool);
function clockExtension() external view returns (Duration clockExtension_); function clockExtension() external view returns (Duration clockExtension_);
function credit(address) external view returns (uint256); function closeGame() external;
function credit(address _recipient) external view returns (uint256 credit_);
function defend(Claim _disputed, uint256 _parentIndex, Claim _claim) external payable; function defend(Claim _disputed, uint256 _parentIndex, Claim _claim) external payable;
function getChallengerDuration(uint256 _claimIndex) external view returns (Duration duration_); function getChallengerDuration(uint256 _claimIndex) external view returns (Duration duration_);
function getNumToResolve(uint256 _claimIndex) external view returns (uint256 numRemainingChildren_); function getNumToResolve(uint256 _claimIndex) external view returns (uint256 numRemainingChildren_);
function getRequiredBond(Position _position) external view returns (uint256 requiredBond_); function getRequiredBond(Position _position) external view returns (uint256 requiredBond_);
function hasUnlockedCredit(address) external view returns (bool);
function initialize() external payable;
function l2BlockNumber() external pure returns (uint256 l2BlockNumber_); function l2BlockNumber() external pure returns (uint256 l2BlockNumber_);
function l2BlockNumberChallenged() external view returns (bool); function l2BlockNumberChallenged() external view returns (bool);
function l2BlockNumberChallenger() external view returns (address); function l2BlockNumberChallenger() external view returns (address);
...@@ -99,6 +108,8 @@ interface IPermissionedDisputeGame is IDisputeGame { ...@@ -99,6 +108,8 @@ interface IPermissionedDisputeGame is IDisputeGame {
function maxClockDuration() external view returns (Duration maxClockDuration_); function maxClockDuration() external view returns (Duration maxClockDuration_);
function maxGameDepth() external view returns (uint256 maxGameDepth_); function maxGameDepth() external view returns (uint256 maxGameDepth_);
function move(Claim _disputed, uint256 _challengeIndex, Claim _claim, bool _isAttack) external payable; function move(Claim _disputed, uint256 _challengeIndex, Claim _claim, bool _isAttack) external payable;
function normalModeCredit(address) external view returns (uint256);
function refundModeCredit(address) external view returns (uint256);
function resolutionCheckpoints(uint256) function resolutionCheckpoints(uint256)
external external
view view
...@@ -113,6 +124,7 @@ interface IPermissionedDisputeGame is IDisputeGame { ...@@ -113,6 +124,7 @@ interface IPermissionedDisputeGame is IDisputeGame {
function subgames(uint256, uint256) external view returns (uint256); function subgames(uint256, uint256) external view returns (uint256);
function version() external view returns (string memory); function version() external view returns (string memory);
function vm() external view returns (IBigStepper vm_); function vm() external view returns (IBigStepper vm_);
function wasRespectedGameTypeWhenCreated() external view returns (bool);
function weth() external view returns (IDelayedWETH weth_); function weth() external view returns (IDelayedWETH weth_);
error BadAuth(); error BadAuth();
......
...@@ -73,7 +73,7 @@ test-upgrade *ARGS: build-go-ffi ...@@ -73,7 +73,7 @@ test-upgrade *ARGS: build-go-ffi
#!/bin/bash #!/bin/bash
echo "Running upgrade tests at block $pinnedBlockNumber" echo "Running upgrade tests at block $pinnedBlockNumber"
export FORK_BLOCK_NUMBER=$pinnedBlockNumber export FORK_BLOCK_NUMBER=$pinnedBlockNumber
export NO_MATCH_CONTRACTS="OptimismPortal2WithMockERC20_Test|OptimismPortal2_FinalizeWithdrawal_Test|'AnchorStateRegistry_*'|FaultDisputeGame_Test|FaultDispute_1v1_Actors_Test" export NO_MATCH_CONTRACTS="OptimismPortal2WithMockERC20_Test|OptimismPortal2_FinalizeWithdrawal_Test|'AnchorStateRegistry_*'|FaultDisputeGame_Test|PermissionedDisputeGame_Test|FaultDispute_1v1_Actors_Test|DelayedWETH_Hold_Test"
export NO_MATCH_PATHS="test/dispute/AnchorStateRegistry.t.sol" export NO_MATCH_PATHS="test/dispute/AnchorStateRegistry.t.sol"
FORK_RPC_URL=$ETH_RPC_URL \ FORK_RPC_URL=$ETH_RPC_URL \
FORK_TEST=true \ FORK_TEST=true \
......
...@@ -112,6 +112,25 @@ ...@@ -112,6 +112,25 @@
"stateMutability": "nonpayable", "stateMutability": "nonpayable",
"type": "function" "type": "function"
}, },
{
"inputs": [
{
"internalType": "contract IDisputeGame",
"name": "_game",
"type": "address"
}
],
"name": "isGameAirgapped",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{ {
"inputs": [ "inputs": [
{ {
...@@ -131,6 +150,44 @@ ...@@ -131,6 +150,44 @@
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
}, },
{
"inputs": [
{
"internalType": "contract IDisputeGame",
"name": "_game",
"type": "address"
}
],
"name": "isGameClaimValid",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "contract IDisputeGame",
"name": "_game",
"type": "address"
}
],
"name": "isGameFinalized",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{ {
"inputs": [ "inputs": [
{ {
...@@ -169,6 +226,25 @@ ...@@ -169,6 +226,25 @@
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
}, },
{
"inputs": [
{
"internalType": "contract IDisputeGame",
"name": "_game",
"type": "address"
}
],
"name": "isGameResolved",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{ {
"inputs": [ "inputs": [
{ {
...@@ -220,10 +296,23 @@ ...@@ -220,10 +296,23 @@
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
}, },
{
"inputs": [],
"name": "respectedGameType",
"outputs": [
{
"internalType": "GameType",
"name": "",
"type": "uint32"
}
],
"stateMutability": "view",
"type": "function"
},
{ {
"inputs": [ "inputs": [
{ {
"internalType": "contract IFaultDisputeGame", "internalType": "contract IDisputeGame",
"name": "_game", "name": "_game",
"type": "address" "type": "address"
} }
...@@ -246,13 +335,6 @@ ...@@ -246,13 +335,6 @@
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
}, },
{
"inputs": [],
"name": "tryUpdateAnchorState",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{ {
"inputs": [], "inputs": [],
"name": "version", "name": "version",
...@@ -307,7 +389,7 @@ ...@@ -307,7 +389,7 @@
}, },
{ {
"inputs": [], "inputs": [],
"name": "AnchorStateRegistry_ImproperAnchorGame", "name": "AnchorStateRegistry_AnchorGameBlacklisted",
"type": "error" "type": "error"
}, },
{ {
......
...@@ -149,6 +149,19 @@ ...@@ -149,6 +149,19 @@
"stateMutability": "nonpayable", "stateMutability": "nonpayable",
"type": "function" "type": "function"
}, },
{
"inputs": [
{
"internalType": "address",
"name": "_guy",
"type": "address"
}
],
"name": "hold",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{ {
"inputs": [ "inputs": [
{ {
...@@ -497,25 +510,6 @@ ...@@ -497,25 +510,6 @@
"name": "Transfer", "name": "Transfer",
"type": "event" "type": "event"
}, },
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "src",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "wad",
"type": "uint256"
}
],
"name": "Unwrap",
"type": "event"
},
{ {
"anonymous": false, "anonymous": false,
"inputs": [ "inputs": [
......
...@@ -134,6 +134,19 @@ ...@@ -134,6 +134,19 @@
"stateMutability": "payable", "stateMutability": "payable",
"type": "function" "type": "function"
}, },
{
"inputs": [],
"name": "bondDistributionMode",
"outputs": [
{
"internalType": "enum BondDistributionMode",
"name": "",
"type": "uint8"
}
],
"stateMutability": "view",
"type": "function"
},
{ {
"inputs": [ "inputs": [
{ {
...@@ -281,6 +294,13 @@ ...@@ -281,6 +294,13 @@
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
}, },
{
"inputs": [],
"name": "closeGame",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{ {
"inputs": [], "inputs": [],
"name": "createdAt", "name": "createdAt",
...@@ -298,7 +318,7 @@ ...@@ -298,7 +318,7 @@
"inputs": [ "inputs": [
{ {
"internalType": "address", "internalType": "address",
"name": "", "name": "_recipient",
"type": "address" "type": "address"
} }
], ],
...@@ -306,7 +326,7 @@ ...@@ -306,7 +326,7 @@
"outputs": [ "outputs": [
{ {
"internalType": "uint256", "internalType": "uint256",
"name": "", "name": "credit_",
"type": "uint256" "type": "uint256"
} }
], ],
...@@ -455,6 +475,25 @@ ...@@ -455,6 +475,25 @@
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
}, },
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "hasUnlockedCredit",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{ {
"inputs": [], "inputs": [],
"name": "initialize", "name": "initialize",
...@@ -581,6 +620,44 @@ ...@@ -581,6 +620,44 @@
"stateMutability": "payable", "stateMutability": "payable",
"type": "function" "type": "function"
}, },
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "normalModeCredit",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "refundModeCredit",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{ {
"inputs": [ "inputs": [
{ {
...@@ -839,6 +916,19 @@ ...@@ -839,6 +916,19 @@
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
}, },
{
"inputs": [],
"name": "wasRespectedGameTypeWhenCreated",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{ {
"inputs": [], "inputs": [],
"name": "weth", "name": "weth",
...@@ -852,6 +942,19 @@ ...@@ -852,6 +942,19 @@
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
}, },
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "enum BondDistributionMode",
"name": "bondDistributionMode",
"type": "uint8"
}
],
"name": "GameClosed",
"type": "event"
},
{ {
"anonymous": false, "anonymous": false,
"inputs": [ "inputs": [
...@@ -960,16 +1063,31 @@ ...@@ -960,16 +1063,31 @@
"name": "GameDepthExceeded", "name": "GameDepthExceeded",
"type": "error" "type": "error"
}, },
{
"inputs": [],
"name": "GameNotFinalized",
"type": "error"
},
{ {
"inputs": [], "inputs": [],
"name": "GameNotInProgress", "name": "GameNotInProgress",
"type": "error" "type": "error"
}, },
{
"inputs": [],
"name": "GameNotResolved",
"type": "error"
},
{ {
"inputs": [], "inputs": [],
"name": "IncorrectBondAmount", "name": "IncorrectBondAmount",
"type": "error" "type": "error"
}, },
{
"inputs": [],
"name": "InvalidBondDistributionMode",
"type": "error"
},
{ {
"inputs": [], "inputs": [],
"name": "InvalidChallengePeriod", "name": "InvalidChallengePeriod",
...@@ -1045,6 +1163,11 @@ ...@@ -1045,6 +1163,11 @@
"name": "OutOfOrderResolution", "name": "OutOfOrderResolution",
"type": "error" "type": "error"
}, },
{
"inputs": [],
"name": "ReservedGameType",
"type": "error"
},
{ {
"inputs": [], "inputs": [],
"name": "UnexpectedList", "name": "UnexpectedList",
......
...@@ -812,6 +812,11 @@ ...@@ -812,6 +812,11 @@
"name": "LargeCalldata", "name": "LargeCalldata",
"type": "error" "type": "error"
}, },
{
"inputs": [],
"name": "LegacyGame",
"type": "error"
},
{ {
"inputs": [], "inputs": [],
"name": "NonReentrant", "name": "NonReentrant",
......
...@@ -835,6 +835,11 @@ ...@@ -835,6 +835,11 @@
"name": "LargeCalldata", "name": "LargeCalldata",
"type": "error" "type": "error"
}, },
{
"inputs": [],
"name": "LegacyGame",
"type": "error"
},
{ {
"inputs": [], "inputs": [],
"name": "NonReentrant", "name": "NonReentrant",
......
...@@ -144,6 +144,19 @@ ...@@ -144,6 +144,19 @@
"stateMutability": "payable", "stateMutability": "payable",
"type": "function" "type": "function"
}, },
{
"inputs": [],
"name": "bondDistributionMode",
"outputs": [
{
"internalType": "enum BondDistributionMode",
"name": "",
"type": "uint8"
}
],
"stateMutability": "view",
"type": "function"
},
{ {
"inputs": [ "inputs": [
{ {
...@@ -304,6 +317,13 @@ ...@@ -304,6 +317,13 @@
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
}, },
{
"inputs": [],
"name": "closeGame",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{ {
"inputs": [], "inputs": [],
"name": "createdAt", "name": "createdAt",
...@@ -321,7 +341,7 @@ ...@@ -321,7 +341,7 @@
"inputs": [ "inputs": [
{ {
"internalType": "address", "internalType": "address",
"name": "", "name": "_recipient",
"type": "address" "type": "address"
} }
], ],
...@@ -329,7 +349,7 @@ ...@@ -329,7 +349,7 @@
"outputs": [ "outputs": [
{ {
"internalType": "uint256", "internalType": "uint256",
"name": "", "name": "credit_",
"type": "uint256" "type": "uint256"
} }
], ],
...@@ -478,6 +498,25 @@ ...@@ -478,6 +498,25 @@
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
}, },
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "hasUnlockedCredit",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{ {
"inputs": [], "inputs": [],
"name": "initialize", "name": "initialize",
...@@ -604,6 +643,25 @@ ...@@ -604,6 +643,25 @@
"stateMutability": "payable", "stateMutability": "payable",
"type": "function" "type": "function"
}, },
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "normalModeCredit",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{ {
"inputs": [], "inputs": [],
"name": "proposer", "name": "proposer",
...@@ -617,6 +675,25 @@ ...@@ -617,6 +675,25 @@
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
}, },
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "refundModeCredit",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{ {
"inputs": [ "inputs": [
{ {
...@@ -875,6 +952,19 @@ ...@@ -875,6 +952,19 @@
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
}, },
{
"inputs": [],
"name": "wasRespectedGameTypeWhenCreated",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{ {
"inputs": [], "inputs": [],
"name": "weth", "name": "weth",
...@@ -888,6 +978,19 @@ ...@@ -888,6 +978,19 @@
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
}, },
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "enum BondDistributionMode",
"name": "bondDistributionMode",
"type": "uint8"
}
],
"name": "GameClosed",
"type": "event"
},
{ {
"anonymous": false, "anonymous": false,
"inputs": [ "inputs": [
...@@ -1001,16 +1104,31 @@ ...@@ -1001,16 +1104,31 @@
"name": "GameDepthExceeded", "name": "GameDepthExceeded",
"type": "error" "type": "error"
}, },
{
"inputs": [],
"name": "GameNotFinalized",
"type": "error"
},
{ {
"inputs": [], "inputs": [],
"name": "GameNotInProgress", "name": "GameNotInProgress",
"type": "error" "type": "error"
}, },
{
"inputs": [],
"name": "GameNotResolved",
"type": "error"
},
{ {
"inputs": [], "inputs": [],
"name": "IncorrectBondAmount", "name": "IncorrectBondAmount",
"type": "error" "type": "error"
}, },
{
"inputs": [],
"name": "InvalidBondDistributionMode",
"type": "error"
},
{ {
"inputs": [], "inputs": [],
"name": "InvalidChallengePeriod", "name": "InvalidChallengePeriod",
...@@ -1086,6 +1204,11 @@ ...@@ -1086,6 +1204,11 @@
"name": "OutOfOrderResolution", "name": "OutOfOrderResolution",
"type": "error" "type": "error"
}, },
{
"inputs": [],
"name": "ReservedGameType",
"type": "error"
},
{ {
"inputs": [], "inputs": [],
"name": "UnexpectedList", "name": "UnexpectedList",
......
...@@ -20,12 +20,12 @@ ...@@ -20,12 +20,12 @@
"sourceCodeHash": "0xdfd5c91e5ddbbf2ad82b867cbf7403437decd6ca70b87891eec935665f17ffd5" "sourceCodeHash": "0xdfd5c91e5ddbbf2ad82b867cbf7403437decd6ca70b87891eec935665f17ffd5"
}, },
"src/L1/OptimismPortal2.sol": { "src/L1/OptimismPortal2.sol": {
"initCodeHash": "0x2121a97875875150106a54a71c6c4c03afe90b3364e416be047f55fdeab57204", "initCodeHash": "0x969e3687d4497cc168af61e610ba0ae187e80f86aaa7b5d5bb598de19f279f08",
"sourceCodeHash": "0x96e3de3ef0025a6def702eeb481acd2d2d88971fd418be657472f51a98029773" "sourceCodeHash": "0xf215a31954f2ef166cfb26d20e466c62fafa235a08fc42c55131dcb81998ff01"
}, },
"src/L1/OptimismPortalInterop.sol": { "src/L1/OptimismPortalInterop.sol": {
"initCodeHash": "0x09ffe45f91bf59315b9fd4a2941b819ed8b1bb0d8643a630c6193bd67acea0ed", "initCodeHash": "0x057c56174304f3773654fed39abf5fab70d9446f531d07fdb225b738a680ad46",
"sourceCodeHash": "0xbb6acc3e88af9594ffcb8a2f30860511b76e09024330e70052316668fe55fd1f" "sourceCodeHash": "0xc04a7f9c14a13ec3587f5cc351c8e9f27fbbe9f1291a1aba07de29edbeef418a"
}, },
"src/L1/ProtocolVersions.sol": { "src/L1/ProtocolVersions.sol": {
"initCodeHash": "0x0000ec89712d8b4609873f1ba76afffd4205bf9110818995c90134dbec12e91e", "initCodeHash": "0x0000ec89712d8b4609873f1ba76afffd4205bf9110818995c90134dbec12e91e",
...@@ -152,20 +152,20 @@ ...@@ -152,20 +152,20 @@
"sourceCodeHash": "0xb7b0a06cd971c4647247dc19ce997d0c64a73e87c81d30731da9cf9efa1b952a" "sourceCodeHash": "0xb7b0a06cd971c4647247dc19ce997d0c64a73e87c81d30731da9cf9efa1b952a"
}, },
"src/dispute/AnchorStateRegistry.sol": { "src/dispute/AnchorStateRegistry.sol": {
"initCodeHash": "0xfbeeac40d86d13e71c7add66eef6357576a93b6a175c9cff6ec6ef587fe3acc4", "initCodeHash": "0xb2618d650808a7a335db7cc56d15ccaf432f50aa551c01be8bde8356893c0e0d",
"sourceCodeHash": "0xbb2e08da74d470fc30dd35dc39834e19f676a45974aa2403eb97e84bc5bed0a8" "sourceCodeHash": "0x745f0e2b07b8f6492e11ca2f69b53d129177fbfd346d5ca4729d72792aff1f83"
}, },
"src/dispute/DelayedWETH.sol": { "src/dispute/DelayedWETH.sol": {
"initCodeHash": "0x759d7f9c52b7c13ce4502f39dae3a75d130c6278240cde0b60ae84616aa2bd48", "initCodeHash": "0xb1f04c9ee86984a157b92a18754c84104e9d4df7a3838633301ca7f557d0220a",
"sourceCodeHash": "0x4406c78e0557bedb88b4ee5977acb1ef13e7bd92b7dbf79f56f8bad95c53e229" "sourceCodeHash": "0x0162302b9c71f184d45bee34ecfb1dfbf427f38fc5652709ab7ffef1ac816d82"
}, },
"src/dispute/DisputeGameFactory.sol": { "src/dispute/DisputeGameFactory.sol": {
"initCodeHash": "0xa728192115c5fdb08c633a0899043318289b1d413d7afeed06356008b2a5a7fa", "initCodeHash": "0xa728192115c5fdb08c633a0899043318289b1d413d7afeed06356008b2a5a7fa",
"sourceCodeHash": "0x155c0334f63616ed245aadf9a94f419ef7d5e2237b3b32172484fd19890a61dc" "sourceCodeHash": "0x155c0334f63616ed245aadf9a94f419ef7d5e2237b3b32172484fd19890a61dc"
}, },
"src/dispute/FaultDisputeGame.sol": { "src/dispute/FaultDisputeGame.sol": {
"initCodeHash": "0x423e8488731c0b0f87b435174f412c09fbf0b17eb0b8c9a03efa37d779ec0cae", "initCodeHash": "0x152fbb1f82488d815f56087fc464b9478f1390e3ecd67ae595344115fdd9ba91",
"sourceCodeHash": "0xe53b970922b309ada1c59f94d5935ffca669e909c797f17ba8a3d309c487e7e8" "sourceCodeHash": "0x9bfea41bd993bc1ef2ede9a5846a432ed5ea183868634fd77c4068b0a4a779b2"
}, },
"src/legacy/DeployerWhitelist.sol": { "src/legacy/DeployerWhitelist.sol": {
"initCodeHash": "0x53099379ed48b87f027d55712dbdd1da7d7099925426eb0531da9c0012e02c29", "initCodeHash": "0x53099379ed48b87f027d55712dbdd1da7d7099925426eb0531da9c0012e02c29",
......
...@@ -50,7 +50,7 @@ ...@@ -50,7 +50,7 @@
}, },
{ {
"bytes": "32", "bytes": "32",
"label": "credit", "label": "normalModeCredit",
"offset": 0, "offset": 0,
"slot": "3", "slot": "3",
"type": "mapping(address => uint256)" "type": "mapping(address => uint256)"
...@@ -89,5 +89,33 @@ ...@@ -89,5 +89,33 @@
"offset": 0, "offset": 0,
"slot": "8", "slot": "8",
"type": "struct OutputRoot" "type": "struct OutputRoot"
},
{
"bytes": "1",
"label": "wasRespectedGameTypeWhenCreated",
"offset": 0,
"slot": "10",
"type": "bool"
},
{
"bytes": "32",
"label": "refundModeCredit",
"offset": 0,
"slot": "11",
"type": "mapping(address => uint256)"
},
{
"bytes": "32",
"label": "hasUnlockedCredit",
"offset": 0,
"slot": "12",
"type": "mapping(address => bool)"
},
{
"bytes": "1",
"label": "bondDistributionMode",
"offset": 0,
"slot": "13",
"type": "enum BondDistributionMode"
} }
] ]
\ No newline at end of file
...@@ -50,7 +50,7 @@ ...@@ -50,7 +50,7 @@
}, },
{ {
"bytes": "32", "bytes": "32",
"label": "credit", "label": "normalModeCredit",
"offset": 0, "offset": 0,
"slot": "3", "slot": "3",
"type": "mapping(address => uint256)" "type": "mapping(address => uint256)"
...@@ -89,5 +89,33 @@ ...@@ -89,5 +89,33 @@
"offset": 0, "offset": 0,
"slot": "8", "slot": "8",
"type": "struct OutputRoot" "type": "struct OutputRoot"
},
{
"bytes": "1",
"label": "wasRespectedGameTypeWhenCreated",
"offset": 0,
"slot": "10",
"type": "bool"
},
{
"bytes": "32",
"label": "refundModeCredit",
"offset": 0,
"slot": "11",
"type": "mapping(address => uint256)"
},
{
"bytes": "32",
"label": "hasUnlockedCredit",
"offset": 0,
"slot": "12",
"type": "mapping(address => bool)"
},
{
"bytes": "1",
"label": "bondDistributionMode",
"offset": 0,
"slot": "13",
"type": "enum BondDistributionMode"
} }
] ]
\ No newline at end of file
...@@ -28,7 +28,8 @@ import { ...@@ -28,7 +28,8 @@ import {
Blacklisted, Blacklisted,
Unproven, Unproven,
ProposalNotValidated, ProposalNotValidated,
AlreadyFinalized AlreadyFinalized,
LegacyGame
} from "src/libraries/PortalErrors.sol"; } from "src/libraries/PortalErrors.sol";
import { GameStatus, GameType, Claim, Timestamp } from "src/dispute/lib/Types.sol"; import { GameStatus, GameType, Claim, Timestamp } from "src/dispute/lib/Types.sol";
...@@ -176,9 +177,9 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ISemver { ...@@ -176,9 +177,9 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ISemver {
} }
/// @notice Semantic version. /// @notice Semantic version.
/// @custom:semver 3.11.0-beta.11 /// @custom:semver 3.12.0-beta.1
function version() public pure virtual returns (string memory) { function version() public pure virtual returns (string memory) {
return "3.11.0-beta.11"; return "3.12.0-beta.1";
} }
/// @notice Constructs the OptimismPortal contract. /// @notice Constructs the OptimismPortal contract.
...@@ -308,6 +309,24 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ISemver { ...@@ -308,6 +309,24 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ISemver {
// The game type of the dispute game must be the respected game type. // The game type of the dispute game must be the respected game type.
if (gameType.raw() != respectedGameType.raw()) revert InvalidGameType(); if (gameType.raw() != respectedGameType.raw()) revert InvalidGameType();
// The game type of the DisputeGame must have been the respected game type at creation.
try gameProxy.wasRespectedGameTypeWhenCreated() returns (bool wasRespected_) {
if (!wasRespected_) revert InvalidGameType();
} catch {
revert LegacyGame();
}
// Game must have been created after the respected game type was updated. This check is a
// strict inequality because we want to prevent users from being able to prove or finalize
// withdrawals against games that were created in the same block that the retirement
// timestamp was set. If the retirement timestamp and game type are changed in the same
// block, such games could still be considered valid even if they used the old game type
// that we intended to invalidate.
require(
gameProxy.createdAt().raw() > respectedGameTypeUpdatedAt,
"OptimismPortal: dispute game created before respected game type was updated"
);
// Verify that the output root can be generated with the elements in the proof. // Verify that the output root can be generated with the elements in the proof.
if (outputRoot.raw() != Hashing.hashOutputRootProof(_outputRootProof)) revert InvalidProof(); if (outputRoot.raw() != Hashing.hashOutputRootProof(_outputRootProof)) revert InvalidProof();
...@@ -476,9 +495,16 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ISemver { ...@@ -476,9 +495,16 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ISemver {
/// @param _gameType The game type to consult for output proposals. /// @param _gameType The game type to consult for output proposals.
function setRespectedGameType(GameType _gameType) external { function setRespectedGameType(GameType _gameType) external {
if (msg.sender != guardian()) revert Unauthorized(); if (msg.sender != guardian()) revert Unauthorized();
respectedGameType = _gameType; // respectedGameTypeUpdatedAt is now no longer set by default. We want to avoid modifying
respectedGameTypeUpdatedAt = uint64(block.timestamp); // this function's signature as that would result in changes to the DeputyGuardianModule.
emit RespectedGameTypeSet(_gameType, Timestamp.wrap(respectedGameTypeUpdatedAt)); // We use type(uint32).max as a temporary solution to allow us to update the
// respectedGameTypeUpdatedAt timestamp without modifying this function's signature.
if (_gameType.raw() == type(uint32).max) {
respectedGameTypeUpdatedAt = uint64(block.timestamp);
} else {
respectedGameType = _gameType;
}
emit RespectedGameTypeSet(respectedGameType, Timestamp.wrap(respectedGameTypeUpdatedAt));
} }
/// @notice Checks if a withdrawal can be finalized. This function will revert if the withdrawal cannot be /// @notice Checks if a withdrawal can be finalized. This function will revert if the withdrawal cannot be
...@@ -497,6 +523,7 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ISemver { ...@@ -497,6 +523,7 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ISemver {
// a timestamp of zero. // a timestamp of zero.
if (provenWithdrawal.timestamp == 0) revert Unproven(); if (provenWithdrawal.timestamp == 0) revert Unproven();
// Grab the createdAt timestamp once.
uint64 createdAt = disputeGameProxy.createdAt().raw(); uint64 createdAt = disputeGameProxy.createdAt().raw();
// As a sanity check, we make sure that the proven withdrawal's timestamp is greater than // As a sanity check, we make sure that the proven withdrawal's timestamp is greater than
...@@ -518,15 +545,25 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ISemver { ...@@ -518,15 +545,25 @@ contract OptimismPortal2 is Initializable, ResourceMetering, ISemver {
// from finalizing withdrawals proven against non-finalized output roots. // from finalizing withdrawals proven against non-finalized output roots.
if (disputeGameProxy.status() != GameStatus.DEFENDER_WINS) revert ProposalNotValidated(); if (disputeGameProxy.status() != GameStatus.DEFENDER_WINS) revert ProposalNotValidated();
// The game type of the dispute game must be the respected game type. This was also checked in // The game type of the dispute game must have been the respected game type at creation
// `proveWithdrawalTransaction`, but we check it again in case the respected game type has changed since // time. We check that the game type is the respected game type at proving time, but it's
// the withdrawal was proven. // possible that the respected game type has since changed. Users can still use this game
if (disputeGameProxy.gameType().raw() != respectedGameType.raw()) revert InvalidGameType(); // to finalize a withdrawal as long as it has not been otherwise invalidated.
// The game type of the DisputeGame must have been the respected game type at creation.
try disputeGameProxy.wasRespectedGameTypeWhenCreated() returns (bool wasRespected_) {
if (!wasRespected_) revert InvalidGameType();
} catch {
revert LegacyGame();
}
// The game must have been created after `respectedGameTypeUpdatedAt`. This is to prevent users from creating // Game must have been created after the respected game type was updated. This check is a
// invalid disputes against a deployed game type while the off-chain challenge agents are not watching. // strict inequality because we want to prevent users from being able to prove or finalize
// withdrawals against games that were created in the same block that the retirement
// timestamp was set. If the retirement timestamp and game type are changed in the same
// block, such games could still be considered valid even if they used the old game type
// that we intended to invalidate.
require( require(
createdAt >= respectedGameTypeUpdatedAt, createdAt > respectedGameTypeUpdatedAt,
"OptimismPortal: dispute game created before respected game type was updated" "OptimismPortal: dispute game created before respected game type was updated"
); );
......
...@@ -28,9 +28,9 @@ contract OptimismPortalInterop is OptimismPortal2 { ...@@ -28,9 +28,9 @@ contract OptimismPortalInterop is OptimismPortal2 {
OptimismPortal2(_proofMaturityDelaySeconds, _disputeGameFinalityDelaySeconds) OptimismPortal2(_proofMaturityDelaySeconds, _disputeGameFinalityDelaySeconds)
{ } { }
/// @custom:semver +interop-beta.8 /// @custom:semver +interop-beta.9
function version() public pure override returns (string memory) { function version() public pure override returns (string memory) {
return string.concat(super.version(), "+interop-beta.8"); return string.concat(super.version(), "+interop-beta.9");
} }
/// @notice Sets static configuration options for the L2 system. /// @notice Sets static configuration options for the L2 system.
......
...@@ -26,14 +26,9 @@ contract DelayedWETH is OwnableUpgradeable, WETH98, ISemver { ...@@ -26,14 +26,9 @@ contract DelayedWETH is OwnableUpgradeable, WETH98, ISemver {
uint256 timestamp; uint256 timestamp;
} }
/// @notice Emitted when an unwrap is started.
/// @param src The address that started the unwrap.
/// @param wad The amount of WETH that was unwrapped.
event Unwrap(address indexed src, uint256 wad);
/// @notice Semantic version. /// @notice Semantic version.
/// @custom:semver 1.2.0-beta.5 /// @custom:semver 1.3.0-beta.1
string public constant version = "1.2.0-beta.5"; string public constant version = "1.3.0-beta.1";
/// @notice Returns a withdrawal request for the given address. /// @notice Returns a withdrawal request for the given address.
mapping(address => mapping(address => WithdrawalRequest)) public withdrawals; mapping(address => mapping(address => WithdrawalRequest)) public withdrawals;
...@@ -107,12 +102,19 @@ contract DelayedWETH is OwnableUpgradeable, WETH98, ISemver { ...@@ -107,12 +102,19 @@ contract DelayedWETH is OwnableUpgradeable, WETH98, ISemver {
require(success, "DelayedWETH: recover failed"); require(success, "DelayedWETH: recover failed");
} }
/// @notice Allows the owner to recover from error cases by pulling ETH from a specific owner. /// @notice Allows the owner to recover from error cases by pulling all WETH from a specific owner.
/// @param _guy The address to recover the WETH from.
function hold(address _guy) external {
hold(_guy, balanceOf(_guy));
}
/// @notice Allows the owner to recover from error cases by pulling a specific amount of WETH from a specific owner.
/// @param _guy The address to recover the WETH from. /// @param _guy The address to recover the WETH from.
/// @param _wad The amount of WETH to recover. /// @param _wad The amount of WETH to recover.
function hold(address _guy, uint256 _wad) external { function hold(address _guy, uint256 _wad) public {
require(msg.sender == owner(), "DelayedWETH: not owner"); require(msg.sender == owner(), "DelayedWETH: not owner");
_allowance[_guy][msg.sender] = _wad; _allowance[_guy][msg.sender] = _wad;
emit Approval(_guy, msg.sender, _wad); emit Approval(_guy, msg.sender, _wad);
transferFrom(_guy, msg.sender, _wad);
} }
} }
...@@ -121,6 +121,18 @@ error BlockNumberMatches(); ...@@ -121,6 +121,18 @@ error BlockNumberMatches();
/// @notice Thrown when the L2 block number claim has already been challenged. /// @notice Thrown when the L2 block number claim has already been challenged.
error L2BlockNumberChallenged(); error L2BlockNumberChallenged();
/// @notice Thrown when the game is not yet finalized.
error GameNotFinalized();
/// @notice Thrown when an invalid bond distribution mode is supplied.
error InvalidBondDistributionMode();
/// @notice Thrown when the game is not yet resolved.
error GameNotResolved();
/// @notice Thrown when a reserved game type is used.
error ReservedGameType();
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
// `PermissionedDisputeGame` Errors // // `PermissionedDisputeGame` Errors //
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
......
...@@ -26,6 +26,17 @@ enum GameStatus { ...@@ -26,6 +26,17 @@ enum GameStatus {
DEFENDER_WINS DEFENDER_WINS
} }
/// @notice The game's bond distribution type. Games are expected to start in the `UNDECIDED`
/// state, and then choose either `NORMAL` or `REFUND`.
enum BondDistributionMode {
// Bond distribution strategy has not been chosen.
UNDECIDED,
// Bonds should be distributed as normal.
NORMAL,
// Bonds should be refunded to claimants.
REFUND
}
/// @notice Represents an L2 output root and the L2 block number at which it was generated. /// @notice Represents an L2 output root and the L2 block number at which it was generated.
/// @custom:field root The output root. /// @custom:field root The output root.
/// @custom:field l2BlockNumber The L2 block number at which the output root was generated. /// @custom:field l2BlockNumber The L2 block number at which the output root was generated.
......
...@@ -38,3 +38,5 @@ error Unproven(); ...@@ -38,3 +38,5 @@ error Unproven();
error ProposalNotValidated(); error ProposalNotValidated();
/// @notice Error for when a withdrawal has already been finalized. /// @notice Error for when a withdrawal has already been finalized.
error AlreadyFinalized(); error AlreadyFinalized();
/// @notice Error for when a game is a legacy game.
error LegacyGame();
...@@ -352,26 +352,48 @@ contract DelayedWETH_Recover_Test is DelayedWETH_Init { ...@@ -352,26 +352,48 @@ contract DelayedWETH_Recover_Test is DelayedWETH_Init {
contract DelayedWETH_Hold_Test is DelayedWETH_Init { contract DelayedWETH_Hold_Test is DelayedWETH_Init {
/// @dev Tests that holding WETH succeeds. /// @dev Tests that holding WETH succeeds.
function test_hold_succeeds() public { function test_hold_byOwner_succeeds() public {
uint256 amount = 1 ether; uint256 amount = 1 ether;
// Pretend to be alice and deposit some WETH. // Pretend to be alice and deposit some WETH.
vm.prank(alice); vm.prank(alice);
delayedWeth.deposit{ value: amount }(); delayedWeth.deposit{ value: amount }();
// Get our balance before.
uint256 initialBalance = delayedWeth.balanceOf(address(this));
// Hold some WETH. // Hold some WETH.
vm.expectEmit(true, true, true, false); vm.expectEmit(true, true, true, false);
emit Approval(alice, address(this), amount); emit Approval(alice, address(this), amount);
delayedWeth.hold(alice, amount); delayedWeth.hold(alice, amount);
// Verify the allowance. // Get our balance after.
assertEq(delayedWeth.allowance(alice, address(this)), amount); uint256 finalBalance = delayedWeth.balanceOf(address(this));
// Verify the transfer.
assertEq(finalBalance, initialBalance + amount);
}
function test_hold_withoutAmount_succeeds() public {
uint256 amount = 1 ether;
// Pretend to be alice and deposit some WETH.
vm.prank(alice);
delayedWeth.deposit{ value: amount }();
// Get our balance before.
uint256 initialBalance = delayedWeth.balanceOf(address(this));
// Hold some WETH.
vm.expectEmit(true, true, true, false);
emit Approval(alice, address(this), amount);
delayedWeth.hold(alice); // without amount parameter
// We can transfer. // Get our balance after.
delayedWeth.transferFrom(alice, address(this), amount); uint256 finalBalance = delayedWeth.balanceOf(address(this));
// Verify the transfer. // Verify the transfer.
assertEq(delayedWeth.balanceOf(address(this)), amount); assertEq(finalBalance, initialBalance + amount);
} }
/// @dev Tests that holding WETH by non-owner fails. /// @dev Tests that holding WETH by non-owner fails.
......
...@@ -45,6 +45,16 @@ contract FaultDisputeGame_Solvency_Invariant is FaultDisputeGame_Init { ...@@ -45,6 +45,16 @@ contract FaultDisputeGame_Solvency_Invariant is FaultDisputeGame_Init {
} }
gameProxy.resolve(); gameProxy.resolve();
// Wait for finalization delay
vm.warp(block.timestamp + 3.5 days + 1 seconds);
// Close the game.
gameProxy.closeGame();
// Claim credit once to trigger unlock period.
gameProxy.claimCredit(address(this));
gameProxy.claimCredit(address(actor));
// Wait for the withdrawal delay. // Wait for the withdrawal delay.
vm.warp(block.timestamp + 7 days + 1 seconds); vm.warp(block.timestamp + 7 days + 1 seconds);
......
...@@ -118,6 +118,9 @@ contract OptimismPortal2_Invariant_Harness is CommonTest { ...@@ -118,6 +118,9 @@ contract OptimismPortal2_Invariant_Harness is CommonTest {
latestBlockhash: bytes32(uint256(0)) latestBlockhash: bytes32(uint256(0))
}); });
// Warp forward in time to ensure that the game is created after the retirement timestamp.
vm.warp(optimismPortal2.respectedGameTypeUpdatedAt() + 1 seconds);
// Create a dispute game with the output root we've proposed. // Create a dispute game with the output root we've proposed.
_proposedBlockNumber = 0xFF; _proposedBlockNumber = 0xFF;
IFaultDisputeGame game = IFaultDisputeGame( IFaultDisputeGame game = IFaultDisputeGame(
......
...@@ -240,6 +240,11 @@ contract DeputyGuardianModule_setRespectedGameType_Test is DeputyGuardianModule_ ...@@ -240,6 +240,11 @@ contract DeputyGuardianModule_setRespectedGameType_Test is DeputyGuardianModule_
/// @dev Tests that `setRespectedGameType` successfully updates the respected game type when called by the deputy /// @dev Tests that `setRespectedGameType` successfully updates the respected game type when called by the deputy
/// guardian. /// guardian.
function testFuzz_setRespectedGameType_succeeds(GameType _gameType) external { function testFuzz_setRespectedGameType_succeeds(GameType _gameType) external {
// Game type(uint32).max is reserved for setting the respectedGameTypeUpdatedAt timestamp.
// TODO(kelvin): Remove this once we've removed the hack.
uint32 boundedGameType = uint32(bound(_gameType.raw(), 0, type(uint32).max - 1));
_gameType = GameType.wrap(boundedGameType);
vm.expectEmit(address(safeInstance.safe)); vm.expectEmit(address(safeInstance.safe));
emit ExecutionFromModuleSuccess(address(deputyGuardianModule)); emit ExecutionFromModuleSuccess(address(deputyGuardianModule));
......
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