Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
N
nebula
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
exchain
nebula
Commits
d3f3751e
Commit
d3f3751e
authored
Aug 01, 2023
by
clabby
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
init L1 head verification
parent
ac02d49e
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
110 additions
and
21 deletions
+110
-21
Deploy.s.sol
packages/contracts-bedrock/scripts/Deploy.s.sol
+1
-1
BlockOracle.sol
packages/contracts-bedrock/src/dispute/BlockOracle.sol
+48
-0
FaultDisputeGame.sol
packages/contracts-bedrock/src/dispute/FaultDisputeGame.sol
+40
-10
DisputeErrors.sol
packages/contracts-bedrock/src/libraries/DisputeErrors.sol
+5
-1
BlockOracle.t.sol
packages/contracts-bedrock/test/BlockOracle.t.sol
+12
-5
FaultDisputeGame.t.sol
packages/contracts-bedrock/test/FaultDisputeGame.t.sol
+4
-4
No files found.
packages/contracts-bedrock/scripts/Deploy.s.sol
View file @
d3f3751e
...
@@ -24,7 +24,7 @@ import { ResourceMetering } from "src/L1/ResourceMetering.sol";
...
@@ -24,7 +24,7 @@ import { ResourceMetering } from "src/L1/ResourceMetering.sol";
import { Constants } from "src/libraries/Constants.sol";
import { Constants } from "src/libraries/Constants.sol";
import { DisputeGameFactory } from "src/dispute/DisputeGameFactory.sol";
import { DisputeGameFactory } from "src/dispute/DisputeGameFactory.sol";
import { FaultDisputeGame } from "src/dispute/FaultDisputeGame.sol";
import { FaultDisputeGame } from "src/dispute/FaultDisputeGame.sol";
import { Block
HashOracle } from "src/dispute/BlockHash
Oracle.sol";
import { Block
Oracle } from "src/dispute/Block
Oracle.sol";
import { L1ERC721Bridge } from "src/L1/L1ERC721Bridge.sol";
import { L1ERC721Bridge } from "src/L1/L1ERC721Bridge.sol";
import { Predeploys } from "src/libraries/Predeploys.sol";
import { Predeploys } from "src/libraries/Predeploys.sol";
...
...
packages/contracts-bedrock/src/dispute/Block
Hash
Oracle.sol
→
packages/contracts-bedrock/src/dispute/BlockOracle.sol
View file @
d3f3751e
...
@@ -4,27 +4,45 @@ pragma solidity 0.8.15;
...
@@ -4,27 +4,45 @@ pragma solidity 0.8.15;
import "../libraries/DisputeTypes.sol";
import "../libraries/DisputeTypes.sol";
import "../libraries/DisputeErrors.sol";
import "../libraries/DisputeErrors.sol";
/// @title Block
Hash
Oracle
/// @title BlockOracle
/// @notice Stores a map of block numbers => block hashes for use in dispute resolution
/// @notice Stores a map of block numbers => block hashes for use in dispute resolution
contract BlockHashOracle {
contract BlockOracle {
/// @notice Maps block numbers to block hashes
/// @notice The BlockInfo struct contains a block's hash and estimated timestamp.
mapping(uint256 => Hash) internal blockHashes;
struct BlockInfo {
Hash hash;
Timestamp timestamp;
}
/// @notice Maps block numbers to block hashes and timestamps
mapping(uint256 => BlockInfo) internal blockHashes;
/// @notice Loads a block hash for a given block number, assuming that the block number
/// @notice Loads a block hash for a given block number, assuming that the block number
/// has been stored in the oracle.
/// has been stored in the oracle.
/// @param _blockNumber The block number to load the block hash for.
/// @param _blockNumber The block number to load the block hash
and timestamp
for.
/// @return block
Hash_ The block hash
for the given block number.
/// @return block
Info_ The block hash and timestamp
for the given block number.
function load(uint256 _blockNumber) external view returns (
Hash blockHash
_) {
function load(uint256 _blockNumber) external view returns (
BlockInfo memory blockInfo
_) {
block
Hash
_ = blockHashes[_blockNumber];
block
Info
_ = blockHashes[_blockNumber];
if (Hash.unwrap(block
Hash_
) == 0) revert BlockHashNotPresent();
if (Hash.unwrap(block
Info_.hash
) == 0) revert BlockHashNotPresent();
}
}
/// @notice Stores a block hash for a given block number, assuming that the block number
/// @notice Stores a block hash for a given block number, assuming that the block number
/// is within the acceptable range of [tip - 256, tip].
/// is within the acceptable range of [tip - 256, tip].
/// @param _blockNumber The block number to persist the block hash for.
/// @param _blockNumber The block number to persist the block hash for.
function store(uint256 _blockNumber) external {
function store(uint256 _blockNumber) external {
// Fetch the block hash for the given block number and revert if it is out of
// the `BLOCKHASH` opcode's range.
bytes32 blockHash = blockhash(_blockNumber);
bytes32 blockHash = blockhash(_blockNumber);
if (blockHash == 0) revert BlockNumberOOB();
if (blockHash == 0) revert BlockNumberOOB();
blockHashes[_blockNumber] = Hash.wrap(blockHash);
// Estimate the timestamp of the block assuming an average block time of 13 seconds.
Timestamp estimatedTimestamp = Timestamp.wrap(
uint64(block.timestamp - ((block.number - _blockNumber) * 13))
);
// Persist the block information.
blockHashes[_blockNumber] = BlockInfo({
hash: Hash.wrap(blockHash),
timestamp: estimatedTimestamp
});
}
}
}
}
packages/contracts-bedrock/src/dispute/FaultDisputeGame.sol
View file @
d3f3751e
...
@@ -7,7 +7,7 @@ import { IInitializable } from "./interfaces/IInitializable.sol";
...
@@ -7,7 +7,7 @@ import { IInitializable } from "./interfaces/IInitializable.sol";
import { IBondManager } from "./interfaces/IBondManager.sol";
import { IBondManager } from "./interfaces/IBondManager.sol";
import { IBigStepper, IPreimageOracle } from "./interfaces/IBigStepper.sol";
import { IBigStepper, IPreimageOracle } from "./interfaces/IBigStepper.sol";
import { L2OutputOracle } from "../L1/L2OutputOracle.sol";
import { L2OutputOracle } from "../L1/L2OutputOracle.sol";
import { Block
HashOracle } from "./BlockHash
Oracle.sol";
import { Block
Oracle } from "./Block
Oracle.sol";
import { Clone } from "../libraries/Clone.sol";
import { Clone } from "../libraries/Clone.sol";
import { Types } from "../libraries/Types.sol";
import { Types } from "../libraries/Types.sol";
...
@@ -43,8 +43,8 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, Semver {
...
@@ -43,8 +43,8 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, Semver {
L2OutputOracle public immutable L2_OUTPUT_ORACLE;
L2OutputOracle public immutable L2_OUTPUT_ORACLE;
/// @notice The block hash oracle, used for loading block hashes further back
/// @notice The block hash oracle, used for loading block hashes further back
/// than the `BLOCKHASH` opcode allows.
/// than the `BLOCKHASH` opcode allows
as well as their estimated timestamps
.
Block
HashOracle public immutable BLOCK_HASH
_ORACLE;
Block
Oracle public immutable BLOCK
_ORACLE;
/// @notice The root claim's position is always at gindex 1.
/// @notice The root claim's position is always at gindex 1.
Position internal constant ROOT_POSITION = Position.wrap(1);
Position internal constant ROOT_POSITION = Position.wrap(1);
...
@@ -64,6 +64,11 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, Semver {
...
@@ -64,6 +64,11 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, Semver {
/// @notice An append-only array of all claims made during the dispute game.
/// @notice An append-only array of all claims made during the dispute game.
ClaimData[] public claimData;
ClaimData[] public claimData;
/// @notice The starting and disputed output proposals, which are used to determine where
/// game participants should start executing Cannon and to verify the state transition,
/// respectively.
Types.OutputProposal[2] public proposals;
/// @notice An internal mapping to allow for constant-time lookups of existing claims.
/// @notice An internal mapping to allow for constant-time lookups of existing claims.
mapping(ClaimHash => bool) internal claims;
mapping(ClaimHash => bool) internal claims;
...
@@ -73,6 +78,9 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, Semver {
...
@@ -73,6 +78,9 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, Semver {
/// @param _vm An onchain VM that performs single instruction steps on a fault proof program
/// @param _vm An onchain VM that performs single instruction steps on a fault proof program
/// trace.
/// trace.
/// @param _l2oo The trusted L2OutputOracle contract.
/// @param _l2oo The trusted L2OutputOracle contract.
/// @param _blockOracle The block oracle, used for loading block hashes further back
/// than the `BLOCKHASH` opcode allows as well as their estimated
/// timestamps.
/// @custom:semver 0.0.5
/// @custom:semver 0.0.5
constructor(
constructor(
Claim _absolutePrestate,
Claim _absolutePrestate,
...
@@ -80,14 +88,14 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, Semver {
...
@@ -80,14 +88,14 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, Semver {
Duration _gameDuration,
Duration _gameDuration,
IBigStepper _vm,
IBigStepper _vm,
L2OutputOracle _l2oo,
L2OutputOracle _l2oo,
Block
HashOracle _blockHash
Oracle
Block
Oracle _block
Oracle
) Semver(0, 0, 5) {
) Semver(0, 0, 5) {
ABSOLUTE_PRESTATE = _absolutePrestate;
ABSOLUTE_PRESTATE = _absolutePrestate;
MAX_GAME_DEPTH = _maxGameDepth;
MAX_GAME_DEPTH = _maxGameDepth;
GAME_DURATION = _gameDuration;
GAME_DURATION = _gameDuration;
VM = _vm;
VM = _vm;
L2_OUTPUT_ORACLE = _l2oo;
L2_OUTPUT_ORACLE = _l2oo;
BLOCK_
HASH_ORACLE = _blockHash
Oracle;
BLOCK_
ORACLE = _block
Oracle;
}
}
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
...
@@ -422,6 +430,9 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, Semver {
...
@@ -422,6 +430,9 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, Semver {
/// @inheritdoc IInitializable
/// @inheritdoc IInitializable
function initialize() external {
function initialize() external {
// SAFETY: Any revert in this function will bubble up to the DisputeGameFactory and
// prevent the game from being created.
// Set the game start
// Set the game start
gameStart = Timestamp.wrap(uint64(block.timestamp));
gameStart = Timestamp.wrap(uint64(block.timestamp));
// Set the game status
// Set the game status
...
@@ -438,11 +449,30 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, Semver {
...
@@ -438,11 +449,30 @@ contract FaultDisputeGame is IFaultDisputeGame, Clone, Semver {
})
})
);
);
// Set the L1 head hash to the block hash of the L1 block number provided.
// Grab the index of the output proposal that commits to the starting L2 head.
// This call can revert if the block hash oracle does not have information
// The output disputed is all outputs after this one.
// about the block number provided to it. This revert will bubble up to the
// TODO(clabby): This is 2 calls too many for the information we need. Maybe
// DisputeGameFactory and prevent the game from being created.
// add a function to the L2OO?
l1Head = BLOCK_HASH_ORACLE.load(_getArgUint256(0x40));
uint256 proposalIdx = L2_OUTPUT_ORACLE.getL2OutputIndexAfter(l2BlockNumber());
Types.OutputProposal memory starting = L2_OUTPUT_ORACLE.getL2Output(proposalIdx);
Types.OutputProposal memory disputed = L2_OUTPUT_ORACLE.getL2Output(proposalIdx + 1);
// SAFETY: This call can revert if the block hash oracle does not have information
// about the block number provided to it.
BlockOracle.BlockInfo memory blockInfo = BLOCK_ORACLE.load(l1BlockNumber());
// INVARIANT: The L1 head must contain the disputed output root. If it does not,
// the game cannot be played.
// TODO(clabby): The block timestamp in the oracle is an estimate that assumes a 13
// second block time. Should we add a buffer here to ensure that the
// estimation has room for error? This invariant cannot break.
if (Timestamp.unwrap(blockInfo.timestamp) < disputed.timestamp) revert L1HeadTooOld();
// Persist the output proposals fetched from the oracle.
// TODO(clabby): Docs on why we do this.
proposals[0] = starting;
proposals[1] = disputed;
// Persist the L1 head hash of the L1 block number provided.
l1Head = blockInfo.hash;
}
}
/// @notice Returns the length of the `claimData` array.
/// @notice Returns the length of the `claimData` array.
...
...
packages/contracts-bedrock/src/libraries/DisputeErrors.sol
View file @
d3f3751e
...
@@ -16,7 +16,7 @@ error NoImplementation(GameType gameType);
...
@@ -16,7 +16,7 @@ error NoImplementation(GameType gameType);
error GameAlreadyExists(Hash uuid);
error GameAlreadyExists(Hash uuid);
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
// `
DisputeGame_Fault.sol` Errors
//
// `
FaultDisputeGame.sol` Errors
//
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
/// @notice Thrown when a supplied bond is too low to cover the
/// @notice Thrown when a supplied bond is too low to cover the
...
@@ -54,6 +54,10 @@ error InvalidPrestate();
...
@@ -54,6 +54,10 @@ error InvalidPrestate();
/// @notice Thrown when a step is made that computes the expected post state correctly.
/// @notice Thrown when a step is made that computes the expected post state correctly.
error ValidStep();
error ValidStep();
/// @notice Thrown when a game is attempted to be initialized with an L1 head that does
/// not contain the disputed output root.
error L1HeadTooOld();
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
// `AttestationDisputeGame` Errors //
// `AttestationDisputeGame` Errors //
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
...
...
packages/contracts-bedrock/test/Block
Hash
Oracle.t.sol
→
packages/contracts-bedrock/test/BlockOracle.t.sol
View file @
d3f3751e
...
@@ -2,16 +2,19 @@
...
@@ -2,16 +2,19 @@
pragma solidity 0.8.15;
pragma solidity 0.8.15;
import { Test } from "forge-std/Test.sol";
import { Test } from "forge-std/Test.sol";
import { Block
HashOracle } from "src/dispute/BlockHash
Oracle.sol";
import { Block
Oracle } from "src/dispute/Block
Oracle.sol";
import "src/libraries/DisputeTypes.sol";
import "src/libraries/DisputeTypes.sol";
import "src/libraries/DisputeErrors.sol";
import "src/libraries/DisputeErrors.sol";
contract Block
Hash
Oracle_Test is Test {
contract BlockOracle_Test is Test {
Block
Hash
Oracle oracle;
BlockOracle oracle;
function setUp() public {
function setUp() public {
oracle = new BlockHashOracle();
oracle = new BlockOracle();
// Roll the chain forward 255 blocks.
vm.roll(block.number + 255);
vm.roll(block.number + 255);
// Set the time to a realistic date.
vm.warp(1690906994);
}
}
/// @notice Tests that loading a block hash for a block number within the range of the
/// @notice Tests that loading a block hash for a block number within the range of the
...
@@ -19,7 +22,11 @@ contract BlockHashOracle_Test is Test {
...
@@ -19,7 +22,11 @@ contract BlockHashOracle_Test is Test {
function testFuzz_store_succeeds(uint256 _blockNumber) public {
function testFuzz_store_succeeds(uint256 _blockNumber) public {
_blockNumber = bound(_blockNumber, 0, 255);
_blockNumber = bound(_blockNumber, 0, 255);
oracle.store(_blockNumber);
oracle.store(_blockNumber);
assertEq(Hash.unwrap(oracle.load(_blockNumber)), blockhash(_blockNumber));
BlockOracle.BlockInfo memory res = oracle.load(_blockNumber);
assertEq(Hash.unwrap(res.hash), blockhash(_blockNumber));
emit log_uint(block.timestamp - ((block.number - _blockNumber) * 13));
assertEq(Timestamp.unwrap(res.timestamp), block.timestamp - ((block.number - _blockNumber) * 13));
}
}
/// @notice Tests that loading a block hash for a block number outside the range of the
/// @notice Tests that loading a block hash for a block number outside the range of the
...
...
packages/contracts-bedrock/test/FaultDisputeGame.t.sol
View file @
d3f3751e
...
@@ -7,7 +7,7 @@ import { DisputeGameFactory_Init } from "./DisputeGameFactory.t.sol";
...
@@ -7,7 +7,7 @@ import { DisputeGameFactory_Init } from "./DisputeGameFactory.t.sol";
import { DisputeGameFactory } from "src/dispute/DisputeGameFactory.sol";
import { DisputeGameFactory } from "src/dispute/DisputeGameFactory.sol";
import { FaultDisputeGame } from "src/dispute/FaultDisputeGame.sol";
import { FaultDisputeGame } from "src/dispute/FaultDisputeGame.sol";
import { L2OutputOracle } from "src/L1/L2OutputOracle.sol";
import { L2OutputOracle } from "src/L1/L2OutputOracle.sol";
import { Block
HashOracle } from "src/dispute/BlockHash
Oracle.sol";
import { Block
Oracle } from "src/dispute/Block
Oracle.sol";
import "src/libraries/DisputeTypes.sol";
import "src/libraries/DisputeTypes.sol";
import "src/libraries/DisputeErrors.sol";
import "src/libraries/DisputeErrors.sol";
...
@@ -35,8 +35,8 @@ contract FaultDisputeGame_Init is DisputeGameFactory_Init {
...
@@ -35,8 +35,8 @@ contract FaultDisputeGame_Init is DisputeGameFactory_Init {
vm.roll(block.number + 1);
vm.roll(block.number + 1);
// Deploy a new block hash oracle and store the block hash for the genesis block.
// Deploy a new block hash oracle and store the block hash for the genesis block.
Block
HashOracle blockHashOracle = new BlockHash
Oracle();
Block
Oracle blockOracle = new Block
Oracle();
block
Hash
Oracle.store(0);
blockOracle.store(0);
// Deploy an implementation of the fault game
// Deploy an implementation of the fault game
gameImpl = new FaultDisputeGame(
gameImpl = new FaultDisputeGame(
...
@@ -45,7 +45,7 @@ contract FaultDisputeGame_Init is DisputeGameFactory_Init {
...
@@ -45,7 +45,7 @@ contract FaultDisputeGame_Init is DisputeGameFactory_Init {
Duration.wrap(7 days),
Duration.wrap(7 days),
new AlphabetVM(absolutePrestate),
new AlphabetVM(absolutePrestate),
L2OutputOracle(deployNoop()),
L2OutputOracle(deployNoop()),
block
Hash
Oracle
blockOracle
);
);
// Register the game implementation with the factory.
// Register the game implementation with the factory.
factory.setImplementation(GAME_TYPE, gameImpl);
factory.setImplementation(GAME_TYPE, gameImpl);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment