Commit 9e976947 authored by Joshua Gutow's avatar Joshua Gutow Committed by GitHub

ops: Use starting L1 Block for timestamp everywhere (#3085)

* ops: Use starting L1 Block for timestamp everywhere

This transitions the starting timestamp to a new flow. The L2 rollup
is anchored on a L1 block. The L2 genesis block & rollup config use
the timestamp of the L1 start block as the their time. Properly
threading this through the HH tasks is a little tricky but possible.
This is because we have two flows: creating a L1 network & placing
the rollup on that and creating a rollup on an existing L1 network
(like goerli). There is still a L1 starting time for the first flow.

This also fixes a circular dependcy that previously existed. The
starting timestamp was provided and served as the starting timestamp
for the L1 genesis & the "L2 Starting Time" in the L2 Output Oracle.
The actual L2 genesis & rollup start time were based on when the
Optimism Portal contract was deployed (after the L2 Output Oracle
contract must have been deployed). The rollup is resilient to being
started before contracts are fully deployed, so using a specific
L1 block as the start is the cleanest solution I have seen.

* Fix lint

* Update packages/contracts-bedrock/deploy-config/goerli.ts

* Add undefined checks to l1StartingBlockTag

* lint

* fix checks
Co-authored-by: default avatarMatthew Slipper <me@matthewslipper.com>
Co-authored-by: default avatarmergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
parent 966e1b19
...@@ -58,9 +58,9 @@ function wait_up { ...@@ -58,9 +58,9 @@ function wait_up {
mkdir -p ./.devnet mkdir -p ./.devnet
if [ ! -f ./.devnet/rollup.json ]; then if [ ! -f ./.devnet/rollup.json ]; then
GENESIS_TIMESTAMP=$(date +%s | xargs printf "0x%x") L1_GENESIS_TIMESTAMP=$(date +%s | xargs printf "0x%x")
else else
GENESIS_TIMESTAMP=$(jq '.timestamp' < .devnet/genesis-l1.json) L1_GENESIS_TIMESTAMP=$(jq '.timestamp' < .devnet/genesis-l1.json)
fi fi
# Regenerate the L1 genesis file if necessary. The existence of the genesis # Regenerate the L1 genesis file if necessary. The existence of the genesis
...@@ -69,7 +69,7 @@ if [ ! -f ./.devnet/genesis-l1.json ]; then ...@@ -69,7 +69,7 @@ if [ ! -f ./.devnet/genesis-l1.json ]; then
echo "Regenerating L1 genesis." echo "Regenerating L1 genesis."
( (
cd $CONTRACTS_BEDROCK cd $CONTRACTS_BEDROCK
L2OO_STARTING_BLOCK_TIMESTAMP=$GENESIS_TIMESTAMP npx hardhat genesis-l1 \ L1_GENESIS_TIMESTAMP=$L1_GENESIS_TIMESTAMP npx hardhat genesis-l1 \
--outfile genesis-l1.json --outfile genesis-l1.json
mv genesis-l1.json ../../.devnet/genesis-l1.json mv genesis-l1.json ../../.devnet/genesis-l1.json
) )
...@@ -89,7 +89,7 @@ if [ ! -d $CONTRACTS_BEDROCK/deployments/$NETWORK ]; then ...@@ -89,7 +89,7 @@ if [ ! -d $CONTRACTS_BEDROCK/deployments/$NETWORK ]; then
( (
echo "Deploying contracts." echo "Deploying contracts."
cd $CONTRACTS_BEDROCK cd $CONTRACTS_BEDROCK
L2OO_STARTING_BLOCK_TIMESTAMP=$GENESIS_TIMESTAMP yarn hardhat --network $NETWORK deploy L1_GENESIS_TIMESTAMP=$L1_GENESIS_TIMESTAMP yarn hardhat --network $NETWORK deploy
) )
else else
echo "Contracts already deployed, skipping." echo "Contracts already deployed, skipping."
...@@ -99,7 +99,7 @@ if [ ! -f ./.devnet/genesis-l2.json ]; then ...@@ -99,7 +99,7 @@ if [ ! -f ./.devnet/genesis-l2.json ]; then
( (
echo "Creating L2 genesis file." echo "Creating L2 genesis file."
cd $CONTRACTS_BEDROCK cd $CONTRACTS_BEDROCK
L2OO_STARTING_BLOCK_TIMESTAMP=$GENESIS_TIMESTAMP npx hardhat --network $NETWORK genesis-l2 L1_GENESIS_TIMESTAMP=$L1_GENESIS_TIMESTAMP npx hardhat --network $NETWORK genesis-l2
mv genesis.json ../../.devnet/genesis-l2.json mv genesis.json ../../.devnet/genesis-l2.json
echo "Created L2 genesis." echo "Created L2 genesis."
) )
...@@ -120,7 +120,7 @@ if [ ! -f ./.devnet/rollup.json ]; then ...@@ -120,7 +120,7 @@ if [ ! -f ./.devnet/rollup.json ]; then
( (
echo "Building rollup config..." echo "Building rollup config..."
cd $CONTRACTS_BEDROCK cd $CONTRACTS_BEDROCK
L2OO_STARTING_BLOCK_TIMESTAMP=$GENESIS_TIMESTAMP npx hardhat --network $NETWORK rollup-config L1_GENESIS_TIMESTAMP=$L1_GENESIS_TIMESTAMP npx hardhat --network $NETWORK rollup-config
mv rollup.json ../../.devnet/rollup.json mv rollup.json ../../.devnet/rollup.json
) )
else else
......
...@@ -2,19 +2,20 @@ import { ethers } from 'ethers' ...@@ -2,19 +2,20 @@ import { ethers } from 'ethers'
const { env } = process const { env } = process
const startingTimestamp = const l1GenesisTimestamp =
typeof env.L2OO_STARTING_BLOCK_TIMESTAMP === 'string' typeof env.L1_GENESIS_TIMESTAMP === 'string'
? ethers.BigNumber.from(env.L2OO_STARTING_BLOCK_TIMESTAMP).toNumber() ? ethers.BigNumber.from(env.L1_GENESIS_TIMESTAMP).toNumber()
: Math.floor(Date.now() / 1000) : Math.floor(Date.now() / 1000)
const config = { const config = {
submissionInterval: 6, submissionInterval: 6,
genesisOutput: ethers.constants.HashZero, genesisOutput: ethers.constants.HashZero,
historicalBlocks: 0, historicalBlocks: 0,
l1StartingBlockTag: 'earliest',
startingBlockNumber: 0, startingBlockNumber: 0,
l2BlockTime: 2, l2BlockTime: 2,
startingTimestamp, l1GenesisTimestamp,
sequencerAddress: '0x70997970C51812dc3A010C7d01b50e0d17dc79C8', sequencerAddress: '0x70997970C51812dc3A010C7d01b50e0d17dc79C8',
l2CrossDomainMessengerOwner: ethers.constants.AddressZero, l2CrossDomainMessengerOwner: ethers.constants.AddressZero,
......
import { ethers } from 'ethers' import { ethers } from 'ethers'
const sequencerAddress = '0x6c23a0dcdfc44b7a57bed148de598895e398d984' const sequencerAddress = '0x6c23a0dcdfc44b7a57bed148de598895e398d984'
const startingTimestamp = 1658777882 const l1StartingBlockTag =
'0xafce66a0a2446856112e4069b275ad32b1f4a607888f9c4c59eddf9be81f8670'
const config = { const config = {
submissionInterval: 6, submissionInterval: 6,
...@@ -9,7 +10,7 @@ const config = { ...@@ -9,7 +10,7 @@ const config = {
historicalBlocks: 0, historicalBlocks: 0,
startingBlockNumber: 0, startingBlockNumber: 0,
l2BlockTime: 2, l2BlockTime: 2,
startingTimestamp, l1StartingBlockTag,
sequencerAddress, sequencerAddress,
l2CrossDomainMessengerOwner: ethers.constants.AddressZero, l2CrossDomainMessengerOwner: ethers.constants.AddressZero,
......
...@@ -2,9 +2,9 @@ import { ethers } from 'ethers' ...@@ -2,9 +2,9 @@ import { ethers } from 'ethers'
const { env } = process const { env } = process
const startingTimestamp = const l1GenesisTimestamp =
typeof env.L2OO_STARTING_BLOCK_TIMESTAMP === 'string' typeof env.L1_GENESIS_TIMESTAMP === 'string'
? ethers.BigNumber.from(env.L2OO_STARTING_BLOCK_TIMESTAMP).toNumber() ? ethers.BigNumber.from(env.L1_GENESIS_TIMESTAMP).toNumber()
: Math.floor(Date.now() / 1000) : Math.floor(Date.now() / 1000)
const config = { const config = {
...@@ -12,8 +12,9 @@ const config = { ...@@ -12,8 +12,9 @@ const config = {
genesisOutput: ethers.constants.HashZero, genesisOutput: ethers.constants.HashZero,
historicalBlocks: 0, historicalBlocks: 0,
startingBlockNumber: 0, startingBlockNumber: 0,
l1StartingBlockTag: 'earliest',
l2BlockTime: 2, l2BlockTime: 2,
startingTimestamp, l1GenesisTimestamp,
sequencerAddress: '0x70997970C51812dc3A010C7d01b50e0d17dc79C8', sequencerAddress: '0x70997970C51812dc3A010C7d01b50e0d17dc79C8',
maxSequencerDrift: 10, maxSequencerDrift: 10,
sequencerWindowSize: 4, sequencerWindowSize: 4,
......
...@@ -10,13 +10,10 @@ const deployFn: DeployFunction = async (hre) => { ...@@ -10,13 +10,10 @@ const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts() const { deployer } = await hre.getNamedAccounts()
const { deployConfig } = hre const { deployConfig } = hre
if ( const l1 = hre.ethers.getDefaultProvider()
typeof deployConfig.startingTimestamp !== 'number' || const l1StartingBlock = await l1.getBlock(deployConfig.l1StartingBlockTag)
isNaN(deployConfig.startingTimestamp) if (l1StartingBlock === null) {
) { throw new Error(`Cannot fetch block tag ${deployConfig.l1StartingBlockTag}`)
throw new Error(
'Cannot deploy L2OutputOracle without specifying a valid startingTimestamp.'
)
} }
await deploy('L2OutputOracleProxy', { await deploy('L2OutputOracleProxy', {
...@@ -34,7 +31,7 @@ const deployFn: DeployFunction = async (hre) => { ...@@ -34,7 +31,7 @@ const deployFn: DeployFunction = async (hre) => {
deployConfig.genesisOutput, deployConfig.genesisOutput,
deployConfig.historicalBlocks, deployConfig.historicalBlocks,
deployConfig.startingBlockNumber, deployConfig.startingBlockNumber,
deployConfig.startingTimestamp, l1StartingBlock.timestamp,
deployConfig.l2BlockTime, deployConfig.l2BlockTime,
deployConfig.sequencerAddress, deployConfig.sequencerAddress,
deployConfig.outputOracleOwner, deployConfig.outputOracleOwner,
...@@ -84,7 +81,7 @@ const deployFn: DeployFunction = async (hre) => { ...@@ -84,7 +81,7 @@ const deployFn: DeployFunction = async (hre) => {
} }
const startingTimestamp = await L2OutputOracle.STARTING_TIMESTAMP() const startingTimestamp = await L2OutputOracle.STARTING_TIMESTAMP()
if (!startingTimestamp.eq(BigNumber.from(deployConfig.startingTimestamp))) { if (!startingTimestamp.eq(BigNumber.from(l1StartingBlock.timestamp))) {
throw new Error('starting timestamp misconfigured') throw new Error('starting timestamp misconfigured')
} }
const l2BlockTime = await L2OutputOracle.L2_BLOCK_TIME() const l2BlockTime = await L2OutputOracle.L2_BLOCK_TIME()
......
...@@ -74,18 +74,15 @@ const config: HardhatUserConfig = { ...@@ -74,18 +74,15 @@ const config: HardhatUserConfig = {
historicalBlocks: { historicalBlocks: {
type: 'number', type: 'number',
}, },
startingBlockNumber: {
type: 'number',
},
startingTimestamp: {
type: 'number',
},
sequencerAddress: { sequencerAddress: {
type: 'address', type: 'address',
}, },
outputOracleOwner: { outputOracleOwner: {
type: 'address', type: 'address',
}, },
l1StartingBlockTag: {
type: 'string',
},
}, },
external: { external: {
contracts: [ contracts: [
......
...@@ -61,7 +61,7 @@ task('genesis-l1', 'create a genesis config') ...@@ -61,7 +61,7 @@ task('genesis-l1', 'create a genesis config')
} }
} }
const timestamp = hre.deployConfig.startingTimestamp const timestamp = hre.deployConfig.l1GenesisTimestamp
if (timestamp === undefined) { if (timestamp === undefined) {
throw new Error('Must configure starting block timestamp') throw new Error('Must configure starting block timestamp')
} }
......
...@@ -462,8 +462,12 @@ task('genesis-l2', 'create a genesis config') ...@@ -462,8 +462,12 @@ task('genesis-l2', 'create a genesis config')
} }
} }
const portal = await hre.deployments.get('OptimismPortalProxy') const l1StartingBlock = await l1.getBlock(deployConfig.l1StartingBlockTag)
const l1StartingBlock = await l1.getBlock(portal.receipt.blockHash) if (l1StartingBlock === null) {
throw new Error(
`Cannot fetch block tag ${deployConfig.l1StartingBlockTag}`
)
}
if (l1StartingBlock === null) { if (l1StartingBlock === null) {
console.log(`Unable to fetch L1 starting timestamp`) console.log(`Unable to fetch L1 starting timestamp`)
......
...@@ -23,13 +23,18 @@ task('rollup-config', 'create a genesis config') ...@@ -23,13 +23,18 @@ task('rollup-config', 'create a genesis config')
const l2Genesis = await l2.getBlock('earliest') const l2Genesis = await l2.getBlock('earliest')
const portal = await hre.deployments.get('OptimismPortalProxy') const portal = await hre.deployments.get('OptimismPortalProxy')
const l1StartingBlock = await l1.getBlock(portal.receipt.blockHash) const l1StartingBlock = await l1.getBlock(deployConfig.l1StartingBlockTag)
if (l1StartingBlock === null) {
throw new Error(
`Cannot fetch block tag ${deployConfig.l1StartingBlockTag}`
)
}
const config: OpNodeConfig = { const config: OpNodeConfig = {
genesis: { genesis: {
l1: { l1: {
hash: portal.receipt.blockHash, hash: l1StartingBlock.hash,
number: portal.receipt.blockNumber, number: l1StartingBlock.number,
}, },
l2: { l2: {
hash: l2Genesis.hash, hash: l2Genesis.hash,
......
...@@ -4,7 +4,5 @@ ...@@ -4,7 +4,5 @@
"rootDir": "./src", "rootDir": "./src",
"outDir": "./dist" "outDir": "./dist"
}, },
"include": [ "include": ["src/**/*"]
"src/**/*"
]
} }
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