Commit 581b7a17 authored by clabby's avatar clabby Committed by GitHub

Merge pull request #8351 from ethereum-optimism/cl/ctb/output-bisection-contract

feat(ctb): `OutputBisectionGame`
parents a413603a 833e3558
This diff is collapsed.
...@@ -214,6 +214,8 @@ type DeployConfig struct { ...@@ -214,6 +214,8 @@ type DeployConfig struct {
FaultGameMaxDuration uint64 `json:"faultGameMaxDuration"` FaultGameMaxDuration uint64 `json:"faultGameMaxDuration"`
// OutputBisectionGameGenesisBlock is the block number for genesis. // OutputBisectionGameGenesisBlock is the block number for genesis.
OutputBisectionGameGenesisBlock uint64 `json:"outputBisectionGameGenesisBlock"` OutputBisectionGameGenesisBlock uint64 `json:"outputBisectionGameGenesisBlock"`
// OutputBisectionGameGenesisOutputRoot is the output root for the genesis block.
OutputBisectionGameGenesisOutputRoot common.Hash `json:"outputBisectionGameGenesisOutputRoot"`
// OutputBisectionGameSplitDepth is the depth at which the output bisection game splits. // OutputBisectionGameSplitDepth is the depth at which the output bisection game splits.
OutputBisectionGameSplitDepth uint64 `json:"outputBisectionGameSplitDepth"` OutputBisectionGameSplitDepth uint64 `json:"outputBisectionGameSplitDepth"`
// FundDevAccounts configures whether or not to fund the dev accounts. Should only be used // FundDevAccounts configures whether or not to fund the dev accounts. Should only be used
......
...@@ -69,6 +69,7 @@ ...@@ -69,6 +69,7 @@
"faultGameMaxDepth": 63, "faultGameMaxDepth": 63,
"faultGameMaxDuration": 604800, "faultGameMaxDuration": 604800,
"outputBisectionGameGenesisBlock": 0, "outputBisectionGameGenesisBlock": 0,
"outputBisectionGameGenesisOutputRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
"outputBisectionGameSplitDepth": 0, "outputBisectionGameSplitDepth": 0,
"systemConfigStartBlock": 0, "systemConfigStartBlock": 0,
"requiredProtocolVersion": "0x0000000000000000000000000000000000000000000000000000000000000000", "requiredProtocolVersion": "0x0000000000000000000000000000000000000000000000000000000000000000",
......
...@@ -121,7 +121,11 @@ func registerOutputCannon( ...@@ -121,7 +121,11 @@ func registerOutputCannon(
if err != nil { if err != nil {
return nil, err return nil, err
} }
accessor, err := outputs.NewOutputCannonTraceAccessor(ctx, logger, m, cfg, l2Client, contract, dir, gameDepth, agreed, disputed) splitDepth, err := contract.GetSplitDepth(ctx)
if err != nil {
return nil, fmt.Errorf("failed to load split depth: %w", err)
}
accessor, err := outputs.NewOutputCannonTraceAccessor(ctx, logger, m, cfg, l2Client, contract, dir, gameDepth, splitDepth, agreed, disputed)
if err != nil { if err != nil {
return nil, err return nil, err
} }
......
...@@ -25,13 +25,12 @@ func NewOutputCannonTraceAccessor( ...@@ -25,13 +25,12 @@ func NewOutputCannonTraceAccessor(
contract cannon.L1HeadSource, contract cannon.L1HeadSource,
dir string, dir string,
gameDepth uint64, gameDepth uint64,
splitDepth uint64,
prestateBlock uint64, prestateBlock uint64,
poststateBlock uint64, poststateBlock uint64,
) (*trace.Accessor, error) { ) (*trace.Accessor, error) {
// TODO(client-pod#43): Load depths from the contract bottomDepth := gameDepth - splitDepth
topDepth := gameDepth / 2 outputProvider, err := NewTraceProvider(ctx, logger, cfg.RollupRpc, splitDepth, prestateBlock, poststateBlock)
bottomDepth := gameDepth - topDepth
outputProvider, err := NewTraceProvider(ctx, logger, cfg.RollupRpc, topDepth, prestateBlock, poststateBlock)
if err != nil { if err != nil {
return nil, err return nil, err
} }
...@@ -48,6 +47,6 @@ func NewOutputCannonTraceAccessor( ...@@ -48,6 +47,6 @@ func NewOutputCannonTraceAccessor(
} }
cache := NewProviderCache(m, "output_cannon_provider", cannonCreator) cache := NewProviderCache(m, "output_cannon_provider", cannonCreator)
selector := split.NewSplitProviderSelector(outputProvider, int(topDepth), OutputRootSplitAdapter(outputProvider, cache.GetOrCreate)) selector := split.NewSplitProviderSelector(outputProvider, int(splitDepth), OutputRootSplitAdapter(outputProvider, cache.GetOrCreate))
return trace.NewAccessor(selector), nil return trace.NewAccessor(selector), nil
} }
...@@ -143,6 +143,7 @@ func (h *FactoryHelper) StartAlphabetGame(ctx context.Context, claimedAlphabet s ...@@ -143,6 +143,7 @@ func (h *FactoryHelper) StartAlphabetGame(ctx context.Context, claimedAlphabet s
func (h *FactoryHelper) StartOutputCannonGame(ctx context.Context, rollupEndpoint string, rootClaim common.Hash) *OutputCannonGameHelper { func (h *FactoryHelper) StartOutputCannonGame(ctx context.Context, rollupEndpoint string, rootClaim common.Hash) *OutputCannonGameHelper {
rollupClient, err := dial.DialRollupClientWithTimeout(ctx, 30*time.Second, testlog.Logger(h.t, log.LvlInfo), rollupEndpoint) rollupClient, err := dial.DialRollupClientWithTimeout(ctx, 30*time.Second, testlog.Logger(h.t, log.LvlInfo), rollupEndpoint)
h.require.NoError(err) h.require.NoError(err)
h.t.Cleanup(rollupClient.Close)
extraData, _ := h.createBisectionGameExtraData(ctx, rollupClient) extraData, _ := h.createBisectionGameExtraData(ctx, rollupClient)
...@@ -279,11 +280,22 @@ func (h *FactoryHelper) createCannonGame(ctx context.Context, rootClaim common.H ...@@ -279,11 +280,22 @@ func (h *FactoryHelper) createCannonGame(ctx context.Context, rootClaim common.H
} }
func (h *FactoryHelper) createBisectionGameExtraData(ctx context.Context, client *sources.RollupClient) (extraData []byte, l2BlockNumber uint64) { func (h *FactoryHelper) createBisectionGameExtraData(ctx context.Context, client *sources.RollupClient) (extraData []byte, l2BlockNumber uint64) {
timeoutCtx, cancel := context.WithTimeout(ctx, 1*time.Minute)
defer cancel()
err := wait.For(timeoutCtx, time.Second, func() (bool, error) {
status, err := client.SyncStatus(ctx)
if err != nil {
return false, err
}
return status.SafeL2.Number > 0, nil
})
h.require.NoError(err, "Safe head did not progress past genesis")
syncStatus, err := client.SyncStatus(ctx) syncStatus, err := client.SyncStatus(ctx)
h.require.NoError(err, "failed to get sync status") h.require.NoError(err, "failed to get sync status")
l2BlockNumber = syncStatus.SafeL2.Number l2BlockNumber = syncStatus.SafeL2.Number
h.t.Logf("Creating game with l2 block number: %v", l2BlockNumber)
extraData = make([]byte, 32) extraData = make([]byte, 32)
binary.BigEndian.PutUint64(extraData, l2BlockNumber) binary.BigEndian.PutUint64(extraData[24:], l2BlockNumber)
return return
} }
......
...@@ -402,10 +402,12 @@ func (g *OutputGameHelper) gameData(ctx context.Context) string { ...@@ -402,10 +402,12 @@ func (g *OutputGameHelper) gameData(ctx context.Context) string {
info = info + fmt.Sprintf("%v - Position: %v, Depth: %v, IndexAtDepth: %v Trace Index: %v, Value: %v, Countered: %v, ParentIndex: %v\n", info = info + fmt.Sprintf("%v - Position: %v, Depth: %v, IndexAtDepth: %v Trace Index: %v, Value: %v, Countered: %v, ParentIndex: %v\n",
i, claim.Position.Int64(), pos.Depth(), pos.IndexAtDepth(), pos.TraceIndex(maxDepth), common.Hash(claim.Claim).Hex(), claim.Countered, claim.ParentIndex) i, claim.Position.Int64(), pos.Depth(), pos.IndexAtDepth(), pos.TraceIndex(maxDepth), common.Hash(claim.Claim).Hex(), claim.Countered, claim.ParentIndex)
} }
l2BlockNum, err := g.game.L2BlockNumber(opts)
g.require.NoError(err, "Load l2 block number")
status, err := g.game.Status(opts) status, err := g.game.Status(opts)
g.require.NoError(err, "Load game status") g.require.NoError(err, "Load game status")
return fmt.Sprintf("Game %v - %v - Split Depth: %v - Max Depth: %v:\n%v\n", return fmt.Sprintf("Game %v - %v - L2 Block: %v - Split Depth: %v - Max Depth: %v:\n%v\n",
g.addr, Status(status), splitDepth, maxDepth, info) g.addr, Status(status), l2BlockNum.Uint64(), splitDepth, maxDepth, info)
} }
func (g *OutputGameHelper) LogGameData(ctx context.Context) { func (g *OutputGameHelper) LogGameData(ctx context.Context) {
......
...@@ -38,12 +38,8 @@ func TestOutputCannonGame(t *testing.T) { ...@@ -38,12 +38,8 @@ func TestOutputCannonGame(t *testing.T) {
game.Attack(ctx, i, common.Hash{0xaa}) game.Attack(ctx, i, common.Hash{0xaa})
game.LogGameData(ctx) game.LogGameData(ctx)
} }
game.WaitForCorrectOutputRoot(ctx, splitDepth)
// Post the first cannon output root (with 01 status code to show the output root is invalid) // Wait for the challenger to post the first claim in the cannon trace
game.Attack(ctx, splitDepth, common.Hash{0x01}) game.WaitForClaimAtDepth(ctx, int(splitDepth+1))
// Challenger should counter
game.WaitForClaimAtDepth(ctx, int(splitDepth+2))
game.LogGameData(ctx) game.LogGameData(ctx)
} }
...@@ -47,10 +47,11 @@ ...@@ -47,10 +47,11 @@
"l2GenesisDeltaTimeOffset": null, "l2GenesisDeltaTimeOffset": null,
"l2GenesisCanyonTimeOffset": "0x0", "l2GenesisCanyonTimeOffset": "0x0",
"faultGameAbsolutePrestate": "0x03c7ae758795765c6664a5d39bf63841c71ff191e9189522bad8ebff5d4eca98", "faultGameAbsolutePrestate": "0x03c7ae758795765c6664a5d39bf63841c71ff191e9189522bad8ebff5d4eca98",
"faultGameMaxDepth": 30, "faultGameMaxDepth": 44,
"faultGameMaxDuration": 1200, "faultGameMaxDuration": 1200,
"outputBisectionGameGenesisBlock": 0, "outputBisectionGameGenesisBlock": 0,
"outputBisectionGameSplitDepth": 15, "outputBisectionGameGenesisOutputRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
"outputBisectionGameSplitDepth": 14,
"systemConfigStartBlock": 0, "systemConfigStartBlock": 0,
"requiredProtocolVersion": "0x0000000000000000000000000000000000000000000000000000000000000000", "requiredProtocolVersion": "0x0000000000000000000000000000000000000000000000000000000000000000",
"recommendedProtocolVersion": "0x0000000000000000000000000000000000000000000000000000000000000000" "recommendedProtocolVersion": "0x0000000000000000000000000000000000000000000000000000000000000000"
......
...@@ -1027,8 +1027,8 @@ contract Deploy is Deployer { ...@@ -1027,8 +1027,8 @@ contract Deploy is Deployer {
_gameType: GameTypes.FAULT, _gameType: GameTypes.FAULT,
_absolutePrestate: loadMipsAbsolutePrestate(), _absolutePrestate: loadMipsAbsolutePrestate(),
_faultVm: IBigStepper(mustGetAddress("Mips")), _faultVm: IBigStepper(mustGetAddress("Mips")),
_maxGameDepth: cfg.faultGameMaxDepth() _maxGameDepth: 30 // Hard code depth for legacy game to keep e2e tests fast
}); });
} }
/// @notice Sets the implementation for the `OUTPUT_CANNON` game type in the `DisputeGameFactory` /// @notice Sets the implementation for the `OUTPUT_CANNON` game type in the `DisputeGameFactory`
...@@ -1106,6 +1106,7 @@ contract Deploy is Deployer { ...@@ -1106,6 +1106,7 @@ contract Deploy is Deployer {
_gameType: _gameType, _gameType: _gameType,
_absolutePrestate: _absolutePrestate, _absolutePrestate: _absolutePrestate,
_genesisBlockNumber: cfg.outputBisectionGameGenesisBlock(), _genesisBlockNumber: cfg.outputBisectionGameGenesisBlock(),
_genesisOutputRoot: Hash.wrap(cfg.outputBisectionGameGenesisOutputRoot()),
_maxGameDepth: _maxGameDepth, _maxGameDepth: _maxGameDepth,
_splitDepth: cfg.outputBisectionGameSplitDepth(), _splitDepth: cfg.outputBisectionGameSplitDepth(),
_gameDuration: Duration.wrap(uint64(cfg.faultGameMaxDuration())), _gameDuration: Duration.wrap(uint64(cfg.faultGameMaxDuration())),
......
...@@ -51,6 +51,7 @@ contract DeployConfig is Script { ...@@ -51,6 +51,7 @@ contract DeployConfig is Script {
uint256 public faultGameMaxDepth; uint256 public faultGameMaxDepth;
uint256 public faultGameMaxDuration; uint256 public faultGameMaxDuration;
uint256 public outputBisectionGameGenesisBlock; uint256 public outputBisectionGameGenesisBlock;
bytes32 public outputBisectionGameGenesisOutputRoot;
uint256 public outputBisectionGameSplitDepth; uint256 public outputBisectionGameSplitDepth;
uint256 public systemConfigStartBlock; uint256 public systemConfigStartBlock;
uint256 public requiredProtocolVersion; uint256 public requiredProtocolVersion;
...@@ -107,6 +108,7 @@ contract DeployConfig is Script { ...@@ -107,6 +108,7 @@ contract DeployConfig is Script {
faultGameMaxDepth = stdJson.readUint(_json, "$.faultGameMaxDepth"); faultGameMaxDepth = stdJson.readUint(_json, "$.faultGameMaxDepth");
faultGameMaxDuration = stdJson.readUint(_json, "$.faultGameMaxDuration"); faultGameMaxDuration = stdJson.readUint(_json, "$.faultGameMaxDuration");
outputBisectionGameGenesisBlock = stdJson.readUint(_json, "$.outputBisectionGameGenesisBlock"); outputBisectionGameGenesisBlock = stdJson.readUint(_json, "$.outputBisectionGameGenesisBlock");
outputBisectionGameGenesisOutputRoot = stdJson.readBytes32(_json, "$.outputBisectionGameGenesisOutputRoot");
outputBisectionGameSplitDepth = stdJson.readUint(_json, "$.outputBisectionGameSplitDepth"); outputBisectionGameSplitDepth = stdJson.readUint(_json, "$.outputBisectionGameSplitDepth");
} }
} }
......
...@@ -100,8 +100,8 @@ ...@@ -100,8 +100,8 @@
"sourceCodeHash": "0x1d0cacaf259aff7802aae91a793e3c7234a4d063614cf9c72176fb04738e7c97" "sourceCodeHash": "0x1d0cacaf259aff7802aae91a793e3c7234a4d063614cf9c72176fb04738e7c97"
}, },
"src/dispute/OutputBisectionGame.sol": { "src/dispute/OutputBisectionGame.sol": {
"initCodeHash": "0xc5ac9d76d7c46ccc073f3e5d74a78253bf2f627b04af9b2e3c86803c44890075", "initCodeHash": "0x400a99278755979b815712d1d26598463dd98ed193df8cd1736ae2ae5831d7c7",
"sourceCodeHash": "0x68df25016fa101a9d40e5d00f839dd053e7b5aa0c73c06c61d483cdfda0be124" "sourceCodeHash": "0x7e267ad18eb946a0242df41ba044c5ee6f0b456e74bef07605a7dd2eb5b3ed01"
}, },
"src/legacy/DeployerWhitelist.sol": { "src/legacy/DeployerWhitelist.sol": {
"initCodeHash": "0x8de80fb23b26dd9d849f6328e56ea7c173cd9e9ce1f05c9beea559d1720deb3d", "initCodeHash": "0x8de80fb23b26dd9d849f6328e56ea7c173cd9e9ce1f05c9beea559d1720deb3d",
......
...@@ -16,6 +16,11 @@ ...@@ -16,6 +16,11 @@
"name": "_genesisBlockNumber", "name": "_genesisBlockNumber",
"type": "uint256" "type": "uint256"
}, },
{
"internalType": "Hash",
"name": "_genesisOutputRoot",
"type": "bytes32"
},
{ {
"internalType": "uint256", "internalType": "uint256",
"name": "_maxGameDepth", "name": "_maxGameDepth",
...@@ -198,6 +203,19 @@ ...@@ -198,6 +203,19 @@
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
}, },
{
"inputs": [],
"name": "GENESIS_OUTPUT_ROOT",
"outputs": [
{
"internalType": "Hash",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "view",
"type": "function"
},
{ {
"inputs": [], "inputs": [],
"name": "MAX_GAME_DEPTH", "name": "MAX_GAME_DEPTH",
......
...@@ -61,10 +61,7 @@ interface IOutputBisectionGame is IDisputeGame { ...@@ -61,10 +61,7 @@ interface IOutputBisectionGame is IDisputeGame {
/// @param _claimIndex The index of the subgame root claim to resolve. /// @param _claimIndex The index of the subgame root claim to resolve.
function resolveClaim(uint256 _claimIndex) external payable; function resolveClaim(uint256 _claimIndex) external payable;
/// @notice An L1 block hash that contains the disputed output root, fetched from the /// @notice A block hash on the L1 that contains the disputed output root.
/// `BlockOracle` and verified by referencing the timestamp associated with the
/// first L2 Output Proposal in the `L2OutputOracle` that contains the disputed
/// L2 block number.
function l1Head() external view returns (Hash l1Head_); function l1Head() external view returns (Hash l1Head_);
/// @notice The l2BlockNumber of the disputed output root in the `L2OutputOracle`. /// @notice The l2BlockNumber of the disputed output root in the `L2OutputOracle`.
......
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