Skip to content

Commit

Permalink
feat: introduce AnchorStateRegistry
Browse files Browse the repository at this point in the history
Introduces the AnchorStateRegistry used to hold anchor states for
different game types. Anchor states are updated automatically when
a game resolves.
  • Loading branch information
smartcontracts committed Mar 18, 2024
1 parent dc2b714 commit ce7f4ca
Show file tree
Hide file tree
Showing 42 changed files with 1,413 additions and 796 deletions.
177 changes: 111 additions & 66 deletions op-bindings/bindings/faultdisputegame.go

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions op-bindings/bindings/faultdisputegame_more.go

Large diffs are not rendered by default.

54 changes: 27 additions & 27 deletions op-challenger/game/fault/contracts/faultdisputegame.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,29 +16,29 @@ import (
)

var (
methodGameDuration = "gameDuration"
methodMaxGameDepth = "maxGameDepth"
methodAbsolutePrestate = "absolutePrestate"
methodStatus = "status"
methodRootClaim = "rootClaim"
methodClaimCount = "claimDataLen"
methodClaim = "claimData"
methodL1Head = "l1Head"
methodResolve = "resolve"
methodResolveClaim = "resolveClaim"
methodAttack = "attack"
methodDefend = "defend"
methodStep = "step"
methodAddLocalData = "addLocalData"
methodVM = "vm"
methodGenesisBlockNumber = "genesisBlockNumber"
methodGenesisOutputRoot = "genesisOutputRoot"
methodSplitDepth = "splitDepth"
methodL2BlockNumber = "l2BlockNumber"
methodRequiredBond = "getRequiredBond"
methodClaimCredit = "claimCredit"
methodCredit = "credit"
methodWETH = "weth"
methodGameDuration = "gameDuration"
methodMaxGameDepth = "maxGameDepth"
methodAbsolutePrestate = "absolutePrestate"
methodStatus = "status"
methodRootClaim = "rootClaim"
methodClaimCount = "claimDataLen"
methodClaim = "claimData"
methodL1Head = "l1Head"
methodResolve = "resolve"
methodResolveClaim = "resolveClaim"
methodAttack = "attack"
methodDefend = "defend"
methodStep = "step"
methodAddLocalData = "addLocalData"
methodVM = "vm"
methodStartingBlockNumber = "startingBlockNumber"
methodStartingRootHash = "startingRootHash"
methodSplitDepth = "splitDepth"
methodL2BlockNumber = "l2BlockNumber"
methodRequiredBond = "getRequiredBond"
methodClaimCredit = "claimCredit"
methodCredit = "credit"
methodWETH = "weth"
)

type FaultDisputeGameContract struct {
Expand Down Expand Up @@ -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).
func (f *FaultDisputeGameContract) GetBlockRange(ctx context.Context) (prestateBlock uint64, poststateBlock uint64, retErr error) {
results, err := f.multiCaller.Call(ctx, rpcblock.Latest,
f.contract.Call(methodGenesisBlockNumber),
f.contract.Call(methodStartingBlockNumber),
f.contract.Call(methodL2BlockNumber))
if err != nil {
retErr = fmt.Errorf("failed to retrieve game block range: %w", err)
Expand Down Expand Up @@ -123,12 +123,12 @@ func (f *FaultDisputeGameContract) GetGameMetadata(ctx context.Context, block rp
return l1Head, l2BlockNumber, rootClaim, status, duration, nil
}

func (f *FaultDisputeGameContract) GetGenesisOutputRoot(ctx context.Context) (common.Hash, error) {
genesisOutputRoot, err := f.multiCaller.SingleCall(ctx, rpcblock.Latest, f.contract.Call(methodGenesisOutputRoot))
func (f *FaultDisputeGameContract) GetStartingRootHash(ctx context.Context) (common.Hash, error) {
startingRootHash, err := f.multiCaller.SingleCall(ctx, rpcblock.Latest, f.contract.Call(methodStartingRootHash))
if err != nil {
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) {
Expand Down
10 changes: 5 additions & 5 deletions op-challenger/game/fault/contracts/faultdisputegame_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ func TestGetBlockRange(t *testing.T) {
stubRpc, contract := setupFaultDisputeGameTest(t)
expectedStart := uint64(65)
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)})
start, end, err := contract.GetBlockRange(context.Background())
require.NoError(t, err)
Expand Down Expand Up @@ -354,13 +354,13 @@ func TestGetGameMetadata(t *testing.T) {
require.Equal(t, expectedGameDuration, duration)
}

func TestGetGenesisOutputRoot(t *testing.T) {
func TestGetStartingRootHash(t *testing.T) {
stubRpc, contract := setupFaultDisputeGameTest(t)
expectedOutputRoot := common.HexToHash("0x1234")
stubRpc.SetResponse(fdgAddr, methodGenesisOutputRoot, rpcblock.Latest, nil, []interface{}{expectedOutputRoot})
genesisOutputRoot, err := contract.GetGenesisOutputRoot(context.Background())
stubRpc.SetResponse(fdgAddr, methodStartingRootHash, rpcblock.Latest, nil, []interface{}{expectedOutputRoot})
startingOutputRoot, err := contract.GetStartingRootHash(context.Background())
require.NoError(t, err)
require.Equal(t, expectedOutputRoot, genesisOutputRoot)
require.Equal(t, expectedOutputRoot, startingOutputRoot)
}

func TestFaultDisputeGame_UpdateOracleTx(t *testing.T) {
Expand Down
8 changes: 4 additions & 4 deletions op-challenger/game/fault/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,8 @@ func registerAlphabet(
return accessor, nil
}
prestateValidator := NewPrestateValidator("alphabet", contract.GetAbsolutePrestateHash, alphabet.PrestateProvider)
genesisValidator := NewPrestateValidator("output root", contract.GetGenesisOutputRoot, prestateProvider)
return NewGamePlayer(ctx, cl, logger, m, dir, game.Proxy, txSender, contract, syncValidator, []Validator{prestateValidator, genesisValidator}, creator, l1HeaderSource, selective, claimants)
startingValidator := NewPrestateValidator("output root", contract.GetStartingRootHash, prestateProvider)
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)
if err != nil {
Expand Down Expand Up @@ -215,8 +215,8 @@ func registerCannon(
return accessor, nil
}
prestateValidator := NewPrestateValidator("cannon", contract.GetAbsolutePrestateHash, cannonPrestateProvider)
genesisValidator := NewPrestateValidator("output root", contract.GetGenesisOutputRoot, prestateProvider)
return NewGamePlayer(ctx, cl, logger, m, dir, game.Proxy, txSender, contract, syncValidator, []Validator{prestateValidator, genesisValidator}, creator, l1HeaderSource, selective, claimants)
startingValidator := NewPrestateValidator("output root", contract.GetStartingRootHash, prestateProvider)
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)
if err != nil {
Expand Down
8 changes: 4 additions & 4 deletions op-e2e/e2eutils/disputegame/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,8 @@ func (h *FactoryHelper) StartOutputCannonGame(ctx context.Context, l2Node string
h.require.NoError(err)

callOpts := &bind.CallOpts{Context: ctx}
prestateBlock, err := game.GenesisBlockNumber(callOpts)
h.require.NoError(err, "Failed to load genesis block number")
prestateBlock, err := game.StartingBlockNumber(callOpts)
h.require.NoError(err, "Failed to load starting block number")
poststateBlock, err := game.L2BlockNumber(callOpts)
h.require.NoError(err, "Failed to load l2 block number")
splitDepth, err := game.SplitDepth(callOpts)
Expand Down Expand Up @@ -251,8 +251,8 @@ func (h *FactoryHelper) StartOutputAlphabetGame(ctx context.Context, l2Node stri
h.require.NoError(err)

callOpts := &bind.CallOpts{Context: ctx}
prestateBlock, err := game.GenesisBlockNumber(callOpts)
h.require.NoError(err, "Failed to load genesis block number")
prestateBlock, err := game.StartingBlockNumber(callOpts)
h.require.NoError(err, "Failed to load starting block number")
poststateBlock, err := game.L2BlockNumber(callOpts)
h.require.NoError(err, "Failed to load l2 block number")
splitDepth, err := game.SplitDepth(callOpts)
Expand Down
6 changes: 3 additions & 3 deletions op-e2e/e2eutils/disputegame/output_game_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,9 @@ func (g *OutputGameHelper) L2BlockNum(ctx context.Context) uint64 {
return blockNum.Uint64()
}

func (g *OutputGameHelper) GenesisBlockNum(ctx context.Context) uint64 {
blockNum, err := g.game.GenesisBlockNumber(&bind.CallOpts{Context: ctx})
g.require.NoError(err, "failed to load genesis block number")
func (g *OutputGameHelper) StartingBlockNum(ctx context.Context) uint64 {
blockNum, err := g.game.StartingBlockNumber(&bind.CallOpts{Context: ctx})
g.require.NoError(err, "failed to load starting block number")
return blockNum.Uint64()
}

Expand Down
2 changes: 1 addition & 1 deletion packages/contracts-bedrock/.gas-snapshot
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ GasBenchMark_L1StandardBridge_Finalize:test_finalizeETHWithdrawal_benchmark() (g
GasBenchMark_L2OutputOracle:test_proposeL2Output_benchmark() (gas: 92930)
GasBenchMark_OptimismPortal:test_depositTransaction_benchmark() (gas: 68360)
GasBenchMark_OptimismPortal:test_depositTransaction_benchmark_1() (gas: 69013)
GasBenchMark_OptimismPortal:test_proveWithdrawalTransaction_benchmark() (gas: 155556)
GasBenchMark_OptimismPortal:test_proveWithdrawalTransaction_benchmark() (gas: 155551)
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
"faultGameMaxDepth": 50,
"faultGameMaxDuration": 2400,
"faultGameGenesisBlock": 0,
"faultGameGenesisOutputRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
"faultGameGenesisOutputRoot": "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF",
"faultGameSplitDepth": 14,
"faultGameWithdrawalDelay": 604800,
"preimageOracleMinProposalSize": 10000,
Expand Down
2 changes: 1 addition & 1 deletion packages/contracts-bedrock/deploy-config/hardhat.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
"faultGameMaxDepth": 8,
"faultGameMaxDuration": 2400,
"faultGameGenesisBlock": 0,
"faultGameGenesisOutputRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
"faultGameGenesisOutputRoot": "0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF",
"faultGameSplitDepth": 4,
"faultGameWithdrawalDelay": 604800,
"preimageOracleMinProposalSize": 10000,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# `FaultDisputeGame` Invariants

## 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.
65 changes: 61 additions & 4 deletions packages/contracts-bedrock/scripts/Deploy.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { DisputeGameFactory } from "src/dispute/DisputeGameFactory.sol";
import { FaultDisputeGame } from "src/dispute/FaultDisputeGame.sol";
import { PermissionedDisputeGame } from "src/dispute/PermissionedDisputeGame.sol";
import { DelayedWETH } from "src/dispute/weth/DelayedWETH.sol";
import { AnchorStateRegistry } from "src/dispute/AnchorStateRegistry.sol";
import { PreimageOracle } from "src/cannon/PreimageOracle.sol";
import { MIPS } from "src/cannon/MIPS.sol";
import { L1ERC721Bridge } from "src/L1/L1ERC721Bridge.sol";
Expand Down Expand Up @@ -69,6 +70,7 @@ contract Deploy is Deployer {
/// 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.
struct FaultDisputeGameParams {
AnchorStateRegistry anchorStateRegistry;
DelayedWETH weth;
GameType gameType;
Claim absolutePrestate;
Expand Down Expand Up @@ -144,6 +146,7 @@ contract Deploy is Deployer {
L2OutputOracle: mustGetAddress("L2OutputOracleProxy"),
DisputeGameFactory: mustGetAddress("DisputeGameFactoryProxy"),
DelayedWETH: mustGetAddress("DelayedWETHProxy"),
AnchorStateRegistry: mustGetAddress("AnchorStateRegistryProxy"),
OptimismMintableERC20Factory: mustGetAddress("OptimismMintableERC20FactoryProxy"),
OptimismPortal: mustGetAddress("OptimismPortalProxy"),
OptimismPortal2: mustGetAddress("OptimismPortalProxy"),
Expand All @@ -162,6 +165,7 @@ contract Deploy is Deployer {
L2OutputOracle: getAddress("L2OutputOracleProxy"),
DisputeGameFactory: getAddress("DisputeGameFactoryProxy"),
DelayedWETH: getAddress("DelayedWETHProxy"),
AnchorStateRegistry: getAddress("AnchorStateRegistryProxy"),
OptimismMintableERC20Factory: getAddress("OptimismMintableERC20FactoryProxy"),
OptimismPortal: getAddress("OptimismPortalProxy"),
OptimismPortal2: getAddress("OptimismPortalProxy"),
Expand Down Expand Up @@ -345,6 +349,7 @@ contract Deploy is Deployer {
deployERC1967Proxy("DisputeGameFactoryProxy");
deployERC1967Proxy("L2OutputOracleProxy");
deployERC1967Proxy("DelayedWETHProxy");
deployERC1967Proxy("AnchorStateRegistryProxy");

transferAddressManagerOwnership(); // to the ProxyAdmin
}
Expand All @@ -366,6 +371,7 @@ contract Deploy is Deployer {
deployDelayedWETH();
deployPreimageOracle();
deployMips();
deployAnchorStateRegistry();
}

/// @notice Initialize all of the implementations
Expand All @@ -379,6 +385,7 @@ contract Deploy is Deployer {
initializeL2OutputOracle();
initializeDisputeGameFactory();
initializeDelayedWETH();
initializeAnchorStateRegistry();

// 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
Expand Down Expand Up @@ -731,6 +738,17 @@ contract Deploy is Deployer {
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
function deploySystemConfig() public broadcast returns (address addr_) {
console.log("Deploying SystemConfig implementation");
Expand Down Expand Up @@ -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
function initializeSystemConfig() public broadcast {
console.log("Upgrading and initializing SystemConfig proxy");
Expand Down Expand Up @@ -1222,6 +1278,7 @@ contract Deploy is Deployer {
_factory: factory,
_allowUpgrade: _allowUpgrade,
_params: FaultDisputeGameParams({
anchorStateRegistry: AnchorStateRegistry(mustGetAddress("AnchorStateRegistryProxy")),
weth: weth,
gameType: GameTypes.CANNON,
absolutePrestate: loadMipsAbsolutePrestate(),
Expand All @@ -1242,6 +1299,7 @@ contract Deploy is Deployer {
_factory: factory,
_allowUpgrade: _allowUpgrade,
_params: FaultDisputeGameParams({
anchorStateRegistry: AnchorStateRegistry(mustGetAddress("AnchorStateRegistryProxy")),
weth: weth,
gameType: GameTypes.PERMISSIONED_CANNON,
absolutePrestate: loadMipsAbsolutePrestate(),
Expand All @@ -1262,6 +1320,7 @@ contract Deploy is Deployer {
_factory: factory,
_allowUpgrade: _allowUpgrade,
_params: FaultDisputeGameParams({
anchorStateRegistry: AnchorStateRegistry(mustGetAddress("AnchorStateRegistryProxy")),
weth: weth,
gameType: GameTypes.ALPHABET,
absolutePrestate: outputAbsolutePrestate,
Expand Down Expand Up @@ -1295,13 +1354,12 @@ contract Deploy is Deployer {
new FaultDisputeGame({
_gameType: _params.gameType,
_absolutePrestate: _params.absolutePrestate,
_genesisBlockNumber: cfg.faultGameGenesisBlock(),
_genesisOutputRoot: Hash.wrap(cfg.faultGameGenesisOutputRoot()),
_maxGameDepth: _params.maxGameDepth,
_splitDepth: cfg.faultGameSplitDepth(),
_gameDuration: Duration.wrap(uint64(cfg.faultGameMaxDuration())),
_vm: _params.faultVm,
_weth: _params.weth,
_anchorStateRegistry: _params.anchorStateRegistry,
_l2ChainId: cfg.l2ChainID()
})
);
Expand All @@ -1311,13 +1369,12 @@ contract Deploy is Deployer {
new PermissionedDisputeGame({
_gameType: _params.gameType,
_absolutePrestate: _params.absolutePrestate,
_genesisBlockNumber: cfg.faultGameGenesisBlock(),
_genesisOutputRoot: Hash.wrap(cfg.faultGameGenesisOutputRoot()),
_maxGameDepth: _params.maxGameDepth,
_splitDepth: cfg.faultGameSplitDepth(),
_gameDuration: Duration.wrap(uint64(cfg.faultGameMaxDuration())),
_vm: _params.faultVm,
_weth: _params.weth,
_anchorStateRegistry: _params.anchorStateRegistry,
_l2ChainId: cfg.l2ChainID(),
_proposer: cfg.l2OutputOracleProposer(),
_challenger: cfg.l2OutputOracleChallenger()
Expand Down
8 changes: 1 addition & 7 deletions packages/contracts-bedrock/scripts/FaultDisputeGameViz.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,7 @@ contract FaultDisputeGameViz is Script, FaultDisputeGame_Init {

function setUp() public override {
super.setUp();
super.init({
rootClaim: ROOT_CLAIM,
absolutePrestate: ABSOLUTE_PRESTATE,
l2BlockNumber: 0x10,
genesisBlockNumber: 0,
genesisOutputRoot: Hash.wrap(bytes32(0))
});
super.init({ rootClaim: ROOT_CLAIM, absolutePrestate: ABSOLUTE_PRESTATE, l2BlockNumber: 0x10 });
}

/**
Expand Down
1 change: 1 addition & 0 deletions packages/contracts-bedrock/scripts/Types.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ library Types {
address L2OutputOracle;
address DisputeGameFactory;
address DelayedWETH;
address AnchorStateRegistry;
address OptimismMintableERC20Factory;
address OptimismPortal;
address OptimismPortal2;
Expand Down
Loading

0 comments on commit ce7f4ca

Please sign in to comment.