Commit 380d8cf5 authored by smartcontracts's avatar smartcontracts Committed by GitHub

feat: introduce AnchorStateRegistry (#9835)

* feat: introduce AnchorStateRegistry

Introduces the AnchorStateRegistry used to hold anchor states for
different game types. Anchor states are updated automatically when
a game resolves.

* kontrol snapshot

---------
Co-authored-by: default avatarclabby <ben@clab.by>
parent dc2b7141
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -16,29 +16,29 @@ import ( ...@@ -16,29 +16,29 @@ import (
) )
var ( var (
methodGameDuration = "gameDuration" methodGameDuration = "gameDuration"
methodMaxGameDepth = "maxGameDepth" methodMaxGameDepth = "maxGameDepth"
methodAbsolutePrestate = "absolutePrestate" methodAbsolutePrestate = "absolutePrestate"
methodStatus = "status" methodStatus = "status"
methodRootClaim = "rootClaim" methodRootClaim = "rootClaim"
methodClaimCount = "claimDataLen" methodClaimCount = "claimDataLen"
methodClaim = "claimData" methodClaim = "claimData"
methodL1Head = "l1Head" methodL1Head = "l1Head"
methodResolve = "resolve" methodResolve = "resolve"
methodResolveClaim = "resolveClaim" methodResolveClaim = "resolveClaim"
methodAttack = "attack" methodAttack = "attack"
methodDefend = "defend" methodDefend = "defend"
methodStep = "step" methodStep = "step"
methodAddLocalData = "addLocalData" methodAddLocalData = "addLocalData"
methodVM = "vm" methodVM = "vm"
methodGenesisBlockNumber = "genesisBlockNumber" methodStartingBlockNumber = "startingBlockNumber"
methodGenesisOutputRoot = "genesisOutputRoot" methodStartingRootHash = "startingRootHash"
methodSplitDepth = "splitDepth" methodSplitDepth = "splitDepth"
methodL2BlockNumber = "l2BlockNumber" methodL2BlockNumber = "l2BlockNumber"
methodRequiredBond = "getRequiredBond" methodRequiredBond = "getRequiredBond"
methodClaimCredit = "claimCredit" methodClaimCredit = "claimCredit"
methodCredit = "credit" methodCredit = "credit"
methodWETH = "weth" methodWETH = "weth"
) )
type FaultDisputeGameContract struct { type FaultDisputeGameContract struct {
...@@ -83,7 +83,7 @@ func (f *FaultDisputeGameContract) GetBalance(ctx context.Context, block rpcbloc ...@@ -83,7 +83,7 @@ func (f *FaultDisputeGameContract) GetBalance(ctx context.Context, block rpcbloc
// and the post-state block (that the proposed output root is for). // and the post-state block (that the proposed output root is for).
func (f *FaultDisputeGameContract) GetBlockRange(ctx context.Context) (prestateBlock uint64, poststateBlock uint64, retErr error) { func (f *FaultDisputeGameContract) GetBlockRange(ctx context.Context) (prestateBlock uint64, poststateBlock uint64, retErr error) {
results, err := f.multiCaller.Call(ctx, rpcblock.Latest, results, err := f.multiCaller.Call(ctx, rpcblock.Latest,
f.contract.Call(methodGenesisBlockNumber), f.contract.Call(methodStartingBlockNumber),
f.contract.Call(methodL2BlockNumber)) f.contract.Call(methodL2BlockNumber))
if err != nil { if err != nil {
retErr = fmt.Errorf("failed to retrieve game block range: %w", err) retErr = fmt.Errorf("failed to retrieve game block range: %w", err)
...@@ -123,12 +123,12 @@ func (f *FaultDisputeGameContract) GetGameMetadata(ctx context.Context, block rp ...@@ -123,12 +123,12 @@ func (f *FaultDisputeGameContract) GetGameMetadata(ctx context.Context, block rp
return l1Head, l2BlockNumber, rootClaim, status, duration, nil return l1Head, l2BlockNumber, rootClaim, status, duration, nil
} }
func (f *FaultDisputeGameContract) GetGenesisOutputRoot(ctx context.Context) (common.Hash, error) { func (f *FaultDisputeGameContract) GetStartingRootHash(ctx context.Context) (common.Hash, error) {
genesisOutputRoot, err := f.multiCaller.SingleCall(ctx, rpcblock.Latest, f.contract.Call(methodGenesisOutputRoot)) startingRootHash, err := f.multiCaller.SingleCall(ctx, rpcblock.Latest, f.contract.Call(methodStartingRootHash))
if err != nil { if err != nil {
return common.Hash{}, fmt.Errorf("failed to retrieve genesis output root: %w", err) return common.Hash{}, fmt.Errorf("failed to retrieve genesis output root: %w", err)
} }
return genesisOutputRoot.GetHash(0), nil return startingRootHash.GetHash(0), nil
} }
func (f *FaultDisputeGameContract) GetSplitDepth(ctx context.Context) (types.Depth, error) { func (f *FaultDisputeGameContract) GetSplitDepth(ctx context.Context) (types.Depth, error) {
......
...@@ -315,7 +315,7 @@ func TestGetBlockRange(t *testing.T) { ...@@ -315,7 +315,7 @@ func TestGetBlockRange(t *testing.T) {
stubRpc, contract := setupFaultDisputeGameTest(t) stubRpc, contract := setupFaultDisputeGameTest(t)
expectedStart := uint64(65) expectedStart := uint64(65)
expectedEnd := uint64(102) expectedEnd := uint64(102)
stubRpc.SetResponse(fdgAddr, methodGenesisBlockNumber, rpcblock.Latest, nil, []interface{}{new(big.Int).SetUint64(expectedStart)}) stubRpc.SetResponse(fdgAddr, methodStartingBlockNumber, rpcblock.Latest, nil, []interface{}{new(big.Int).SetUint64(expectedStart)})
stubRpc.SetResponse(fdgAddr, methodL2BlockNumber, rpcblock.Latest, nil, []interface{}{new(big.Int).SetUint64(expectedEnd)}) stubRpc.SetResponse(fdgAddr, methodL2BlockNumber, rpcblock.Latest, nil, []interface{}{new(big.Int).SetUint64(expectedEnd)})
start, end, err := contract.GetBlockRange(context.Background()) start, end, err := contract.GetBlockRange(context.Background())
require.NoError(t, err) require.NoError(t, err)
...@@ -354,13 +354,13 @@ func TestGetGameMetadata(t *testing.T) { ...@@ -354,13 +354,13 @@ func TestGetGameMetadata(t *testing.T) {
require.Equal(t, expectedGameDuration, duration) require.Equal(t, expectedGameDuration, duration)
} }
func TestGetGenesisOutputRoot(t *testing.T) { func TestGetStartingRootHash(t *testing.T) {
stubRpc, contract := setupFaultDisputeGameTest(t) stubRpc, contract := setupFaultDisputeGameTest(t)
expectedOutputRoot := common.HexToHash("0x1234") expectedOutputRoot := common.HexToHash("0x1234")
stubRpc.SetResponse(fdgAddr, methodGenesisOutputRoot, rpcblock.Latest, nil, []interface{}{expectedOutputRoot}) stubRpc.SetResponse(fdgAddr, methodStartingRootHash, rpcblock.Latest, nil, []interface{}{expectedOutputRoot})
genesisOutputRoot, err := contract.GetGenesisOutputRoot(context.Background()) startingOutputRoot, err := contract.GetStartingRootHash(context.Background())
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, expectedOutputRoot, genesisOutputRoot) require.Equal(t, expectedOutputRoot, startingOutputRoot)
} }
func TestFaultDisputeGame_UpdateOracleTx(t *testing.T) { func TestFaultDisputeGame_UpdateOracleTx(t *testing.T) {
......
...@@ -131,8 +131,8 @@ func registerAlphabet( ...@@ -131,8 +131,8 @@ func registerAlphabet(
return accessor, nil return accessor, nil
} }
prestateValidator := NewPrestateValidator("alphabet", contract.GetAbsolutePrestateHash, alphabet.PrestateProvider) prestateValidator := NewPrestateValidator("alphabet", contract.GetAbsolutePrestateHash, alphabet.PrestateProvider)
genesisValidator := NewPrestateValidator("output root", contract.GetGenesisOutputRoot, prestateProvider) startingValidator := NewPrestateValidator("output root", contract.GetStartingRootHash, prestateProvider)
return NewGamePlayer(ctx, cl, logger, m, dir, game.Proxy, txSender, contract, syncValidator, []Validator{prestateValidator, genesisValidator}, creator, l1HeaderSource, selective, claimants) return NewGamePlayer(ctx, cl, logger, m, dir, game.Proxy, txSender, contract, syncValidator, []Validator{prestateValidator, startingValidator}, creator, l1HeaderSource, selective, claimants)
} }
err := registerOracle(ctx, oracles, gameFactory, caller, faultTypes.AlphabetGameType) err := registerOracle(ctx, oracles, gameFactory, caller, faultTypes.AlphabetGameType)
if err != nil { if err != nil {
...@@ -215,8 +215,8 @@ func registerCannon( ...@@ -215,8 +215,8 @@ func registerCannon(
return accessor, nil return accessor, nil
} }
prestateValidator := NewPrestateValidator("cannon", contract.GetAbsolutePrestateHash, cannonPrestateProvider) prestateValidator := NewPrestateValidator("cannon", contract.GetAbsolutePrestateHash, cannonPrestateProvider)
genesisValidator := NewPrestateValidator("output root", contract.GetGenesisOutputRoot, prestateProvider) startingValidator := NewPrestateValidator("output root", contract.GetStartingRootHash, prestateProvider)
return NewGamePlayer(ctx, cl, logger, m, dir, game.Proxy, txSender, contract, syncValidator, []Validator{prestateValidator, genesisValidator}, creator, l1HeaderSource, selective, claimants) return NewGamePlayer(ctx, cl, logger, m, dir, game.Proxy, txSender, contract, syncValidator, []Validator{prestateValidator, startingValidator}, creator, l1HeaderSource, selective, claimants)
} }
err := registerOracle(ctx, oracles, gameFactory, caller, gameType) err := registerOracle(ctx, oracles, gameFactory, caller, gameType)
if err != nil { if err != nil {
......
...@@ -185,8 +185,8 @@ func (h *FactoryHelper) StartOutputCannonGame(ctx context.Context, l2Node string ...@@ -185,8 +185,8 @@ func (h *FactoryHelper) StartOutputCannonGame(ctx context.Context, l2Node string
h.require.NoError(err) h.require.NoError(err)
callOpts := &bind.CallOpts{Context: ctx} callOpts := &bind.CallOpts{Context: ctx}
prestateBlock, err := game.GenesisBlockNumber(callOpts) prestateBlock, err := game.StartingBlockNumber(callOpts)
h.require.NoError(err, "Failed to load genesis block number") h.require.NoError(err, "Failed to load starting block number")
poststateBlock, err := game.L2BlockNumber(callOpts) poststateBlock, err := game.L2BlockNumber(callOpts)
h.require.NoError(err, "Failed to load l2 block number") h.require.NoError(err, "Failed to load l2 block number")
splitDepth, err := game.SplitDepth(callOpts) splitDepth, err := game.SplitDepth(callOpts)
...@@ -251,8 +251,8 @@ func (h *FactoryHelper) StartOutputAlphabetGame(ctx context.Context, l2Node stri ...@@ -251,8 +251,8 @@ func (h *FactoryHelper) StartOutputAlphabetGame(ctx context.Context, l2Node stri
h.require.NoError(err) h.require.NoError(err)
callOpts := &bind.CallOpts{Context: ctx} callOpts := &bind.CallOpts{Context: ctx}
prestateBlock, err := game.GenesisBlockNumber(callOpts) prestateBlock, err := game.StartingBlockNumber(callOpts)
h.require.NoError(err, "Failed to load genesis block number") h.require.NoError(err, "Failed to load starting block number")
poststateBlock, err := game.L2BlockNumber(callOpts) poststateBlock, err := game.L2BlockNumber(callOpts)
h.require.NoError(err, "Failed to load l2 block number") h.require.NoError(err, "Failed to load l2 block number")
splitDepth, err := game.SplitDepth(callOpts) splitDepth, err := game.SplitDepth(callOpts)
......
...@@ -87,9 +87,9 @@ func (g *OutputGameHelper) L2BlockNum(ctx context.Context) uint64 { ...@@ -87,9 +87,9 @@ func (g *OutputGameHelper) L2BlockNum(ctx context.Context) uint64 {
return blockNum.Uint64() return blockNum.Uint64()
} }
func (g *OutputGameHelper) GenesisBlockNum(ctx context.Context) uint64 { func (g *OutputGameHelper) StartingBlockNum(ctx context.Context) uint64 {
blockNum, err := g.game.GenesisBlockNumber(&bind.CallOpts{Context: ctx}) blockNum, err := g.game.StartingBlockNumber(&bind.CallOpts{Context: ctx})
g.require.NoError(err, "failed to load genesis block number") g.require.NoError(err, "failed to load starting block number")
return blockNum.Uint64() return blockNum.Uint64()
} }
......
...@@ -8,4 +8,4 @@ GasBenchMark_L1StandardBridge_Finalize:test_finalizeETHWithdrawal_benchmark() (g ...@@ -8,4 +8,4 @@ GasBenchMark_L1StandardBridge_Finalize:test_finalizeETHWithdrawal_benchmark() (g
GasBenchMark_L2OutputOracle:test_proposeL2Output_benchmark() (gas: 92930) GasBenchMark_L2OutputOracle:test_proposeL2Output_benchmark() (gas: 92930)
GasBenchMark_OptimismPortal:test_depositTransaction_benchmark() (gas: 68360) GasBenchMark_OptimismPortal:test_depositTransaction_benchmark() (gas: 68360)
GasBenchMark_OptimismPortal:test_depositTransaction_benchmark_1() (gas: 69013) GasBenchMark_OptimismPortal:test_depositTransaction_benchmark_1() (gas: 69013)
GasBenchMark_OptimismPortal:test_proveWithdrawalTransaction_benchmark() (gas: 155556) GasBenchMark_OptimismPortal:test_proveWithdrawalTransaction_benchmark() (gas: 155551)
\ No newline at end of file \ No newline at end of file
...@@ -53,7 +53,7 @@ ...@@ -53,7 +53,7 @@
"faultGameMaxDepth": 50, "faultGameMaxDepth": 50,
"faultGameMaxDuration": 2400, "faultGameMaxDuration": 2400,
"faultGameGenesisBlock": 0, "faultGameGenesisBlock": 0,
"faultGameGenesisOutputRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", "faultGameGenesisOutputRoot": "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF",
"faultGameSplitDepth": 14, "faultGameSplitDepth": 14,
"faultGameWithdrawalDelay": 604800, "faultGameWithdrawalDelay": 604800,
"preimageOracleMinProposalSize": 10000, "preimageOracleMinProposalSize": 10000,
......
...@@ -47,7 +47,7 @@ ...@@ -47,7 +47,7 @@
"faultGameMaxDepth": 8, "faultGameMaxDepth": 8,
"faultGameMaxDuration": 2400, "faultGameMaxDuration": 2400,
"faultGameGenesisBlock": 0, "faultGameGenesisBlock": 0,
"faultGameGenesisOutputRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", "faultGameGenesisOutputRoot": "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF",
"faultGameSplitDepth": 4, "faultGameSplitDepth": 4,
"faultGameWithdrawalDelay": 604800, "faultGameWithdrawalDelay": 604800,
"preimageOracleMinProposalSize": 10000, "preimageOracleMinProposalSize": 10000,
......
# `FaultDisputeGame` Invariants # `FaultDisputeGame` Invariants
## FaultDisputeGame always returns all ETH on total resolution ## FaultDisputeGame always returns all ETH on total resolution
**Test:** [`FaultDisputeGame.t.sol#L39`](../test/invariants/FaultDisputeGame.t.sol#L39) **Test:** [`FaultDisputeGame.t.sol#L33`](../test/invariants/FaultDisputeGame.t.sol#L33)
The FaultDisputeGame contract should always return all ETH in the contract to the correct recipients upon resolution of all outstanding claims. There may never be any ETH left in the contract after a full resolution. The FaultDisputeGame contract should always return all ETH in the contract to the correct recipients upon resolution of all outstanding claims. There may never be any ETH left in the contract after a full resolution.
\ No newline at end of file
...@@ -34,6 +34,7 @@ import { DisputeGameFactory } from "src/dispute/DisputeGameFactory.sol"; ...@@ -34,6 +34,7 @@ import { DisputeGameFactory } from "src/dispute/DisputeGameFactory.sol";
import { FaultDisputeGame } from "src/dispute/FaultDisputeGame.sol"; import { FaultDisputeGame } from "src/dispute/FaultDisputeGame.sol";
import { PermissionedDisputeGame } from "src/dispute/PermissionedDisputeGame.sol"; import { PermissionedDisputeGame } from "src/dispute/PermissionedDisputeGame.sol";
import { DelayedWETH } from "src/dispute/weth/DelayedWETH.sol"; import { DelayedWETH } from "src/dispute/weth/DelayedWETH.sol";
import { AnchorStateRegistry } from "src/dispute/AnchorStateRegistry.sol";
import { PreimageOracle } from "src/cannon/PreimageOracle.sol"; import { PreimageOracle } from "src/cannon/PreimageOracle.sol";
import { MIPS } from "src/cannon/MIPS.sol"; import { MIPS } from "src/cannon/MIPS.sol";
import { L1ERC721Bridge } from "src/L1/L1ERC721Bridge.sol"; import { L1ERC721Bridge } from "src/L1/L1ERC721Bridge.sol";
...@@ -69,6 +70,7 @@ contract Deploy is Deployer { ...@@ -69,6 +70,7 @@ contract Deploy is Deployer {
/// to finally adopt PUSHN and get rid of stack too deep once and for all. /// to finally adopt PUSHN and get rid of stack too deep once and for all.
/// Someday we will look back and laugh about stack too deep, today is not that day. /// Someday we will look back and laugh about stack too deep, today is not that day.
struct FaultDisputeGameParams { struct FaultDisputeGameParams {
AnchorStateRegistry anchorStateRegistry;
DelayedWETH weth; DelayedWETH weth;
GameType gameType; GameType gameType;
Claim absolutePrestate; Claim absolutePrestate;
...@@ -144,6 +146,7 @@ contract Deploy is Deployer { ...@@ -144,6 +146,7 @@ contract Deploy is Deployer {
L2OutputOracle: mustGetAddress("L2OutputOracleProxy"), L2OutputOracle: mustGetAddress("L2OutputOracleProxy"),
DisputeGameFactory: mustGetAddress("DisputeGameFactoryProxy"), DisputeGameFactory: mustGetAddress("DisputeGameFactoryProxy"),
DelayedWETH: mustGetAddress("DelayedWETHProxy"), DelayedWETH: mustGetAddress("DelayedWETHProxy"),
AnchorStateRegistry: mustGetAddress("AnchorStateRegistryProxy"),
OptimismMintableERC20Factory: mustGetAddress("OptimismMintableERC20FactoryProxy"), OptimismMintableERC20Factory: mustGetAddress("OptimismMintableERC20FactoryProxy"),
OptimismPortal: mustGetAddress("OptimismPortalProxy"), OptimismPortal: mustGetAddress("OptimismPortalProxy"),
OptimismPortal2: mustGetAddress("OptimismPortalProxy"), OptimismPortal2: mustGetAddress("OptimismPortalProxy"),
...@@ -162,6 +165,7 @@ contract Deploy is Deployer { ...@@ -162,6 +165,7 @@ contract Deploy is Deployer {
L2OutputOracle: getAddress("L2OutputOracleProxy"), L2OutputOracle: getAddress("L2OutputOracleProxy"),
DisputeGameFactory: getAddress("DisputeGameFactoryProxy"), DisputeGameFactory: getAddress("DisputeGameFactoryProxy"),
DelayedWETH: getAddress("DelayedWETHProxy"), DelayedWETH: getAddress("DelayedWETHProxy"),
AnchorStateRegistry: getAddress("AnchorStateRegistryProxy"),
OptimismMintableERC20Factory: getAddress("OptimismMintableERC20FactoryProxy"), OptimismMintableERC20Factory: getAddress("OptimismMintableERC20FactoryProxy"),
OptimismPortal: getAddress("OptimismPortalProxy"), OptimismPortal: getAddress("OptimismPortalProxy"),
OptimismPortal2: getAddress("OptimismPortalProxy"), OptimismPortal2: getAddress("OptimismPortalProxy"),
...@@ -345,6 +349,7 @@ contract Deploy is Deployer { ...@@ -345,6 +349,7 @@ contract Deploy is Deployer {
deployERC1967Proxy("DisputeGameFactoryProxy"); deployERC1967Proxy("DisputeGameFactoryProxy");
deployERC1967Proxy("L2OutputOracleProxy"); deployERC1967Proxy("L2OutputOracleProxy");
deployERC1967Proxy("DelayedWETHProxy"); deployERC1967Proxy("DelayedWETHProxy");
deployERC1967Proxy("AnchorStateRegistryProxy");
transferAddressManagerOwnership(); // to the ProxyAdmin transferAddressManagerOwnership(); // to the ProxyAdmin
} }
...@@ -366,6 +371,7 @@ contract Deploy is Deployer { ...@@ -366,6 +371,7 @@ contract Deploy is Deployer {
deployDelayedWETH(); deployDelayedWETH();
deployPreimageOracle(); deployPreimageOracle();
deployMips(); deployMips();
deployAnchorStateRegistry();
} }
/// @notice Initialize all of the implementations /// @notice Initialize all of the implementations
...@@ -379,6 +385,7 @@ contract Deploy is Deployer { ...@@ -379,6 +385,7 @@ contract Deploy is Deployer {
initializeL2OutputOracle(); initializeL2OutputOracle();
initializeDisputeGameFactory(); initializeDisputeGameFactory();
initializeDelayedWETH(); initializeDelayedWETH();
initializeAnchorStateRegistry();
// Selectively initialize either the original OptimismPortal or the new OptimismPortal2. Since this will upgrade // Selectively initialize either the original OptimismPortal or the new OptimismPortal2. Since this will upgrade
// the proxy, we cannot initialize both. FPAC warning can be removed once we're done with the old OptimismPortal // the proxy, we cannot initialize both. FPAC warning can be removed once we're done with the old OptimismPortal
...@@ -731,6 +738,17 @@ contract Deploy is Deployer { ...@@ -731,6 +738,17 @@ contract Deploy is Deployer {
addr_ = address(mips); addr_ = address(mips);
} }
/// @notice Deploy the AnchorStateRegistry
function deployAnchorStateRegistry() public broadcast returns (address addr_) {
console.log("Deploying AnchorStateRegistry implementation");
AnchorStateRegistry anchorStateRegistry =
new AnchorStateRegistry{ salt: _implSalt() }(DisputeGameFactory(mustGetAddress("DisputeGameFactoryProxy")));
save("AnchorStateRegistry", address(anchorStateRegistry));
console.log("AnchorStateRegistry deployed at %s", address(anchorStateRegistry));
addr_ = address(anchorStateRegistry);
}
/// @notice Deploy the SystemConfig /// @notice Deploy the SystemConfig
function deploySystemConfig() public broadcast returns (address addr_) { function deploySystemConfig() public broadcast returns (address addr_) {
console.log("Deploying SystemConfig implementation"); console.log("Deploying SystemConfig implementation");
...@@ -869,6 +887,44 @@ contract Deploy is Deployer { ...@@ -869,6 +887,44 @@ contract Deploy is Deployer {
}); });
} }
function initializeAnchorStateRegistry() public broadcast {
console.log("Upgrading and initializing AnchorStateRegistry proxy");
address anchorStateRegistryProxy = mustGetAddress("AnchorStateRegistryProxy");
address anchorStateRegistry = mustGetAddress("AnchorStateRegistry");
AnchorStateRegistry.StartingAnchorRoot[] memory roots = new AnchorStateRegistry.StartingAnchorRoot[](3);
roots[0] = AnchorStateRegistry.StartingAnchorRoot({
gameType: GameTypes.CANNON,
outputRoot: OutputRoot({
root: Hash.wrap(cfg.faultGameGenesisOutputRoot()),
l2BlockNumber: cfg.faultGameGenesisBlock()
})
});
roots[1] = AnchorStateRegistry.StartingAnchorRoot({
gameType: GameTypes.PERMISSIONED_CANNON,
outputRoot: OutputRoot({
root: Hash.wrap(cfg.faultGameGenesisOutputRoot()),
l2BlockNumber: cfg.faultGameGenesisBlock()
})
});
roots[2] = AnchorStateRegistry.StartingAnchorRoot({
gameType: GameTypes.ALPHABET,
outputRoot: OutputRoot({
root: Hash.wrap(cfg.faultGameGenesisOutputRoot()),
l2BlockNumber: cfg.faultGameGenesisBlock()
})
});
_upgradeAndCallViaSafe({
_proxy: payable(anchorStateRegistryProxy),
_implementation: anchorStateRegistry,
_innerCallData: abi.encodeCall(AnchorStateRegistry.initialize, (roots))
});
string memory version = AnchorStateRegistry(payable(anchorStateRegistryProxy)).version();
console.log("AnchorStateRegistry version: %s", version);
}
/// @notice Initialize the SystemConfig /// @notice Initialize the SystemConfig
function initializeSystemConfig() public broadcast { function initializeSystemConfig() public broadcast {
console.log("Upgrading and initializing SystemConfig proxy"); console.log("Upgrading and initializing SystemConfig proxy");
...@@ -1222,6 +1278,7 @@ contract Deploy is Deployer { ...@@ -1222,6 +1278,7 @@ contract Deploy is Deployer {
_factory: factory, _factory: factory,
_allowUpgrade: _allowUpgrade, _allowUpgrade: _allowUpgrade,
_params: FaultDisputeGameParams({ _params: FaultDisputeGameParams({
anchorStateRegistry: AnchorStateRegistry(mustGetAddress("AnchorStateRegistryProxy")),
weth: weth, weth: weth,
gameType: GameTypes.CANNON, gameType: GameTypes.CANNON,
absolutePrestate: loadMipsAbsolutePrestate(), absolutePrestate: loadMipsAbsolutePrestate(),
...@@ -1242,6 +1299,7 @@ contract Deploy is Deployer { ...@@ -1242,6 +1299,7 @@ contract Deploy is Deployer {
_factory: factory, _factory: factory,
_allowUpgrade: _allowUpgrade, _allowUpgrade: _allowUpgrade,
_params: FaultDisputeGameParams({ _params: FaultDisputeGameParams({
anchorStateRegistry: AnchorStateRegistry(mustGetAddress("AnchorStateRegistryProxy")),
weth: weth, weth: weth,
gameType: GameTypes.PERMISSIONED_CANNON, gameType: GameTypes.PERMISSIONED_CANNON,
absolutePrestate: loadMipsAbsolutePrestate(), absolutePrestate: loadMipsAbsolutePrestate(),
...@@ -1262,6 +1320,7 @@ contract Deploy is Deployer { ...@@ -1262,6 +1320,7 @@ contract Deploy is Deployer {
_factory: factory, _factory: factory,
_allowUpgrade: _allowUpgrade, _allowUpgrade: _allowUpgrade,
_params: FaultDisputeGameParams({ _params: FaultDisputeGameParams({
anchorStateRegistry: AnchorStateRegistry(mustGetAddress("AnchorStateRegistryProxy")),
weth: weth, weth: weth,
gameType: GameTypes.ALPHABET, gameType: GameTypes.ALPHABET,
absolutePrestate: outputAbsolutePrestate, absolutePrestate: outputAbsolutePrestate,
...@@ -1295,13 +1354,12 @@ contract Deploy is Deployer { ...@@ -1295,13 +1354,12 @@ contract Deploy is Deployer {
new FaultDisputeGame({ new FaultDisputeGame({
_gameType: _params.gameType, _gameType: _params.gameType,
_absolutePrestate: _params.absolutePrestate, _absolutePrestate: _params.absolutePrestate,
_genesisBlockNumber: cfg.faultGameGenesisBlock(),
_genesisOutputRoot: Hash.wrap(cfg.faultGameGenesisOutputRoot()),
_maxGameDepth: _params.maxGameDepth, _maxGameDepth: _params.maxGameDepth,
_splitDepth: cfg.faultGameSplitDepth(), _splitDepth: cfg.faultGameSplitDepth(),
_gameDuration: Duration.wrap(uint64(cfg.faultGameMaxDuration())), _gameDuration: Duration.wrap(uint64(cfg.faultGameMaxDuration())),
_vm: _params.faultVm, _vm: _params.faultVm,
_weth: _params.weth, _weth: _params.weth,
_anchorStateRegistry: _params.anchorStateRegistry,
_l2ChainId: cfg.l2ChainID() _l2ChainId: cfg.l2ChainID()
}) })
); );
...@@ -1311,13 +1369,12 @@ contract Deploy is Deployer { ...@@ -1311,13 +1369,12 @@ contract Deploy is Deployer {
new PermissionedDisputeGame({ new PermissionedDisputeGame({
_gameType: _params.gameType, _gameType: _params.gameType,
_absolutePrestate: _params.absolutePrestate, _absolutePrestate: _params.absolutePrestate,
_genesisBlockNumber: cfg.faultGameGenesisBlock(),
_genesisOutputRoot: Hash.wrap(cfg.faultGameGenesisOutputRoot()),
_maxGameDepth: _params.maxGameDepth, _maxGameDepth: _params.maxGameDepth,
_splitDepth: cfg.faultGameSplitDepth(), _splitDepth: cfg.faultGameSplitDepth(),
_gameDuration: Duration.wrap(uint64(cfg.faultGameMaxDuration())), _gameDuration: Duration.wrap(uint64(cfg.faultGameMaxDuration())),
_vm: _params.faultVm, _vm: _params.faultVm,
_weth: _params.weth, _weth: _params.weth,
_anchorStateRegistry: _params.anchorStateRegistry,
_l2ChainId: cfg.l2ChainID(), _l2ChainId: cfg.l2ChainID(),
_proposer: cfg.l2OutputOracleProposer(), _proposer: cfg.l2OutputOracleProposer(),
_challenger: cfg.l2OutputOracleChallenger() _challenger: cfg.l2OutputOracleChallenger()
......
...@@ -25,13 +25,7 @@ contract FaultDisputeGameViz is Script, FaultDisputeGame_Init { ...@@ -25,13 +25,7 @@ contract FaultDisputeGameViz is Script, FaultDisputeGame_Init {
function setUp() public override { function setUp() public override {
super.setUp(); super.setUp();
super.init({ super.init({ rootClaim: ROOT_CLAIM, absolutePrestate: ABSOLUTE_PRESTATE, l2BlockNumber: 0x10 });
rootClaim: ROOT_CLAIM,
absolutePrestate: ABSOLUTE_PRESTATE,
l2BlockNumber: 0x10,
genesisBlockNumber: 0,
genesisOutputRoot: Hash.wrap(bytes32(0))
});
} }
/** /**
......
...@@ -9,6 +9,7 @@ library Types { ...@@ -9,6 +9,7 @@ library Types {
address L2OutputOracle; address L2OutputOracle;
address DisputeGameFactory; address DisputeGameFactory;
address DelayedWETH; address DelayedWETH;
address AnchorStateRegistry;
address OptimismMintableERC20Factory; address OptimismMintableERC20Factory;
address OptimismPortal; address OptimismPortal;
address OptimismPortal2; address OptimismPortal2;
......
...@@ -3,7 +3,9 @@ pragma solidity 0.8.15; ...@@ -3,7 +3,9 @@ pragma solidity 0.8.15;
import { Proxy } from "src/universal/Proxy.sol"; import { Proxy } from "src/universal/Proxy.sol";
import { IDisputeGame } from "src/dispute/interfaces/IDisputeGame.sol"; import { IDisputeGame } from "src/dispute/interfaces/IDisputeGame.sol";
import { AnchorStateRegistry } from "src/dispute/AnchorStateRegistry.sol";
import { StdAssertions } from "forge-std/StdAssertions.sol"; import { StdAssertions } from "forge-std/StdAssertions.sol";
import "src/libraries/DisputeTypes.sol";
import "scripts/Deploy.s.sol"; import "scripts/Deploy.s.sol";
/// @notice Deploys the Fault Proof Alpha Chad contracts. /// @notice Deploys the Fault Proof Alpha Chad contracts.
...@@ -22,10 +24,12 @@ contract FPACOPS is Deploy, StdAssertions { ...@@ -22,10 +24,12 @@ contract FPACOPS is Deploy, StdAssertions {
// Deploy the proxies. // Deploy the proxies.
deployERC1967Proxy("DisputeGameFactoryProxy"); deployERC1967Proxy("DisputeGameFactoryProxy");
deployERC1967Proxy("DelayedWETHProxy"); deployERC1967Proxy("DelayedWETHProxy");
deployERC1967Proxy("AnchorStateRegistryProxy");
// Deploy implementations. // Deploy implementations.
deployDisputeGameFactory(); deployDisputeGameFactory();
deployDelayedWETH(); deployDelayedWETH();
deployAnchorStateRegistry();
deployPreimageOracle(); deployPreimageOracle();
deployMips(); deployMips();
...@@ -35,6 +39,7 @@ contract FPACOPS is Deploy, StdAssertions { ...@@ -35,6 +39,7 @@ contract FPACOPS is Deploy, StdAssertions {
// Initialize the proxies. // Initialize the proxies.
initializeDisputeGameFactoryProxy(); initializeDisputeGameFactoryProxy();
initializeDelayedWETHProxy(); initializeDelayedWETHProxy();
initializeAnchorStateRegistryProxy();
// Deploy the Cannon Fault game implementation and set it as game ID = 0. // Deploy the Cannon Fault game implementation and set it as game ID = 0.
setCannonFaultGameImplementation({ _allowUpgrade: false }); setCannonFaultGameImplementation({ _allowUpgrade: false });
...@@ -78,6 +83,31 @@ contract FPACOPS is Deploy, StdAssertions { ...@@ -78,6 +83,31 @@ contract FPACOPS is Deploy, StdAssertions {
); );
} }
function initializeAnchorStateRegistryProxy() internal broadcast {
console.log("Initializing AnchorStateRegistryProxy with AnchorStateRegistry.");
AnchorStateRegistry.StartingAnchorRoot[] memory roots = new AnchorStateRegistry.StartingAnchorRoot[](3);
roots[0] = AnchorStateRegistry.StartingAnchorRoot({
gameType: GameTypes.CANNON,
outputRoot: OutputRoot({
root: Hash.wrap(cfg.faultGameGenesisOutputRoot()),
l2BlockNumber: cfg.faultGameGenesisBlock()
})
});
roots[1] = AnchorStateRegistry.StartingAnchorRoot({
gameType: GameTypes.PERMISSIONED_CANNON,
outputRoot: OutputRoot({
root: Hash.wrap(cfg.faultGameGenesisOutputRoot()),
l2BlockNumber: cfg.faultGameGenesisBlock()
})
});
address asrProxy = mustGetAddress("AnchorStateRegistryProxy");
Proxy(payable(asrProxy)).upgradeToAndCall(
mustGetAddress("AnchorStateRegistry"), abi.encodeCall(AnchorStateRegistry.initialize, (roots))
);
}
/// @notice Transfers admin rights of the `DisputeGameFactoryProxy` to the `ProxyAdmin` and sets the /// @notice Transfers admin rights of the `DisputeGameFactoryProxy` to the `ProxyAdmin` and sets the
/// `DisputeGameFactory` owner to the `SystemOwnerSafe`. /// `DisputeGameFactory` owner to the `SystemOwnerSafe`.
function transferDGFOwnershipFinal(address _proxyAdmin, address _systemOwnerSafe) internal broadcast { function transferDGFOwnershipFinal(address _proxyAdmin, address _systemOwnerSafe) internal broadcast {
...@@ -137,8 +167,6 @@ contract FPACOPS is Deploy, StdAssertions { ...@@ -137,8 +167,6 @@ contract FPACOPS is Deploy, StdAssertions {
assertEq(gameImpl.splitDepth(), cfg.faultGameSplitDepth()); assertEq(gameImpl.splitDepth(), cfg.faultGameSplitDepth());
assertEq(gameImpl.gameDuration().raw(), cfg.faultGameMaxDuration()); assertEq(gameImpl.gameDuration().raw(), cfg.faultGameMaxDuration());
assertEq(gameImpl.absolutePrestate().raw(), bytes32(cfg.faultGameAbsolutePrestate())); assertEq(gameImpl.absolutePrestate().raw(), bytes32(cfg.faultGameAbsolutePrestate()));
assertEq(gameImpl.genesisBlockNumber(), cfg.faultGameGenesisBlock());
assertEq(gameImpl.genesisOutputRoot().raw(), cfg.faultGameGenesisOutputRoot());
// Check the security override yoke configuration. // Check the security override yoke configuration.
PermissionedDisputeGame soyGameImpl = PermissionedDisputeGame soyGameImpl =
...@@ -147,8 +175,15 @@ contract FPACOPS is Deploy, StdAssertions { ...@@ -147,8 +175,15 @@ contract FPACOPS is Deploy, StdAssertions {
assertEq(soyGameImpl.splitDepth(), cfg.faultGameSplitDepth()); assertEq(soyGameImpl.splitDepth(), cfg.faultGameSplitDepth());
assertEq(soyGameImpl.gameDuration().raw(), cfg.faultGameMaxDuration()); assertEq(soyGameImpl.gameDuration().raw(), cfg.faultGameMaxDuration());
assertEq(soyGameImpl.absolutePrestate().raw(), bytes32(cfg.faultGameAbsolutePrestate())); assertEq(soyGameImpl.absolutePrestate().raw(), bytes32(cfg.faultGameAbsolutePrestate()));
assertEq(soyGameImpl.genesisBlockNumber(), cfg.faultGameGenesisBlock());
assertEq(soyGameImpl.genesisOutputRoot().raw(), cfg.faultGameGenesisOutputRoot()); // Check the AnchorStateRegistry configuration.
AnchorStateRegistry asr = AnchorStateRegistry(mustGetAddress("AnchorStateRegistry"));
(Hash root1, uint256 l2BlockNumber1) = asr.anchors(GameTypes.CANNON);
(Hash root2, uint256 l2BlockNumber2) = asr.anchors(GameTypes.PERMISSIONED_CANNON);
assertEq(root1.raw(), cfg.faultGameGenesisOutputRoot());
assertEq(root2.raw(), cfg.faultGameGenesisOutputRoot());
assertEq(l2BlockNumber1, cfg.faultGameGenesisBlock());
assertEq(l2BlockNumber2, cfg.faultGameGenesisBlock());
} }
/// @notice Prints a review of the fault proof configuration section of the deploy config. /// @notice Prints a review of the fault proof configuration section of the deploy config.
......
...@@ -103,13 +103,17 @@ ...@@ -103,13 +103,17 @@
"initCodeHash": "0x9e0d25588d96934044c6f20b62b21444d734c85fe86b305efd8d226024e92725", "initCodeHash": "0x9e0d25588d96934044c6f20b62b21444d734c85fe86b305efd8d226024e92725",
"sourceCodeHash": "0xa2d7d5a1de4159a41ff99c2f05d33b9b472c2d00ea62f817b372988f56650153" "sourceCodeHash": "0xa2d7d5a1de4159a41ff99c2f05d33b9b472c2d00ea62f817b372988f56650153"
}, },
"src/dispute/AnchorStateRegistry.sol": {
"initCodeHash": "0x2f8c56069e43e306b0e40fba43109188b29328e83569560021a68aa5d9f2486b",
"sourceCodeHash": "0xb6e67d68b801b7a110207b0c33802ea94cf26f9a7f3ebba78123d930a2eb0b2a"
},
"src/dispute/DisputeGameFactory.sol": { "src/dispute/DisputeGameFactory.sol": {
"initCodeHash": "0x80d749a56c1776930fe0deb5c3c646217716e5875ace99c4d036af0452236476", "initCodeHash": "0x80d749a56c1776930fe0deb5c3c646217716e5875ace99c4d036af0452236476",
"sourceCodeHash": "0x1e5a6deded88804971fc1847c9eac65921771bff353437c0b29ed2f55513b984" "sourceCodeHash": "0x1e5a6deded88804971fc1847c9eac65921771bff353437c0b29ed2f55513b984"
}, },
"src/dispute/FaultDisputeGame.sol": { "src/dispute/FaultDisputeGame.sol": {
"initCodeHash": "0x9f8e47a4073639af201991a075c7eb03a73055750b3808281e7615b46d47a104", "initCodeHash": "0x9dcd4df1dd3e7a09dab46bfe1ebd9376f533cc533f9edce2f01aa754301e25aa",
"sourceCodeHash": "0xef3dee1a6dd89270b67c7e6a54862de9d11b9059cf053127bd26704e7e6b1fbf" "sourceCodeHash": "0xbe89df391f9cd4165389a7f6f65af752db13d0e508a1ec8430737aba5b7174dc"
}, },
"src/dispute/weth/DelayedWETH.sol": { "src/dispute/weth/DelayedWETH.sol": {
"initCodeHash": "0xf812f5a6de97162b48ccc847ff64c3be4ba575f1325d6e08fbf9dda91a7dc4c2", "initCodeHash": "0xf812f5a6de97162b48ccc847ff64c3be4ba575f1325d6e08fbf9dda91a7dc4c2",
......
[
{
"inputs": [
{
"internalType": "contract IDisputeGameFactory",
"name": "_disputeGameFactory",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [
{
"internalType": "GameType",
"name": "",
"type": "uint32"
}
],
"name": "anchors",
"outputs": [
{
"internalType": "Hash",
"name": "root",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "l2BlockNumber",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "disputeGameFactory",
"outputs": [
{
"internalType": "contract IDisputeGameFactory",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"components": [
{
"internalType": "GameType",
"name": "gameType",
"type": "uint32"
},
{
"components": [
{
"internalType": "Hash",
"name": "root",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "l2BlockNumber",
"type": "uint256"
}
],
"internalType": "struct OutputRoot",
"name": "outputRoot",
"type": "tuple"
}
],
"internalType": "struct AnchorStateRegistry.StartingAnchorRoot[]",
"name": "_startingAnchorRoots",
"type": "tuple[]"
}
],
"name": "initialize",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "tryUpdateAnchorState",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "version",
"outputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"stateMutability": "view",
"type": "function"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint8",
"name": "version",
"type": "uint8"
}
],
"name": "Initialized",
"type": "event"
}
]
\ No newline at end of file
...@@ -11,16 +11,6 @@ ...@@ -11,16 +11,6 @@
"name": "_absolutePrestate", "name": "_absolutePrestate",
"type": "bytes32" "type": "bytes32"
}, },
{
"internalType": "uint256",
"name": "_genesisBlockNumber",
"type": "uint256"
},
{
"internalType": "Hash",
"name": "_genesisOutputRoot",
"type": "bytes32"
},
{ {
"internalType": "uint256", "internalType": "uint256",
"name": "_maxGameDepth", "name": "_maxGameDepth",
...@@ -46,6 +36,11 @@ ...@@ -46,6 +36,11 @@
"name": "_weth", "name": "_weth",
"type": "address" "type": "address"
}, },
{
"internalType": "contract IAnchorStateRegistry",
"name": "_anchorStateRegistry",
"type": "address"
},
{ {
"internalType": "uint256", "internalType": "uint256",
"name": "_l2ChainId", "name": "_l2ChainId",
...@@ -317,32 +312,6 @@ ...@@ -317,32 +312,6 @@
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
}, },
{
"inputs": [],
"name": "genesisBlockNumber",
"outputs": [
{
"internalType": "uint256",
"name": "genesisBlockNumber_",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "genesisOutputRoot",
"outputs": [
{
"internalType": "Hash",
"name": "genesisOutputRoot_",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{ {
"inputs": [ "inputs": [
{ {
...@@ -509,6 +478,50 @@ ...@@ -509,6 +478,50 @@
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
}, },
{
"inputs": [],
"name": "startingBlockNumber",
"outputs": [
{
"internalType": "uint256",
"name": "startingBlockNumber_",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "startingOutputRoot",
"outputs": [
{
"internalType": "Hash",
"name": "root",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "l2BlockNumber",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "startingRootHash",
"outputs": [
{
"internalType": "Hash",
"name": "startingRootHash_",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{ {
"inputs": [], "inputs": [],
"name": "status", "name": "status",
...@@ -632,6 +645,11 @@ ...@@ -632,6 +645,11 @@
"name": "AlreadyInitialized", "name": "AlreadyInitialized",
"type": "error" "type": "error"
}, },
{
"inputs": [],
"name": "AnchorRootNotFound",
"type": "error"
},
{ {
"inputs": [], "inputs": [],
"name": "BondTransferFailed", "name": "BondTransferFailed",
......
...@@ -11,16 +11,6 @@ ...@@ -11,16 +11,6 @@
"name": "_absolutePrestate", "name": "_absolutePrestate",
"type": "bytes32" "type": "bytes32"
}, },
{
"internalType": "uint256",
"name": "_genesisBlockNumber",
"type": "uint256"
},
{
"internalType": "Hash",
"name": "_genesisOutputRoot",
"type": "bytes32"
},
{ {
"internalType": "uint256", "internalType": "uint256",
"name": "_maxGameDepth", "name": "_maxGameDepth",
...@@ -46,6 +36,11 @@ ...@@ -46,6 +36,11 @@
"name": "_weth", "name": "_weth",
"type": "address" "type": "address"
}, },
{
"internalType": "contract IAnchorStateRegistry",
"name": "_anchorStateRegistry",
"type": "address"
},
{ {
"internalType": "uint256", "internalType": "uint256",
"name": "_l2ChainId", "name": "_l2ChainId",
...@@ -327,32 +322,6 @@ ...@@ -327,32 +322,6 @@
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
}, },
{
"inputs": [],
"name": "genesisBlockNumber",
"outputs": [
{
"internalType": "uint256",
"name": "genesisBlockNumber_",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "genesisOutputRoot",
"outputs": [
{
"internalType": "Hash",
"name": "genesisOutputRoot_",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{ {
"inputs": [ "inputs": [
{ {
...@@ -519,6 +488,50 @@ ...@@ -519,6 +488,50 @@
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
}, },
{
"inputs": [],
"name": "startingBlockNumber",
"outputs": [
{
"internalType": "uint256",
"name": "startingBlockNumber_",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "startingOutputRoot",
"outputs": [
{
"internalType": "Hash",
"name": "root",
"type": "bytes32"
},
{
"internalType": "uint256",
"name": "l2BlockNumber",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "startingRootHash",
"outputs": [
{
"internalType": "Hash",
"name": "startingRootHash_",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{ {
"inputs": [], "inputs": [],
"name": "status", "name": "status",
...@@ -642,6 +655,11 @@ ...@@ -642,6 +655,11 @@
"name": "AlreadyInitialized", "name": "AlreadyInitialized",
"type": "error" "type": "error"
}, },
{
"inputs": [],
"name": "AnchorRootNotFound",
"type": "error"
},
{ {
"inputs": [], "inputs": [],
"name": "BadAuth", "name": "BadAuth",
......
[
{
"bytes": "1",
"label": "_initialized",
"offset": 0,
"slot": "0",
"type": "uint8"
},
{
"bytes": "1",
"label": "_initializing",
"offset": 1,
"slot": "0",
"type": "bool"
},
{
"bytes": "32",
"label": "anchors",
"offset": 0,
"slot": "1",
"type": "mapping(GameType => struct OutputRoot)"
}
]
\ No newline at end of file
...@@ -61,5 +61,12 @@ ...@@ -61,5 +61,12 @@
"offset": 1, "offset": 1,
"slot": "5", "slot": "5",
"type": "bool" "type": "bool"
},
{
"bytes": "64",
"label": "startingOutputRoot",
"offset": 0,
"slot": "6",
"type": "struct OutputRoot"
} }
] ]
\ No newline at end of file
...@@ -61,5 +61,12 @@ ...@@ -61,5 +61,12 @@
"offset": 1, "offset": 1,
"slot": "5", "slot": "5",
"type": "bool" "type": "bool"
},
{
"bytes": "64",
"label": "startingOutputRoot",
"offset": 0,
"slot": "6",
"type": "struct OutputRoot"
} }
] ]
\ No newline at end of file
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol";
import { ISemver } from "src/universal/ISemver.sol";
import { IAnchorStateRegistry } from "src/dispute/interfaces/IAnchorStateRegistry.sol";
import { IFaultDisputeGame } from "src/dispute/interfaces/IFaultDisputeGame.sol";
import { IDisputeGame } from "src/dispute/interfaces/IDisputeGame.sol";
import { IDisputeGameFactory } from "src/dispute/interfaces/IDisputeGameFactory.sol";
import "src/libraries/DisputeTypes.sol";
contract AnchorStateRegistry is Initializable, IAnchorStateRegistry, ISemver {
/// @notice Describes an initial anchor state for a game type.
struct StartingAnchorRoot {
GameType gameType;
OutputRoot outputRoot;
}
/// @notice Semantic version.
/// @custom:semver 0.1.0
string public constant version = "0.1.0";
/// @notice DisputeGameFactory address.
IDisputeGameFactory internal immutable DISPUTE_GAME_FACTORY;
/// @inheritdoc IAnchorStateRegistry
mapping(GameType => OutputRoot) public anchors;
/// @param _disputeGameFactory DisputeGameFactory address.
constructor(IDisputeGameFactory _disputeGameFactory) {
DISPUTE_GAME_FACTORY = _disputeGameFactory;
// Initialize the implementation with an empty array of starting anchor roots.
initialize(new StartingAnchorRoot[](0));
}
/// @notice Initializes the contract.
/// @param _startingAnchorRoots An array of starting anchor roots.
function initialize(StartingAnchorRoot[] memory _startingAnchorRoots) public initializer {
for (uint256 i = 0; i < _startingAnchorRoots.length; i++) {
StartingAnchorRoot memory startingAnchorRoot = _startingAnchorRoots[i];
anchors[startingAnchorRoot.gameType] = startingAnchorRoot.outputRoot;
}
}
/// @inheritdoc IAnchorStateRegistry
function disputeGameFactory() external view returns (IDisputeGameFactory) {
return DISPUTE_GAME_FACTORY;
}
/// @inheritdoc IAnchorStateRegistry
function tryUpdateAnchorState() external {
// Grab the game and game data.
IFaultDisputeGame game = IFaultDisputeGame(msg.sender);
(GameType gameType, Claim rootClaim, bytes memory extraData) = game.gameData();
// Grab the verified address of the game based on the game data.
// slither-disable-next-line unused-return
(IDisputeGame factoryRegisteredGame,) =
DISPUTE_GAME_FACTORY.games({ _gameType: gameType, _rootClaim: rootClaim, _extraData: extraData });
// Must be a valid game.
require(
address(factoryRegisteredGame) == address(game),
"AnchorStateRegistry: fault dispute game not registered with factory"
);
// No need to update anything if the anchor state is already newer.
if (game.l2BlockNumber() <= anchors[gameType].l2BlockNumber) {
return;
}
// Must be a game that resolved in favor of the state.
if (game.status() != GameStatus.DEFENDER_WINS) {
return;
}
// Actually update the anchor state.
anchors[gameType] = OutputRoot({ l2BlockNumber: game.l2BlockNumber(), root: Hash.wrap(game.rootClaim().raw()) });
}
}
...@@ -8,6 +8,7 @@ import { IDisputeGame } from "src/dispute/interfaces/IDisputeGame.sol"; ...@@ -8,6 +8,7 @@ import { IDisputeGame } from "src/dispute/interfaces/IDisputeGame.sol";
import { IFaultDisputeGame } from "src/dispute/interfaces/IFaultDisputeGame.sol"; import { IFaultDisputeGame } from "src/dispute/interfaces/IFaultDisputeGame.sol";
import { IInitializable } from "src/dispute/interfaces/IInitializable.sol"; import { IInitializable } from "src/dispute/interfaces/IInitializable.sol";
import { IBigStepper, IPreimageOracle } from "src/dispute/interfaces/IBigStepper.sol"; import { IBigStepper, IPreimageOracle } from "src/dispute/interfaces/IBigStepper.sol";
import { IAnchorStateRegistry } from "src/dispute/interfaces/IAnchorStateRegistry.sol";
import { Clone } from "src/libraries/Clone.sol"; import { Clone } from "src/libraries/Clone.sol";
import { Types } from "src/libraries/Types.sol"; import { Types } from "src/libraries/Types.sol";
...@@ -41,18 +42,15 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver { ...@@ -41,18 +42,15 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {
/// @notice An onchain VM that performs single instruction steps on a fault proof program trace. /// @notice An onchain VM that performs single instruction steps on a fault proof program trace.
IBigStepper internal immutable VM; IBigStepper internal immutable VM;
/// @notice The genesis block number.
uint256 internal immutable GENESIS_BLOCK_NUMBER;
/// @notice The genesis output root.
Hash internal immutable GENESIS_OUTPUT_ROOT;
/// @notice The game type ID. /// @notice The game type ID.
GameType internal immutable GAME_TYPE; GameType internal immutable GAME_TYPE;
/// @notice WETH contract for holding ETH. /// @notice WETH contract for holding ETH.
IDelayedWETH internal immutable WETH; IDelayedWETH internal immutable WETH;
/// @notice The anchor state registry.
IAnchorStateRegistry internal immutable ANCHOR_STATE_REGISTRY;
/// @notice The chain ID of the L2 network this contract argues about. /// @notice The chain ID of the L2 network this contract argues about.
uint256 internal immutable L2_CHAIN_ID; uint256 internal immutable L2_CHAIN_ID;
...@@ -89,30 +87,31 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver { ...@@ -89,30 +87,31 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {
/// @notice Flag for the `initialize` function to prevent re-initialization. /// @notice Flag for the `initialize` function to prevent re-initialization.
bool internal initialized; bool internal initialized;
/// @notice The latest finalized output root, serving as the anchor for output bisection.
OutputRoot public startingOutputRoot;
/// @notice Semantic version. /// @notice Semantic version.
/// @custom:semver 0.7.1 /// @custom:semver 0.8.1
string public constant version = "0.7.1"; string public constant version = "0.8.1";
/// @param _gameType The type ID of the game. /// @param _gameType The type ID of the game.
/// @param _absolutePrestate The absolute prestate of the instruction trace. /// @param _absolutePrestate The absolute prestate of the instruction trace.
/// @param _genesisBlockNumber The block number of the genesis block.
/// @param _genesisOutputRoot The output root of the genesis block.
/// @param _maxGameDepth The maximum depth of bisection. /// @param _maxGameDepth The maximum depth of bisection.
/// @param _splitDepth The final depth of the output bisection portion of the game. /// @param _splitDepth The final depth of the output bisection portion of the game.
/// @param _gameDuration The duration of the game. /// @param _gameDuration The duration of the game.
/// @param _vm An onchain VM that performs single instruction steps on an FPP trace. /// @param _vm An onchain VM that performs single instruction steps on an FPP trace.
/// @param _weth WETH contract for holding ETH. /// @param _weth WETH contract for holding ETH.
/// @param _anchorStateRegistry The contract that stores the anchor state for each game type.
/// @param _l2ChainId Chain ID of the L2 network this contract argues about. /// @param _l2ChainId Chain ID of the L2 network this contract argues about.
constructor( constructor(
GameType _gameType, GameType _gameType,
Claim _absolutePrestate, Claim _absolutePrestate,
uint256 _genesisBlockNumber,
Hash _genesisOutputRoot,
uint256 _maxGameDepth, uint256 _maxGameDepth,
uint256 _splitDepth, uint256 _splitDepth,
Duration _gameDuration, Duration _gameDuration,
IBigStepper _vm, IBigStepper _vm,
IDelayedWETH _weth, IDelayedWETH _weth,
IAnchorStateRegistry _anchorStateRegistry,
uint256 _l2ChainId uint256 _l2ChainId
) { ) {
// The split depth cannot be greater than or equal to the max game depth. // The split depth cannot be greater than or equal to the max game depth.
...@@ -120,13 +119,12 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver { ...@@ -120,13 +119,12 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {
GAME_TYPE = _gameType; GAME_TYPE = _gameType;
ABSOLUTE_PRESTATE = _absolutePrestate; ABSOLUTE_PRESTATE = _absolutePrestate;
GENESIS_BLOCK_NUMBER = _genesisBlockNumber;
GENESIS_OUTPUT_ROOT = _genesisOutputRoot;
MAX_GAME_DEPTH = _maxGameDepth; MAX_GAME_DEPTH = _maxGameDepth;
SPLIT_DEPTH = _splitDepth; SPLIT_DEPTH = _splitDepth;
GAME_DURATION = _gameDuration; GAME_DURATION = _gameDuration;
VM = _vm; VM = _vm;
WETH = _weth; WETH = _weth;
ANCHOR_STATE_REGISTRY = _anchorStateRegistry;
L2_CHAIN_ID = _l2ChainId; L2_CHAIN_ID = _l2ChainId;
} }
...@@ -352,8 +350,9 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver { ...@@ -352,8 +350,9 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {
// Load the disputed proposal's L2 block number as a big-endian uint64 in the // Load the disputed proposal's L2 block number as a big-endian uint64 in the
// high order 8 bytes of the word. // high order 8 bytes of the word.
// We add the index at depth + 1 to the genesis block number to get the disputed L2 block number. // We add the index at depth + 1 to the starting block number to get the disputed L2
uint256 l2Number = GENESIS_BLOCK_NUMBER + disputedPos.traceIndex(SPLIT_DEPTH) + 1; // block number.
uint256 l2Number = startingOutputRoot.l2BlockNumber + disputedPos.traceIndex(SPLIT_DEPTH) + 1;
oracle.loadLocalData(_ident, uuid.raw(), bytes32(l2Number << 0xC0), 8, _partOffset); oracle.loadLocalData(_ident, uuid.raw(), bytes32(l2Number << 0xC0), 8, _partOffset);
} else if (_ident == LocalPreimageKey.CHAIN_ID) { } else if (_ident == LocalPreimageKey.CHAIN_ID) {
...@@ -395,7 +394,11 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver { ...@@ -395,7 +394,11 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {
status_ = claimData[0].counteredBy == address(0) ? GameStatus.DEFENDER_WINS : GameStatus.CHALLENGER_WINS; status_ = claimData[0].counteredBy == address(0) ? GameStatus.DEFENDER_WINS : GameStatus.CHALLENGER_WINS;
resolvedAt = Timestamp.wrap(uint64(block.timestamp)); resolvedAt = Timestamp.wrap(uint64(block.timestamp));
// Update the status and emit the resolved event, note that we're performing an assignment here.
emit Resolved(status = status_); emit Resolved(status = status_);
// Try to update the anchor state, this should not revert.
ANCHOR_STATE_REGISTRY.tryUpdateAnchorState();
} }
/// @inheritdoc IFaultDisputeGame /// @inheritdoc IFaultDisputeGame
...@@ -492,6 +495,16 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver { ...@@ -492,6 +495,16 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {
extraData_ = extraData(); extraData_ = extraData();
} }
/// @inheritdoc IFaultDisputeGame
function startingBlockNumber() external view returns (uint256 startingBlockNumber_) {
startingBlockNumber_ = startingOutputRoot.l2BlockNumber;
}
/// @inheritdoc IFaultDisputeGame
function startingRootHash() external view returns (Hash startingRootHash_) {
startingRootHash_ = startingOutputRoot.root;
}
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
// MISC EXTERNAL // // MISC EXTERNAL //
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
...@@ -507,14 +520,23 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver { ...@@ -507,14 +520,23 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {
// //
// Explicit checks: // Explicit checks:
// - The game must not have already been initialized. // - The game must not have already been initialized.
// - An output root cannot be proposed at or before the genesis block. // - An output root cannot be proposed at or before the starting block number.
// INVARIANT: The game must not have already been initialized. // INVARIANT: The game must not have already been initialized.
if (initialized) revert AlreadyInitialized(); if (initialized) revert AlreadyInitialized();
// Grab the latest anchor root.
(Hash root, uint256 rootBlockNumber) = ANCHOR_STATE_REGISTRY.anchors(GAME_TYPE);
// Should only happen if this is a new game type that hasn't been set up yet.
if (root.raw() == bytes32(0)) revert AnchorRootNotFound();
// Set the starting output root.
startingOutputRoot = OutputRoot({ l2BlockNumber: rootBlockNumber, root: root });
// Do not allow the game to be initialized if the root claim corresponds to a block at or before the // Do not allow the game to be initialized if the root claim corresponds to a block at or before the
// configured genesis block number. // configured starting block number.
if (l2BlockNumber() <= GENESIS_BLOCK_NUMBER) revert UnexpectedRootClaim(rootClaim()); if (l2BlockNumber() <= rootBlockNumber) revert UnexpectedRootClaim(rootClaim());
// Revert if the calldata size is too large, which signals that the `extraData` contains more than expected. // Revert if the calldata size is too large, which signals that the `extraData` contains more than expected.
// This is to prevent adding extra bytes to the `extraData` that result in a different game UUID in the factory, // This is to prevent adding extra bytes to the `extraData` that result in a different game UUID in the factory,
...@@ -658,16 +680,6 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver { ...@@ -658,16 +680,6 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {
vm_ = VM; vm_ = VM;
} }
/// @notice Returns the genesis block number.
function genesisBlockNumber() external view returns (uint256 genesisBlockNumber_) {
genesisBlockNumber_ = GENESIS_BLOCK_NUMBER;
}
/// @notice Returns the genesis output root.
function genesisOutputRoot() external view returns (Hash genesisOutputRoot_) {
genesisOutputRoot_ = GENESIS_OUTPUT_ROOT;
}
/// @notice Returns the WETH contract for holding ETH. /// @notice Returns the WETH contract for holding ETH.
function weth() external view returns (IDelayedWETH weth_) { function weth() external view returns (IDelayedWETH weth_) {
weth_ = WETH; weth_ = WETH;
...@@ -813,14 +825,14 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver { ...@@ -813,14 +825,14 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, ISemver {
// 2. If it was a defense, the starting output root is `claim`, and the disputed output root is // 2. If it was a defense, the starting output root is `claim`, and the disputed output root is
// elsewhere in the DAG (it must commit to the block # index at depth of `outputPos + 1`). // elsewhere in the DAG (it must commit to the block # index at depth of `outputPos + 1`).
if (wasAttack) { if (wasAttack) {
// If this is an attack on the first output root (the block directly after genesis), the // If this is an attack on the first output root (the block directly after the starting
// starting claim nor position exists in the tree. We leave these as 0, which can be easily // block number), the starting claim nor position exists in the tree. We leave these as
// identified due to 0 being an invalid Gindex. // 0, which can be easily identified due to 0 being an invalid Gindex.
if (outputPos.indexAtDepth() > 0) { if (outputPos.indexAtDepth() > 0) {
ClaimData storage starting = _findTraceAncestor(Position.wrap(outputPos.raw() - 1), claimIdx, true); ClaimData storage starting = _findTraceAncestor(Position.wrap(outputPos.raw() - 1), claimIdx, true);
(startingClaim_, startingPos_) = (starting.claim, starting.position); (startingClaim_, startingPos_) = (starting.claim, starting.position);
} else { } else {
startingClaim_ = Claim.wrap(GENESIS_OUTPUT_ROOT.raw()); startingClaim_ = Claim.wrap(startingOutputRoot.root.raw());
} }
(disputedClaim_, disputedPos_) = (claim.claim, claim.position); (disputedClaim_, disputedPos_) = (claim.claim, claim.position);
} else { } else {
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
pragma solidity 0.8.15; pragma solidity 0.8.15;
import { IDelayedWETH } from "src/dispute/interfaces/IDelayedWETH.sol"; import { IDelayedWETH } from "src/dispute/interfaces/IDelayedWETH.sol";
import { IAnchorStateRegistry } from "src/dispute/interfaces/IAnchorStateRegistry.sol";
import { FaultDisputeGame, IFaultDisputeGame, IBigStepper, IInitializable } from "src/dispute/FaultDisputeGame.sol"; import { FaultDisputeGame, IFaultDisputeGame, IBigStepper, IInitializable } from "src/dispute/FaultDisputeGame.sol";
import "src/libraries/DisputeTypes.sol"; import "src/libraries/DisputeTypes.sol";
import "src/libraries/DisputeErrors.sol"; import "src/libraries/DisputeErrors.sol";
...@@ -29,26 +30,24 @@ contract PermissionedDisputeGame is FaultDisputeGame { ...@@ -29,26 +30,24 @@ contract PermissionedDisputeGame is FaultDisputeGame {
/// @param _gameType The type ID of the game. /// @param _gameType The type ID of the game.
/// @param _absolutePrestate The absolute prestate of the instruction trace. /// @param _absolutePrestate The absolute prestate of the instruction trace.
/// @param _genesisBlockNumber The block number of the genesis block.
/// @param _genesisOutputRoot The output root of the genesis block.
/// @param _maxGameDepth The maximum depth of bisection. /// @param _maxGameDepth The maximum depth of bisection.
/// @param _splitDepth The final depth of the output bisection portion of the game. /// @param _splitDepth The final depth of the output bisection portion of the game.
/// @param _gameDuration The duration of the game. /// @param _gameDuration The duration of the game.
/// @param _vm An onchain VM that performs single instruction steps on an FPP trace. /// @param _vm An onchain VM that performs single instruction steps on an FPP trace.
/// @param _weth WETH contract for holding ETH. /// @param _weth WETH contract for holding ETH.
/// @param _anchorStateRegistry The contract that stores the anchor state for each game type.
/// @param _l2ChainId Chain ID of the L2 network this contract argues about. /// @param _l2ChainId Chain ID of the L2 network this contract argues about.
/// @param _proposer Address that is allowed to create instances of this contract. /// @param _proposer Address that is allowed to create instances of this contract.
/// @param _challenger Address that is allowed to challenge instances of this contract. /// @param _challenger Address that is allowed to challenge instances of this contract.
constructor( constructor(
GameType _gameType, GameType _gameType,
Claim _absolutePrestate, Claim _absolutePrestate,
uint256 _genesisBlockNumber,
Hash _genesisOutputRoot,
uint256 _maxGameDepth, uint256 _maxGameDepth,
uint256 _splitDepth, uint256 _splitDepth,
Duration _gameDuration, Duration _gameDuration,
IBigStepper _vm, IBigStepper _vm,
IDelayedWETH _weth, IDelayedWETH _weth,
IAnchorStateRegistry _anchorStateRegistry,
uint256 _l2ChainId, uint256 _l2ChainId,
address _proposer, address _proposer,
address _challenger address _challenger
...@@ -56,13 +55,12 @@ contract PermissionedDisputeGame is FaultDisputeGame { ...@@ -56,13 +55,12 @@ contract PermissionedDisputeGame is FaultDisputeGame {
FaultDisputeGame( FaultDisputeGame(
_gameType, _gameType,
_absolutePrestate, _absolutePrestate,
_genesisBlockNumber,
_genesisOutputRoot,
_maxGameDepth, _maxGameDepth,
_splitDepth, _splitDepth,
_gameDuration, _gameDuration,
_vm, _vm,
_weth, _weth,
_anchorStateRegistry,
_l2ChainId _l2ChainId
) )
{ {
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IDisputeGameFactory } from "src/dispute/interfaces/IDisputeGameFactory.sol";
import "src/libraries/DisputeTypes.sol";
/// @title IAnchorStateRegistry
/// @notice Describes a contract that stores the anchor state for each game type.
interface IAnchorStateRegistry {
/// @notice Returns the anchor state for the given game type.
/// @param _gameType The game type to get the anchor state for.
/// @return The anchor state for the given game type.
function anchors(GameType _gameType) external view returns (Hash, uint256);
/// @notice Returns the DisputeGameFactory address.
/// @return DisputeGameFactory address.
function disputeGameFactory() external view returns (IDisputeGameFactory);
/// @notice Callable by FaultDisputeGame contracts to update the anchor state. Pulls the anchor
/// state directly from the FaultDisputeGame contract and stores it in the registry if
/// the new anchor state is valid and the state is newer than the current anchor state.
function tryUpdateAnchorState() external;
}
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.8.15; pragma solidity ^0.8.0;
import { IPreimageOracle } from "src/cannon/interfaces/IPreimageOracle.sol"; import { IPreimageOracle } from "src/cannon/interfaces/IPreimageOracle.sol";
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.8.15; pragma solidity ^0.8.0;
import { IWETH } from "src/dispute/interfaces/IWETH.sol"; import { IWETH } from "src/dispute/interfaces/IWETH.sol";
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.8.15; pragma solidity ^0.8.0;
import { IInitializable } from "src/dispute/interfaces/IInitializable.sol"; import { IInitializable } from "src/dispute/interfaces/IInitializable.sol";
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.8.15; pragma solidity ^0.8.0;
import { IDisputeGame } from "./IDisputeGame.sol"; import { IDisputeGame } from "./IDisputeGame.sol";
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.8.15; pragma solidity ^0.8.0;
import { IDisputeGame } from "./IDisputeGame.sol"; import { IDisputeGame } from "./IDisputeGame.sol";
...@@ -67,4 +67,13 @@ interface IFaultDisputeGame is IDisputeGame { ...@@ -67,4 +67,13 @@ interface IFaultDisputeGame is IDisputeGame {
/// @notice The l2BlockNumber of the disputed output root in the `L2OutputOracle`. /// @notice The l2BlockNumber of the disputed output root in the `L2OutputOracle`.
function l2BlockNumber() external view returns (uint256 l2BlockNumber_); function l2BlockNumber() external view returns (uint256 l2BlockNumber_);
/// @notice Starting output root and block number of the game.
function startingOutputRoot() external view returns (Hash startingRoot_, uint256 l2BlockNumber_);
/// @notice Only the starting block number of the game.
function startingBlockNumber() external view returns (uint256 startingBlockNumber_);
/// @notice Only the starting output root of the game.
function startingRootHash() external view returns (Hash startingRootHash_);
} }
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.8.15; pragma solidity ^0.8.0;
/// @title IInitializable /// @title IInitializable
/// @notice An interface for initializable contracts. /// @notice An interface for initializable contracts.
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity ^0.8.15; pragma solidity ^0.8.0;
/// @title IWETH /// @title IWETH
/// @notice Interface for WETH9. /// @notice Interface for WETH9.
......
...@@ -95,6 +95,9 @@ error InvalidSplitDepth(); ...@@ -95,6 +95,9 @@ error InvalidSplitDepth();
/// an instruction step. /// an instruction step.
error DuplicateStep(); error DuplicateStep();
/// @notice Thrown when an anchor root is not found for a given game type.
error AnchorRootNotFound();
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
// `PermissionedDisputeGame` Errors // // `PermissionedDisputeGame` Errors //
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
......
...@@ -89,6 +89,14 @@ enum GameStatus { ...@@ -89,6 +89,14 @@ enum GameStatus {
DEFENDER_WINS DEFENDER_WINS
} }
/// @notice Represents an L2 output root and the L2 block number at which it was generated.
/// @custom:field root The output root.
/// @custom:field l2BlockNumber The L2 block number at which the output root was generated.
struct OutputRoot {
Hash root;
uint256 l2BlockNumber;
}
/// @title GameTypes /// @title GameTypes
/// @notice A library that defines the IDs of games that can be played. /// @notice A library that defines the IDs of games that can be played.
library GameTypes { library GameTypes {
......
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.15;
import "src/libraries/DisputeTypes.sol";
import "src/libraries/DisputeErrors.sol";
import { Test } from "forge-std/Test.sol";
import { FaultDisputeGame_Init, _changeClaimStatus } from "test/dispute/FaultDisputeGame.t.sol";
contract AnchorStateRegistry_Init is FaultDisputeGame_Init {
function setUp() public virtual override {
// Duplicating the initialization/setup logic of FaultDisputeGame_Test.
// See that test for more information, actual values here not really important.
Claim rootClaim = Claim.wrap(bytes32((uint256(1) << 248) | uint256(10)));
bytes memory absolutePrestateData = abi.encode(0);
Claim absolutePrestate = _changeClaimStatus(Claim.wrap(keccak256(absolutePrestateData)), VMStatuses.UNFINISHED);
super.setUp();
super.init({ rootClaim: rootClaim, absolutePrestate: absolutePrestate, l2BlockNumber: 0x10 });
}
}
contract AnchorStateRegistry_Initialize_Test is AnchorStateRegistry_Init {
/// @dev Tests that initialization is successful.
function test_initialize_succeeds() public {
(Hash cannonRoot, uint256 cannonL2BlockNumber) = anchorStateRegistry.anchors(GameTypes.CANNON);
(Hash permissionedCannonRoot, uint256 permissionedCannonL2BlockNumber) =
anchorStateRegistry.anchors(GameTypes.PERMISSIONED_CANNON);
(Hash alphabetRoot, uint256 alphabetL2BlockNumber) = anchorStateRegistry.anchors(GameTypes.ALPHABET);
assertEq(cannonRoot.raw(), 0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF);
assertEq(cannonL2BlockNumber, 0);
assertEq(permissionedCannonRoot.raw(), 0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF);
assertEq(permissionedCannonL2BlockNumber, 0);
assertEq(alphabetRoot.raw(), 0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF);
assertEq(alphabetL2BlockNumber, 0);
}
}
contract AnchorStateRegistry_TryUpdateAnchorState_Test is AnchorStateRegistry_Init {
/// @dev Tests that updating the anchor state succeeds when the game state is valid and newer.
function test_tryUpdateAnchorState_validNewerState_succeeds() public {
// Confirm that the anchor state is older than the game state.
(Hash root, uint256 l2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType());
assert(l2BlockNumber < gameProxy.l2BlockNumber());
// Mock the state that we want.
vm.mockCall(
address(gameProxy), abi.encodeWithSelector(gameProxy.status.selector), abi.encode(GameStatus.DEFENDER_WINS)
);
// Try to update the anchor state.
vm.prank(address(gameProxy));
anchorStateRegistry.tryUpdateAnchorState();
// Confirm that the anchor state is now the same as the game state.
(root, l2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType());
assertEq(l2BlockNumber, gameProxy.l2BlockNumber());
assertEq(root.raw(), gameProxy.rootClaim().raw());
}
/// @dev Tests that updating the anchor state fails when the game state is valid but older.
function test_tryUpdateAnchorState_validOlderState_fails() public {
// Confirm that the anchor state is newer than the game state.
vm.mockCall(address(gameProxy), abi.encodeWithSelector(gameProxy.l2BlockNumber.selector), abi.encode(0));
(Hash root, uint256 l2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType());
assert(l2BlockNumber >= gameProxy.l2BlockNumber());
// Mock the state that we want.
vm.mockCall(address(gameProxy), abi.encodeWithSelector(gameProxy.l2BlockNumber.selector), abi.encode(0));
vm.mockCall(
address(gameProxy), abi.encodeWithSelector(gameProxy.status.selector), abi.encode(GameStatus.DEFENDER_WINS)
);
// Try to update the anchor state.
vm.prank(address(gameProxy));
anchorStateRegistry.tryUpdateAnchorState();
// Confirm that the anchor state has not updated.
(Hash updatedRoot, uint256 updatedL2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType());
assertEq(updatedL2BlockNumber, l2BlockNumber);
assertEq(updatedRoot.raw(), root.raw());
}
/// @dev Tests that updating the anchor state fails when the game state is invalid.
function test_tryUpdateAnchorState_invalidNewerState_fails() public {
// Confirm that the anchor state is older than the game state.
(Hash root, uint256 l2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType());
assert(l2BlockNumber < gameProxy.l2BlockNumber());
// Mock the state that we want.
vm.mockCall(
address(gameProxy),
abi.encodeWithSelector(gameProxy.status.selector),
abi.encode(GameStatus.CHALLENGER_WINS)
);
// Try to update the anchor state.
vm.prank(address(gameProxy));
anchorStateRegistry.tryUpdateAnchorState();
// Confirm that the anchor state has not updated.
(Hash updatedRoot, uint256 updatedL2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType());
assertEq(updatedL2BlockNumber, l2BlockNumber);
assertEq(updatedRoot.raw(), root.raw());
}
/// @dev Tests that updating the anchor state fails when the game is not registered with the factory.
function test_tryUpdateAnchorState_invalidGame_fails() public {
// Confirm that the anchor state is older than the game state.
(Hash root, uint256 l2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType());
assert(l2BlockNumber < gameProxy.l2BlockNumber());
// Mock the state that we want.
vm.mockCall(
address(disputeGameFactory),
abi.encodeWithSelector(
disputeGameFactory.games.selector, gameProxy.gameType(), gameProxy.rootClaim(), gameProxy.extraData()
),
abi.encode(address(0), 0)
);
// Try to update the anchor state.
vm.prank(address(gameProxy));
vm.expectRevert("AnchorStateRegistry: fault dispute game not registered with factory");
anchorStateRegistry.tryUpdateAnchorState();
// Confirm that the anchor state has not updated.
(Hash updatedRoot, uint256 updatedL2BlockNumber) = anchorStateRegistry.anchors(gameProxy.gameType());
assertEq(updatedL2BlockNumber, l2BlockNumber);
assertEq(updatedRoot.raw(), root.raw());
}
}
...@@ -39,15 +39,7 @@ contract PermissionedDisputeGame_Init is DisputeGameFactory_Init { ...@@ -39,15 +39,7 @@ contract PermissionedDisputeGame_Init is DisputeGameFactory_Init {
event Move(uint256 indexed parentIndex, Claim indexed pivot, address indexed claimant); event Move(uint256 indexed parentIndex, Claim indexed pivot, address indexed claimant);
function init( function init(Claim rootClaim, Claim absolutePrestate, uint256 l2BlockNumber) public {
Claim rootClaim,
Claim absolutePrestate,
uint256 l2BlockNumber,
uint256 genesisBlockNumber,
Hash genesisOutputRoot
)
public
{
// Set the time to a realistic date. // Set the time to a realistic date.
vm.warp(1690906994); vm.warp(1690906994);
...@@ -63,13 +55,12 @@ contract PermissionedDisputeGame_Init is DisputeGameFactory_Init { ...@@ -63,13 +55,12 @@ contract PermissionedDisputeGame_Init is DisputeGameFactory_Init {
gameImpl = new PermissionedDisputeGame({ gameImpl = new PermissionedDisputeGame({
_gameType: GAME_TYPE, _gameType: GAME_TYPE,
_absolutePrestate: absolutePrestate, _absolutePrestate: absolutePrestate,
_genesisBlockNumber: genesisBlockNumber,
_genesisOutputRoot: genesisOutputRoot,
_maxGameDepth: 2 ** 3, _maxGameDepth: 2 ** 3,
_splitDepth: 2 ** 2, _splitDepth: 2 ** 2,
_gameDuration: Duration.wrap(7 days), _gameDuration: Duration.wrap(7 days),
_vm: _vm, _vm: _vm,
_weth: _weth, _weth: _weth,
_anchorStateRegistry: anchorStateRegistry,
_l2ChainId: 10, _l2ChainId: 10,
_proposer: PROPOSER, _proposer: PROPOSER,
_challenger: CHALLENGER _challenger: CHALLENGER
...@@ -84,8 +75,6 @@ contract PermissionedDisputeGame_Init is DisputeGameFactory_Init { ...@@ -84,8 +75,6 @@ contract PermissionedDisputeGame_Init is DisputeGameFactory_Init {
// Check immutables // Check immutables
assertEq(gameProxy.gameType().raw(), GAME_TYPE.raw()); assertEq(gameProxy.gameType().raw(), GAME_TYPE.raw());
assertEq(gameProxy.absolutePrestate().raw(), absolutePrestate.raw()); assertEq(gameProxy.absolutePrestate().raw(), absolutePrestate.raw());
assertEq(gameProxy.genesisBlockNumber(), genesisBlockNumber);
assertEq(gameProxy.genesisOutputRoot().raw(), genesisOutputRoot.raw());
assertEq(gameProxy.maxGameDepth(), 2 ** 3); assertEq(gameProxy.maxGameDepth(), 2 ** 3);
assertEq(gameProxy.splitDepth(), 2 ** 2); assertEq(gameProxy.splitDepth(), 2 ** 2);
assertEq(gameProxy.gameDuration().raw(), 7 days); assertEq(gameProxy.gameDuration().raw(), 7 days);
...@@ -116,13 +105,7 @@ contract PermissionedDisputeGame_Test is PermissionedDisputeGame_Init { ...@@ -116,13 +105,7 @@ contract PermissionedDisputeGame_Test is PermissionedDisputeGame_Init {
absolutePrestate = _changeClaimStatus(Claim.wrap(keccak256(absolutePrestateData)), VMStatuses.UNFINISHED); absolutePrestate = _changeClaimStatus(Claim.wrap(keccak256(absolutePrestateData)), VMStatuses.UNFINISHED);
super.setUp(); super.setUp();
super.init({ super.init({ rootClaim: ROOT_CLAIM, absolutePrestate: absolutePrestate, l2BlockNumber: 0x10 });
rootClaim: ROOT_CLAIM,
absolutePrestate: absolutePrestate,
l2BlockNumber: 0x10,
genesisBlockNumber: 0,
genesisOutputRoot: Hash.wrap(bytes32(0))
});
} }
/// @dev Tests that the proposer can create a permissioned dispute game. /// @dev Tests that the proposer can create a permissioned dispute game.
......
...@@ -18,13 +18,7 @@ contract FaultDisputeGame_Solvency_Invariant is FaultDisputeGame_Init { ...@@ -18,13 +18,7 @@ contract FaultDisputeGame_Solvency_Invariant is FaultDisputeGame_Init {
function setUp() public override { function setUp() public override {
super.setUp(); super.setUp();
super.init({ super.init({ rootClaim: ROOT_CLAIM, absolutePrestate: ABSOLUTE_PRESTATE, l2BlockNumber: 0x10 });
rootClaim: ROOT_CLAIM,
absolutePrestate: ABSOLUTE_PRESTATE,
l2BlockNumber: 0x10,
genesisBlockNumber: 0,
genesisOutputRoot: Hash.wrap(bytes32(0))
});
actor = new RandomClaimActor(gameProxy, vm); actor = new RandomClaimActor(gameProxy, vm);
......
...@@ -20,6 +20,7 @@ import { OptimismPortal } from "src/L1/OptimismPortal.sol"; ...@@ -20,6 +20,7 @@ import { OptimismPortal } from "src/L1/OptimismPortal.sol";
import { OptimismPortal2 } from "src/L1/OptimismPortal2.sol"; import { OptimismPortal2 } from "src/L1/OptimismPortal2.sol";
import { DisputeGameFactory } from "src/dispute/DisputeGameFactory.sol"; import { DisputeGameFactory } from "src/dispute/DisputeGameFactory.sol";
import { DelayedWETH } from "src/dispute/weth/DelayedWETH.sol"; import { DelayedWETH } from "src/dispute/weth/DelayedWETH.sol";
import { AnchorStateRegistry } from "src/dispute/AnchorStateRegistry.sol";
import { L1CrossDomainMessenger } from "src/L1/L1CrossDomainMessenger.sol"; import { L1CrossDomainMessenger } from "src/L1/L1CrossDomainMessenger.sol";
import { DeployConfig } from "scripts/DeployConfig.s.sol"; import { DeployConfig } from "scripts/DeployConfig.s.sol";
import { Deploy } from "scripts/Deploy.s.sol"; import { Deploy } from "scripts/Deploy.s.sol";
...@@ -62,6 +63,7 @@ contract Setup { ...@@ -62,6 +63,7 @@ contract Setup {
ProtocolVersions protocolVersions; ProtocolVersions protocolVersions;
SuperchainConfig superchainConfig; SuperchainConfig superchainConfig;
DataAvailabilityChallenge dataAvailabilityChallenge; DataAvailabilityChallenge dataAvailabilityChallenge;
AnchorStateRegistry anchorStateRegistry;
L2CrossDomainMessenger l2CrossDomainMessenger = L2CrossDomainMessenger l2CrossDomainMessenger =
L2CrossDomainMessenger(payable(Predeploys.L2_CROSS_DOMAIN_MESSENGER)); L2CrossDomainMessenger(payable(Predeploys.L2_CROSS_DOMAIN_MESSENGER));
...@@ -114,6 +116,7 @@ contract Setup { ...@@ -114,6 +116,7 @@ contract Setup {
OptimismMintableERC20Factory(deploy.mustGetAddress("OptimismMintableERC20FactoryProxy")); OptimismMintableERC20Factory(deploy.mustGetAddress("OptimismMintableERC20FactoryProxy"));
protocolVersions = ProtocolVersions(deploy.mustGetAddress("ProtocolVersionsProxy")); protocolVersions = ProtocolVersions(deploy.mustGetAddress("ProtocolVersionsProxy"));
superchainConfig = SuperchainConfig(deploy.mustGetAddress("SuperchainConfigProxy")); superchainConfig = SuperchainConfig(deploy.mustGetAddress("SuperchainConfigProxy"));
anchorStateRegistry = AnchorStateRegistry(deploy.mustGetAddress("AnchorStateRegistryProxy"));
vm.label(address(l2OutputOracle), "L2OutputOracle"); vm.label(address(l2OutputOracle), "L2OutputOracle");
vm.label(deploy.mustGetAddress("L2OutputOracleProxy"), "L2OutputOracleProxy"); vm.label(deploy.mustGetAddress("L2OutputOracleProxy"), "L2OutputOracleProxy");
......
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