Commit 659b43f9 authored by Matthew Slipper's avatar Matthew Slipper Committed by GitHub

Merge pull request #4969 from ethereum-optimism/develop

Develop -> Master
parents 29c26522 aa675751
---
'@eth-optimism/ci-builder': patch
---
Bump foundry to edf15abd648bb96e2bcee342c1d72ec7d1066cd1
---
"@eth-optimism/indexer": patch
---
build(deps): bump golang.org/x/text from 0.3.7 to 0.3.8 in /indexer
---
'@eth-optimism/ci-builder': minor
'@eth-optimism/contracts-bedrock': patch
---
Update foundry
...@@ -26,11 +26,9 @@ jobs: ...@@ -26,11 +26,9 @@ jobs:
replica-healthcheck: ${{ steps.packages.outputs.replica-healthcheck }} replica-healthcheck: ${{ steps.packages.outputs.replica-healthcheck }}
hardhat-node: ${{ steps.packages.outputs.hardhat-node }} hardhat-node: ${{ steps.packages.outputs.hardhat-node }}
canary-docker-tag: ${{ steps.docker-image-name.outputs.canary-docker-tag }} canary-docker-tag: ${{ steps.docker-image-name.outputs.canary-docker-tag }}
proxyd: ${{ steps.packages.outputs.proxyd }}
op-exporter: ${{ steps.packages.outputs.op-exporter }} op-exporter: ${{ steps.packages.outputs.op-exporter }}
l2geth-exporter: ${{ steps.packages.outputs.l2geth-exporter }} l2geth-exporter: ${{ steps.packages.outputs.l2geth-exporter }}
batch-submitter-service: ${{ steps.packages.outputs.batch-submitter-service }} batch-submitter-service: ${{ steps.packages.outputs.batch-submitter-service }}
indexer: ${{ steps.packages.outputs.indexer }}
endpoint-monitor: ${{ steps.packages.outputs.l2geth-exporter }} endpoint-monitor: ${{ steps.packages.outputs.l2geth-exporter }}
steps: steps:
...@@ -393,43 +391,6 @@ jobs: ...@@ -393,43 +391,6 @@ jobs:
push: true push: true
tags: ethereumoptimism/replica-healthcheck:${{ needs.canary-publish.outputs.canary-docker-tag }} tags: ethereumoptimism/replica-healthcheck:${{ needs.canary-publish.outputs.canary-docker-tag }}
proxyd:
name: Publish proxyd Version ${{ needs.canary-publish.outputs.canary-docker-tag }}
needs: canary-publish
if: needs.canary-publish.outputs.proxyd != ''
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_USERNAME }}
password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_SECRET }}
- name: Set build args
id: build_args
run: |
echo ::set-output name=GITDATE::"$(date +%d-%m-%Y)"
echo ::set-output name=GITVERSION::$(jq -r .version ./proxyd/package.json)
echo ::set-output name=GITCOMMIT::"$GITHUB_SHA"
- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
file: ./proxyd/Dockerfile
push: true
tags: ethereumoptimism/proxyd:${{ needs.canary-publish.outputs.proxyd }}
build-args: |
GITDATE=${{ steps.build_args.outputs.GITDATE }}
GITCOMMIT=${{ steps.build_args.outputs.GITCOMMIT }}
GITVERSION=${{ steps.build_args.outputs.GITVERSION }}
op-exporter: op-exporter:
name: Publish op-exporter Version ${{ needs.canary-publish.outputs.canary-docker-tag }} name: Publish op-exporter Version ${{ needs.canary-publish.outputs.canary-docker-tag }}
needs: canary-publish needs: canary-publish
...@@ -530,43 +491,6 @@ jobs: ...@@ -530,43 +491,6 @@ jobs:
push: true push: true
tags: ethereumoptimism/batch-submitter-service:${{ needs.canary-publish.outputs.batch-submitter-service }} tags: ethereumoptimism/batch-submitter-service:${{ needs.canary-publish.outputs.batch-submitter-service }}
indexer:
name: Publish indexer Version ${{ needs.canary-publish.outputs.canary-docker-tag }}
needs: canary-publish
if: needs.canary-publish.outputs.indexer != ''
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_USERNAME }}
password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_SECRET }}
- name: Set build args
id: build_args
run: |
echo ::set-output name=GITDATE::"$(date +%d-%m-%Y)"
echo ::set-output name=GITVERSION::$(jq -r .version ./indexer/package.json)
echo ::set-output name=GITCOMMIT::"$GITHUB_SHA"
- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
file: ./indexer/Dockerfile
push: true
tags: ethereumoptimism/indexer:${{ needs.canary-publish.outputs.indexer }}
build-args: |
GITDATE=${{ steps.build_args.outputs.GITDATE }}
GITCOMMIT=${{ steps.build_args.outputs.GITCOMMIT }}
GITVERSION=${{ steps.build_args.outputs.GITVERSION }}
endpoint-monitor: endpoint-monitor:
name: Publish endpoint-monitor Version ${{ needs.canary-publish.outputs.canary-docker-tag }} name: Publish endpoint-monitor Version ${{ needs.canary-publish.outputs.canary-docker-tag }}
needs: canary-publish needs: canary-publish
......
...@@ -21,12 +21,10 @@ jobs: ...@@ -21,12 +21,10 @@ jobs:
balance-monitor: ${{ steps.packages.outputs.balance-monitor }} balance-monitor: ${{ steps.packages.outputs.balance-monitor }}
gas-oracle: ${{ steps.packages.outputs.gas-oracle }} gas-oracle: ${{ steps.packages.outputs.gas-oracle }}
replica-healthcheck: ${{ steps.packages.outputs.replica-healthcheck }} replica-healthcheck: ${{ steps.packages.outputs.replica-healthcheck }}
proxyd: ${{ steps.packages.outputs.proxyd }}
hardhat-node: ${{ steps.packages.outputs.hardhat-node }} hardhat-node: ${{ steps.packages.outputs.hardhat-node }}
op-exporter: ${{ steps.packages.outputs.op-exporter }} op-exporter: ${{ steps.packages.outputs.op-exporter }}
l2geth-exporter: ${{ steps.packages.outputs.l2geth-exporter }} l2geth-exporter: ${{ steps.packages.outputs.l2geth-exporter }}
batch-submitter-service: ${{ steps.packages.outputs.batch-submitter-service }} batch-submitter-service: ${{ steps.packages.outputs.batch-submitter-service }}
indexer: ${{ steps.packages.outputs.indexer }}
ci-builder: ${{ steps.packages.outputs.ci-builder }} ci-builder: ${{ steps.packages.outputs.ci-builder }}
foundry: ${{ steps.packages.outputs.foundry }} foundry: ${{ steps.packages.outputs.foundry }}
endpoint-monitor: ${{ steps.packages.outputs.endpoint-monitor }} endpoint-monitor: ${{ steps.packages.outputs.endpoint-monitor }}
...@@ -210,43 +208,6 @@ jobs: ...@@ -210,43 +208,6 @@ jobs:
push: true push: true
tags: ethereumoptimism/foundry:${{ needs.release.outputs.foundry }},ethereumoptimism/foundry:latest tags: ethereumoptimism/foundry:${{ needs.release.outputs.foundry }},ethereumoptimism/foundry:latest
proxyd:
name: Publish proxyd Version ${{ needs.release.outputs.proxyd }}
needs: release
if: needs.release.outputs.proxyd != ''
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_USERNAME }}
password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_SECRET }}
- name: Set build args
id: build_args
run: |
echo ::set-output name=GITDATE::"$(date +%d-%m-%Y)"
echo ::set-output name=GITVERSION::$(jq -r .version ./proxyd/package.json)
echo ::set-output name=GITCOMMIT::"$GITHUB_SHA"
- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
file: ./proxyd/Dockerfile
push: true
tags: ethereumoptimism/proxyd:${{ needs.release.outputs.proxyd }},ethereumoptimism/proxyd:latest
build-args: |
GITDATE=${{ steps.build_args.outputs.GITDATE }}
GITCOMMIT=${{ steps.build_args.outputs.GITCOMMIT }}
GITVERSION=${{ steps.build_args.outputs.GITVERSION }}
l2geth-exporter: l2geth-exporter:
name: Publish l2geth-exporter Version ${{ needs.release.outputs.l2geth-exporter}} name: Publish l2geth-exporter Version ${{ needs.release.outputs.l2geth-exporter}}
needs: release needs: release
...@@ -590,43 +551,6 @@ jobs: ...@@ -590,43 +551,6 @@ jobs:
push: true push: true
tags: ethereumoptimism/batch-submitter-service:${{ needs.release.outputs.batch-submitter-service }},ethereumoptimism/batch-submitter-service:latest tags: ethereumoptimism/batch-submitter-service:${{ needs.release.outputs.batch-submitter-service }},ethereumoptimism/batch-submitter-service:latest
indexer:
name: Publish Indexer Version ${{ needs.release.outputs.indexer }}
needs: release
if: needs.release.outputs.indexer != ''
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_USERNAME }}
password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN_SECRET }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Set build args
id: build_args
run: |
echo ::set-output name=GITDATE::"$(date +%d-%m-%Y)"
echo ::set-output name=GITVERSION::$(jq -r .version ./indexer/package.json)
echo ::set-output name=GITCOMMIT::"$GITHUB_SHA"
- name: Publish Indexer
uses: docker/build-push-action@v2
with:
context: .
file: ./indexer/Dockerfile
push: true
tags: ethereumoptimism/indexer:${{ needs.release.outputs.indexer }},ethereumoptimism/indexer:latest
build-args: |
GITDATE=${{ steps.build_args.outputs.GITDATE }}
GITCOMMIT=${{ steps.build_args.outputs.GITCOMMIT }}
GITVERSION=${{ steps.build_args.outputs.GITVERSION }}
endpoint-monitor: endpoint-monitor:
name: Publish endpoint-monitor Version ${{ needs.release.outputs.endpoint-monitor}} name: Publish endpoint-monitor Version ${{ needs.release.outputs.endpoint-monitor}}
needs: release needs: release
......
...@@ -152,7 +152,7 @@ require ( ...@@ -152,7 +152,7 @@ require (
golang.org/x/sync v0.1.0 // indirect golang.org/x/sync v0.1.0 // indirect
golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43 // indirect golang.org/x/sys v0.0.0-20221013171732-95e765b1cc43 // indirect
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 // indirect golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 // indirect
golang.org/x/text v0.3.7 // indirect golang.org/x/text v0.3.8 // indirect
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 // indirect golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 // indirect
golang.org/x/tools v0.1.12 // indirect golang.org/x/tools v0.1.12 // indirect
google.golang.org/protobuf v1.28.1 // indirect google.golang.org/protobuf v1.28.1 // indirect
......
...@@ -1404,8 +1404,9 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= ...@@ -1404,8 +1404,9 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
......
{
"name": "@eth-optimism/indexer",
"version": "0.7.0",
"private": true,
"license": "MIT"
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
...@@ -192,7 +192,7 @@ func MigrateDB(ldb ethdb.Database, config *DeployConfig, l1Block *types.Block, m ...@@ -192,7 +192,7 @@ func MigrateDB(ldb ethdb.Database, config *DeployConfig, l1Block *types.Block, m
} }
// Finally we migrate the balances held inside the LegacyERC20ETH contract into the state trie. // Finally we migrate the balances held inside the LegacyERC20ETH contract into the state trie.
// Note that we do NOT delete the balances from the LegacyERC20ETH contract. // We also delete the balances from the LegacyERC20ETH contract.
log.Info("Starting to migrate ERC20 ETH") log.Info("Starting to migrate ERC20 ETH")
err = ether.MigrateLegacyETH(db, addrs, int(config.L1ChainID), noCheck) err = ether.MigrateLegacyETH(db, addrs, int(config.L1ChainID), noCheck)
if err != nil { if err != nil {
......
...@@ -123,7 +123,8 @@ func BuildL1DeveloperGenesis(config *DeployConfig) (*core.Genesis, error) { ...@@ -123,7 +123,8 @@ func BuildL1DeveloperGenesis(config *DeployConfig) (*core.Genesis, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
data, err = portalABI.Pack("initialize") // Initialize the OptimismPortal without being paused
data, err = portalABI.Pack("initialize", false)
if err != nil { if err != nil {
return nil, fmt.Errorf("cannot abi encode initialize for OptimismPortal: %w", err) return nil, fmt.Errorf("cannot abi encode initialize for OptimismPortal: %w", err)
} }
...@@ -290,9 +291,15 @@ func deployL1Contracts(config *DeployConfig, backend *backends.SimulatedBackend) ...@@ -290,9 +291,15 @@ func deployL1Contracts(config *DeployConfig, backend *backends.SimulatedBackend)
}, },
}, },
{ {
// The implementation of the OptimismPortal is deployed
// as being paused to prevent invalid usage of the network
// as only the proxy should be used
Name: "OptimismPortal", Name: "OptimismPortal",
Args: []interface{}{ Args: []interface{}{
predeploys.DevL2OutputOracleAddr,
config.FinalSystemOwner,
uint642Big(config.FinalizationPeriodSeconds), uint642Big(config.FinalizationPeriodSeconds),
true, // _paused
}, },
}, },
{ {
...@@ -354,8 +361,10 @@ func l1Deployer(backend *backends.SimulatedBackend, opts *bind.TransactOpts, dep ...@@ -354,8 +361,10 @@ func l1Deployer(backend *backends.SimulatedBackend, opts *bind.TransactOpts, dep
_, tx, _, err = bindings.DeployOptimismPortal( _, tx, _, err = bindings.DeployOptimismPortal(
opts, opts,
backend, backend,
predeploys.DevL2OutputOracleAddr, deployment.Args[0].(common.Address),
deployment.Args[0].(*big.Int), deployment.Args[2].(*big.Int),
deployment.Args[1].(common.Address),
deployment.Args[3].(bool),
) )
case "L1CrossDomainMessenger": case "L1CrossDomainMessenger":
_, tx, _, err = bindings.DeployL1CrossDomainMessenger( _, tx, _, err = bindings.DeployL1CrossDomainMessenger(
......
package op_e2e
import (
"context"
"errors"
"fmt"
"math/big"
"reflect"
"testing"
"github.com/ethereum-optimism/optimism/op-chain-ops/genesis"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils"
"github.com/ethereum-optimism/optimism/op-node/client"
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum-optimism/optimism/op-node/sources"
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
gn "github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
"github.com/stretchr/testify/require"
)
var (
// ErrForkChoiceUpdatedNotValid is returned when a forkChoiceUpdated returns a status other than Valid
ErrForkChoiceUpdatedNotValid = errors.New("forkChoiceUpdated status was not valid")
// ErrNewPayloadNotValid is returned when a newPayload call returns a status other than Valid, indicating the new block is invalid
ErrNewPayloadNotValid = errors.New("newPayload status was not valid")
)
// OpGeth is an actor that functions as a l2 op-geth node
// It provides useful functions for advancing and querying the chain
type OpGeth struct {
node *gn.Node
l2Engine *sources.EngineClient
L2Client *ethclient.Client
SystemConfig eth.SystemConfig
L1ChainConfig *params.ChainConfig
L2ChainConfig *params.ChainConfig
L1Head eth.BlockInfo
L2Head *eth.ExecutionPayload
sequenceNum uint64
}
func NewOpGeth(t *testing.T, ctx context.Context, cfg *SystemConfig) (*OpGeth, error) {
logger := testlog.Logger(t, log.LvlCrit)
l1Genesis, err := genesis.BuildL1DeveloperGenesis(cfg.DeployConfig)
require.Nil(t, err)
l1Block := l1Genesis.ToBlock()
l2Genesis, err := genesis.BuildL2DeveloperGenesis(cfg.DeployConfig, l1Block)
require.Nil(t, err)
l2GenesisBlock := l2Genesis.ToBlock()
rollupGenesis := rollup.Genesis{
L1: eth.BlockID{
Hash: l1Block.Hash(),
Number: l1Block.NumberU64(),
},
L2: eth.BlockID{
Hash: l2GenesisBlock.Hash(),
Number: l2GenesisBlock.NumberU64(),
},
L2Time: l2GenesisBlock.Time(),
SystemConfig: e2eutils.SystemConfigFromDeployConfig(cfg.DeployConfig),
}
node, _, err := initL2Geth("l2", big.NewInt(int64(cfg.DeployConfig.L2ChainID)), l2Genesis, cfg.JWTFilePath)
require.Nil(t, err)
require.Nil(t, node.Start())
auth := rpc.WithHTTPAuth(gn.NewJWTAuth(cfg.JWTSecret))
l2Node, err := client.NewRPC(ctx, logger, node.WSAuthEndpoint(), auth)
require.Nil(t, err)
// Finally create the engine client
l2Engine, err := sources.NewEngineClient(
l2Node,
logger,
nil,
sources.EngineClientDefaultConfig(&rollup.Config{Genesis: rollupGenesis}),
)
require.Nil(t, err)
l2Client, err := ethclient.Dial(node.HTTPEndpoint())
require.Nil(t, err)
genesisPayload, err := eth.BlockAsPayload(l2GenesisBlock)
require.Nil(t, err)
return &OpGeth{
node: node,
L2Client: l2Client,
l2Engine: l2Engine,
SystemConfig: rollupGenesis.SystemConfig,
L1ChainConfig: l1Genesis.Config,
L2ChainConfig: l2Genesis.Config,
L1Head: l1Block,
L2Head: genesisPayload,
}, nil
}
func (d *OpGeth) Close() {
_ = d.node.Close()
d.l2Engine.Close()
d.L2Client.Close()
}
// AddL2Block Appends a new L2 block to the current chain including the specified transactions
// The L1Info transaction is automatically prepended to the created block
func (d *OpGeth) AddL2Block(ctx context.Context, txs ...*types.Transaction) (*eth.ExecutionPayload, error) {
attrs, err := d.CreatePayloadAttributes(txs...)
if err != nil {
return nil, err
}
res, err := d.StartBlockBuilding(ctx, attrs)
if err != nil {
return nil, err
}
payload, err := d.l2Engine.GetPayload(ctx, *res.PayloadID)
if err != nil {
return nil, err
}
if !reflect.DeepEqual(payload.Transactions, attrs.Transactions) {
return nil, errors.New("required transactions were not included")
}
status, err := d.l2Engine.NewPayload(ctx, payload)
if err != nil {
return nil, err
}
if status.Status != eth.ExecutionValid {
return nil, fmt.Errorf("%w: %s", ErrNewPayloadNotValid, status.Status)
}
fc := eth.ForkchoiceState{
HeadBlockHash: payload.BlockHash,
SafeBlockHash: payload.BlockHash,
}
res, err = d.l2Engine.ForkchoiceUpdate(ctx, &fc, nil)
if err != nil {
return nil, err
}
if res.PayloadStatus.Status != eth.ExecutionValid {
return nil, fmt.Errorf("%w: %s", ErrForkChoiceUpdatedNotValid, res.PayloadStatus.Status)
}
d.L2Head = payload
d.sequenceNum = d.sequenceNum + 1
return payload, nil
}
// StartBlockBuilding begins block building for the specified PayloadAttributes by sending a engine_forkChoiceUpdated call.
// The current L2Head is used as the parent of the new block.
// ErrForkChoiceUpdatedNotValid is returned if the forkChoiceUpdate call returns a status other than valid.
func (d *OpGeth) StartBlockBuilding(ctx context.Context, attrs *eth.PayloadAttributes) (*eth.ForkchoiceUpdatedResult, error) {
fc := eth.ForkchoiceState{
HeadBlockHash: d.L2Head.BlockHash,
SafeBlockHash: d.L2Head.BlockHash,
}
res, err := d.l2Engine.ForkchoiceUpdate(ctx, &fc, attrs)
if err != nil {
return nil, err
}
if res.PayloadStatus.Status != eth.ExecutionValid {
return nil, fmt.Errorf("%w: %s", ErrForkChoiceUpdatedNotValid, res.PayloadStatus.Status)
}
if res.PayloadID == nil {
return nil, errors.New("forkChoiceUpdated returned nil PayloadID")
}
return res, nil
}
// CreatePayloadAttributes creates a valid PayloadAttributes containing a L1Info deposit transaction followed by the supplied transactions.
func (d *OpGeth) CreatePayloadAttributes(txs ...*types.Transaction) (*eth.PayloadAttributes, error) {
timestamp := d.L2Head.Timestamp + 2
l1Info, err := derive.L1InfoDepositBytes(d.sequenceNum, d.L1Head, d.SystemConfig, false)
if err != nil {
return nil, err
}
var txBytes []hexutil.Bytes
txBytes = append(txBytes, l1Info)
for _, tx := range txs {
bin, err := tx.MarshalBinary()
if err != nil {
return nil, fmt.Errorf("tx marshalling failed: %w", err)
}
txBytes = append(txBytes, bin)
}
attrs := eth.PayloadAttributes{
Timestamp: timestamp,
Transactions: txBytes,
NoTxPool: true,
GasLimit: (*eth.Uint64Quantity)(&d.SystemConfig.GasLimit),
}
return &attrs, nil
}
...@@ -6,91 +6,29 @@ import ( ...@@ -6,91 +6,29 @@ import (
"testing" "testing"
"time" "time"
"github.com/ethereum-optimism/optimism/op-chain-ops/genesis"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils"
"github.com/ethereum-optimism/optimism/op-node/client"
"github.com/ethereum-optimism/optimism/op-node/eth" "github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum-optimism/optimism/op-node/sources"
"github.com/ethereum-optimism/optimism/op-node/testlog"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
gn "github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
// TestMissingGasLimit tests that op-geth cannot build a block without gas limit while optimism is active in the chain config. // TestMissingGasLimit tests that op-geth cannot build a block without gas limit while optimism is active in the chain config.
func TestMissingGasLimit(t *testing.T) { func TestMissingGasLimit(t *testing.T) {
// Setup an L2 EE and create a client connection to the engine.
// We also need to setup a L1 Genesis to create the rollup genesis.
log := testlog.Logger(t, log.LvlCrit)
cfg := DefaultSystemConfig(t) cfg := DefaultSystemConfig(t)
cfg.DeployConfig.FundDevAccounts = false cfg.DeployConfig.FundDevAccounts = false
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
l1Genesis, err := genesis.BuildL1DeveloperGenesis(cfg.DeployConfig)
require.Nil(t, err)
l1Block := l1Genesis.ToBlock()
l2Genesis, err := genesis.BuildL2DeveloperGenesis(cfg.DeployConfig, l1Block)
require.Nil(t, err)
l2GenesisBlock := l2Genesis.ToBlock()
rollupGenesis := rollup.Genesis{
L1: eth.BlockID{
Hash: l1Block.Hash(),
Number: l1Block.NumberU64(),
},
L2: eth.BlockID{
Hash: l2GenesisBlock.Hash(),
Number: l2GenesisBlock.NumberU64(),
},
L2Time: l2GenesisBlock.Time(),
SystemConfig: e2eutils.SystemConfigFromDeployConfig(cfg.DeployConfig),
}
node, _, err := initL2Geth("l2", big.NewInt(int64(cfg.DeployConfig.L2ChainID)), l2Genesis, writeDefaultJWT(t))
require.Nil(t, err)
require.Nil(t, node.Start())
defer node.Close()
auth := rpc.WithHTTPAuth(gn.NewJWTAuth(testingJWTSecret))
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel() defer cancel()
l2Node, err := client.NewRPC(ctx, log, node.WSAuthEndpoint(), auth) opGeth, err := NewOpGeth(t, ctx, &cfg)
require.Nil(t, err) require.NoError(t, err)
defer opGeth.Close()
// Finally create the engine client
client, err := sources.NewEngineClient(
l2Node,
log,
nil,
sources.EngineClientDefaultConfig(&rollup.Config{Genesis: rollupGenesis}),
)
require.Nil(t, err)
attrs := eth.PayloadAttributes{ attrs, err := opGeth.CreatePayloadAttributes()
Timestamp: hexutil.Uint64(l2GenesisBlock.Time() + 2), require.NoError(t, err)
Transactions: []hexutil.Bytes{}, // Remove the GasLimit from the otherwise valid attributes
NoTxPool: true, attrs.GasLimit = nil
GasLimit: nil, // no gas limit
}
ctx, cancel = context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
fc := eth.ForkchoiceState{ res, err := opGeth.StartBlockBuilding(ctx, attrs)
HeadBlockHash: l2GenesisBlock.Hash(),
SafeBlockHash: l2GenesisBlock.Hash(),
}
res, err := client.ForkchoiceUpdate(ctx, &fc, &attrs)
require.ErrorIs(t, err, eth.InputError{}) require.ErrorIs(t, err, eth.InputError{})
require.Equal(t, eth.InvalidPayloadAttributes, err.(eth.InputError).Code) require.Equal(t, eth.InvalidPayloadAttributes, err.(eth.InputError).Code)
require.Nil(t, res) require.Nil(t, res)
...@@ -99,109 +37,36 @@ func TestMissingGasLimit(t *testing.T) { ...@@ -99,109 +37,36 @@ func TestMissingGasLimit(t *testing.T) {
// TestInvalidDepositInFCU runs an invalid deposit through a FCU/GetPayload/NewPayload/FCU set of calls. // TestInvalidDepositInFCU runs an invalid deposit through a FCU/GetPayload/NewPayload/FCU set of calls.
// This tests that deposits must always allow the block to be built even if they are invalid. // This tests that deposits must always allow the block to be built even if they are invalid.
func TestInvalidDepositInFCU(t *testing.T) { func TestInvalidDepositInFCU(t *testing.T) {
// Setup an L2 EE and create a client connection to the engine.
// We also need to setup a L1 Genesis to create the rollup genesis.
log := testlog.Logger(t, log.LvlCrit)
cfg := DefaultSystemConfig(t) cfg := DefaultSystemConfig(t)
cfg.DeployConfig.FundDevAccounts = false cfg.DeployConfig.FundDevAccounts = false
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
l1Genesis, err := genesis.BuildL1DeveloperGenesis(cfg.DeployConfig)
require.Nil(t, err)
l1Block := l1Genesis.ToBlock()
l2Genesis, err := genesis.BuildL2DeveloperGenesis(cfg.DeployConfig, l1Block)
require.Nil(t, err)
l2GenesisBlock := l2Genesis.ToBlock()
rollupGenesis := rollup.Genesis{
L1: eth.BlockID{
Hash: l1Block.Hash(),
Number: l1Block.NumberU64(),
},
L2: eth.BlockID{
Hash: l2GenesisBlock.Hash(),
Number: l2GenesisBlock.NumberU64(),
},
L2Time: l2GenesisBlock.Time(),
SystemConfig: e2eutils.SystemConfigFromDeployConfig(cfg.DeployConfig),
}
node, _, err := initL2Geth("l2", big.NewInt(int64(cfg.DeployConfig.L2ChainID)), l2Genesis, writeDefaultJWT(t))
require.Nil(t, err)
require.Nil(t, node.Start())
defer node.Close()
auth := rpc.WithHTTPAuth(gn.NewJWTAuth(testingJWTSecret))
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel() defer cancel()
l2Node, err := client.NewRPC(ctx, log, node.WSAuthEndpoint(), auth) opGeth, err := NewOpGeth(t, ctx, &cfg)
require.Nil(t, err) require.NoError(t, err)
defer opGeth.Close()
// Finally create the engine client
client, err := sources.NewEngineClient(
l2Node,
log,
nil,
sources.EngineClientDefaultConfig(&rollup.Config{Genesis: rollupGenesis}),
)
require.Nil(t, err)
// Create the test data (L1 Info Tx and then always failing deposit)
l1Info, err := derive.L1InfoDepositBytes(1, l1Block, rollupGenesis.SystemConfig)
require.Nil(t, err)
// Create a deposit from alice that will always fail (not enough funds) // Create a deposit from alice that will always fail (not enough funds)
fromAddr := cfg.Secrets.Addresses().Alice fromAddr := cfg.Secrets.Addresses().Alice
l2Client, err := ethclient.Dial(node.HTTPEndpoint()) balance, err := opGeth.L2Client.BalanceAt(ctx, fromAddr, nil)
require.Nil(t, err)
balance, err := l2Client.BalanceAt(ctx, fromAddr, nil)
require.Nil(t, err) require.Nil(t, err)
require.Equal(t, 0, balance.Cmp(common.Big0)) require.Equal(t, 0, balance.Cmp(common.Big0))
badDepositTx := types.NewTx(&types.DepositTx{ badDepositTx := types.NewTx(&types.DepositTx{
// TODO: Source Hash SourceHash: opGeth.L1Head.Hash(),
From: fromAddr, From: fromAddr,
To: &fromAddr, // send it to ourselves To: &fromAddr, // send it to ourselves
Value: big.NewInt(params.Ether), Value: big.NewInt(params.Ether),
Gas: 25000, Gas: 25000,
IsSystemTransaction: false, IsSystemTransaction: false,
}) })
badDeposit, err := badDepositTx.MarshalBinary()
require.Nil(t, err)
attrs := eth.PayloadAttributes{
Timestamp: hexutil.Uint64(l2GenesisBlock.Time() + 2),
Transactions: []hexutil.Bytes{l1Info, badDeposit},
NoTxPool: true,
GasLimit: (*eth.Uint64Quantity)(&rollupGenesis.SystemConfig.GasLimit),
}
// Go through the flow of FCU, GetPayload, NewPayload, FCU
// We are inserting a block with an invalid deposit. // We are inserting a block with an invalid deposit.
// The invalid deposit should still remain in the block. // The invalid deposit should still remain in the block.
ctx, cancel = context.WithTimeout(context.Background(), 2*time.Second) _, err = opGeth.AddL2Block(ctx, badDepositTx)
defer cancel() require.NoError(t, err)
fc := eth.ForkchoiceState{ // Deposit tx was included, but Alice still shouldn't have any ETH
HeadBlockHash: l2GenesisBlock.Hash(), balance, err = opGeth.L2Client.BalanceAt(ctx, fromAddr, nil)
SafeBlockHash: l2GenesisBlock.Hash(),
}
res, err := client.ForkchoiceUpdate(ctx, &fc, &attrs)
require.Nil(t, err) require.Nil(t, err)
require.Equal(t, eth.ExecutionValid, res.PayloadStatus.Status) require.Equal(t, 0, balance.Cmp(common.Big0))
require.NotNil(t, res.PayloadID)
payload, err := client.GetPayload(ctx, *res.PayloadID)
require.Nil(t, err)
require.NotNil(t, payload)
require.Equal(t, payload.Transactions, attrs.Transactions) // Ensure we don't drop the transactions
status, err := client.NewPayload(ctx, payload)
require.Nil(t, err)
require.Equal(t, eth.ExecutionValid, status.Status)
fc.HeadBlockHash = payload.BlockHash
res, err = client.ForkchoiceUpdate(ctx, &fc, nil)
require.Nil(t, err)
require.Equal(t, eth.ExecutionValid, res.PayloadStatus.Status)
} }
...@@ -34,9 +34,9 @@ require ( ...@@ -34,9 +34,9 @@ require (
github.com/prometheus/procfs v0.7.3 // indirect github.com/prometheus/procfs v0.7.3 // indirect
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c // indirect golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c // indirect
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5 // indirect golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
golang.org/x/text v0.3.7 // indirect golang.org/x/text v0.3.8 // indirect
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 // indirect golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 // indirect
google.golang.org/appengine v1.6.6 // indirect google.golang.org/appengine v1.6.6 // indirect
google.golang.org/protobuf v1.27.1 // indirect google.golang.org/protobuf v1.27.1 // indirect
......
...@@ -635,8 +635,8 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w ...@@ -635,8 +635,8 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5 h1:y/woIyUBFbpQGKS0u1aHF/40WUDnek3fPOyD08H5Vng= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
...@@ -650,8 +650,9 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= ...@@ -650,8 +650,9 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
......
...@@ -73,6 +73,14 @@ var NetworksByName = map[string]rollup.Config{ ...@@ -73,6 +73,14 @@ var NetworksByName = map[string]rollup.Config{
"goerli": Goerli, "goerli": Goerli,
} }
var L2ChainIDToNetworkName = func() map[string]string {
out := make(map[string]string)
for name, netCfg := range NetworksByName {
out[netCfg.L2ChainID.String()] = name
}
return out
}()
func AvailableNetworks() []string { func AvailableNetworks() []string {
var networks []string var networks []string
for name := range NetworksByName { for name := range NetworksByName {
......
...@@ -109,7 +109,7 @@ func RollupNodeMain(ctx *cli.Context) error { ...@@ -109,7 +109,7 @@ func RollupNodeMain(ctx *cli.Context) error {
log.Error("Unable to create the rollup node", "error", err) log.Error("Unable to create the rollup node", "error", err)
return err return err
} }
log.Info("Starting rollup node") log.Info("Starting rollup node", "version", VersionWithMeta)
if err := n.Start(context.Background()); err != nil { if err := n.Start(context.Background()); err != nil {
log.Error("Unable to start rollup node", "error", err) log.Error("Unable to start rollup node", "error", err)
......
...@@ -13,6 +13,7 @@ import ( ...@@ -13,6 +13,7 @@ import (
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/op-node/chaincfg"
"github.com/ethereum-optimism/optimism/op-node/client" "github.com/ethereum-optimism/optimism/op-node/client"
"github.com/ethereum-optimism/optimism/op-node/eth" "github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-node/metrics" "github.com/ethereum-optimism/optimism/op-node/metrics"
...@@ -61,9 +62,11 @@ func New(ctx context.Context, cfg *Config, log log.Logger, snapshotLog log.Logge ...@@ -61,9 +62,11 @@ func New(ctx context.Context, cfg *Config, log log.Logger, snapshotLog log.Logge
// not a context leak, gossipsub is closed with a context. // not a context leak, gossipsub is closed with a context.
n.resourcesCtx, n.resourcesClose = context.WithCancel(context.Background()) n.resourcesCtx, n.resourcesClose = context.WithCancel(context.Background())
log.Info("rollup config:\n" + cfg.Rollup.Description(chaincfg.L2ChainIDToNetworkName))
err := n.init(ctx, cfg, snapshotLog) err := n.init(ctx, cfg, snapshotLog)
if err != nil { if err != nil {
log.Error("Error intializing the rollup node", "err", err) log.Error("Error initializing the rollup node", "err", err)
// ensure we always close the node resources if we fail to initialize the node. // ensure we always close the node resources if we fail to initialize the node.
if closeErr := n.Close(); closeErr != nil { if closeErr := n.Close(); closeErr != nil {
return nil, multierror.Append(err, closeErr) return nil, multierror.Append(err, closeErr)
......
...@@ -100,7 +100,7 @@ func (ba *FetchingAttributesBuilder) PreparePayloadAttributes(ctx context.Contex ...@@ -100,7 +100,7 @@ func (ba *FetchingAttributesBuilder) PreparePayloadAttributes(ctx context.Contex
l2Parent, nextL2Time, eth.ToBlockID(l1Info), l1Info.Time())) l2Parent, nextL2Time, eth.ToBlockID(l1Info), l1Info.Time()))
} }
l1InfoTx, err := L1InfoDepositBytes(seqNumber, l1Info, sysConfig) l1InfoTx, err := L1InfoDepositBytes(seqNumber, l1Info, sysConfig, ba.cfg.IsRegolith(nextL2Time))
if err != nil { if err != nil {
return nil, NewCriticalError(fmt.Errorf("failed to create l1InfoTx: %w", err)) return nil, NewCriticalError(fmt.Errorf("failed to create l1InfoTx: %w", err))
} }
......
...@@ -66,7 +66,7 @@ func TestAttributesQueue(t *testing.T) { ...@@ -66,7 +66,7 @@ func TestAttributesQueue(t *testing.T) {
l2Fetcher := &testutils.MockL2Client{} l2Fetcher := &testutils.MockL2Client{}
l2Fetcher.ExpectSystemConfigByL2Hash(safeHead.Hash, parentL1Cfg, nil) l2Fetcher.ExpectSystemConfigByL2Hash(safeHead.Hash, parentL1Cfg, nil)
l1InfoTx, err := L1InfoDepositBytes(safeHead.SequenceNumber+1, l1Info, expectedL1Cfg) l1InfoTx, err := L1InfoDepositBytes(safeHead.SequenceNumber+1, l1Info, expectedL1Cfg, false)
require.NoError(t, err) require.NoError(t, err)
attrs := eth.PayloadAttributes{ attrs := eth.PayloadAttributes{
Timestamp: eth.Uint64Quantity(safeHead.Time + cfg.BlockTime), Timestamp: eth.Uint64Quantity(safeHead.Time + cfg.BlockTime),
......
...@@ -113,7 +113,7 @@ func TestPreparePayloadAttributes(t *testing.T) { ...@@ -113,7 +113,7 @@ func TestPreparePayloadAttributes(t *testing.T) {
l1Info.InfoParentHash = l2Parent.L1Origin.Hash l1Info.InfoParentHash = l2Parent.L1Origin.Hash
l1Info.InfoNum = l2Parent.L1Origin.Number + 1 l1Info.InfoNum = l2Parent.L1Origin.Number + 1
epoch := l1Info.ID() epoch := l1Info.ID()
l1InfoTx, err := L1InfoDepositBytes(0, l1Info, testSysCfg) l1InfoTx, err := L1InfoDepositBytes(0, l1Info, testSysCfg, false)
require.NoError(t, err) require.NoError(t, err)
l1Fetcher.ExpectFetchReceipts(epoch.Hash, l1Info, nil, nil) l1Fetcher.ExpectFetchReceipts(epoch.Hash, l1Info, nil, nil)
attrBuilder := NewFetchingAttributesBuilder(cfg, l1Fetcher, l1CfgFetcher) attrBuilder := NewFetchingAttributesBuilder(cfg, l1Fetcher, l1CfgFetcher)
...@@ -150,7 +150,7 @@ func TestPreparePayloadAttributes(t *testing.T) { ...@@ -150,7 +150,7 @@ func TestPreparePayloadAttributes(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
epoch := l1Info.ID() epoch := l1Info.ID()
l1InfoTx, err := L1InfoDepositBytes(0, l1Info, testSysCfg) l1InfoTx, err := L1InfoDepositBytes(0, l1Info, testSysCfg, false)
require.NoError(t, err) require.NoError(t, err)
l2Txs := append(append(make([]eth.Data, 0), l1InfoTx), usedDepositTxs...) l2Txs := append(append(make([]eth.Data, 0), l1InfoTx), usedDepositTxs...)
...@@ -180,7 +180,7 @@ func TestPreparePayloadAttributes(t *testing.T) { ...@@ -180,7 +180,7 @@ func TestPreparePayloadAttributes(t *testing.T) {
l1Info.InfoNum = l2Parent.L1Origin.Number l1Info.InfoNum = l2Parent.L1Origin.Number
epoch := l1Info.ID() epoch := l1Info.ID()
l1InfoTx, err := L1InfoDepositBytes(l2Parent.SequenceNumber+1, l1Info, testSysCfg) l1InfoTx, err := L1InfoDepositBytes(l2Parent.SequenceNumber+1, l1Info, testSysCfg, false)
require.NoError(t, err) require.NoError(t, err)
l1Fetcher.ExpectInfoByHash(epoch.Hash, l1Info, nil) l1Fetcher.ExpectInfoByHash(epoch.Hash, l1Info, nil)
...@@ -195,6 +195,53 @@ func TestPreparePayloadAttributes(t *testing.T) { ...@@ -195,6 +195,53 @@ func TestPreparePayloadAttributes(t *testing.T) {
require.Equal(t, l1InfoTx, []byte(attrs.Transactions[0])) require.Equal(t, l1InfoTx, []byte(attrs.Transactions[0]))
require.True(t, attrs.NoTxPool) require.True(t, attrs.NoTxPool)
}) })
// Test that the payload attributes builder changes the deposit format based on L2-time-based regolith activation
t.Run("regolith", func(t *testing.T) {
testCases := []struct {
name string
l1Time uint64
l2ParentTime uint64
regolithTime uint64
regolith bool
}{
{"exactly", 900, 1000 - cfg.BlockTime, 1000, true},
{"almost", 900, 1000 - cfg.BlockTime - 1, 1000, false},
{"inactive", 700, 700, 1000, false},
{"l1 time before regolith", 1000, 1001, 1001, true},
{"l1 time way before regolith", 1000, 2000, 2000, true},
{"l1 time before regoltih and l2 after", 1000, 3000, 2000, true},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
cfgCopy := *cfg // copy, we are making regolith config modifications
cfg := &cfgCopy
rng := rand.New(rand.NewSource(1234))
l1Fetcher := &testutils.MockL1Source{}
defer l1Fetcher.AssertExpectations(t)
l2Parent := testutils.RandomL2BlockRef(rng)
cfg.RegolithTime = &tc.regolithTime
l2Parent.Time = tc.l2ParentTime
l1CfgFetcher := &testutils.MockL2Client{}
l1CfgFetcher.ExpectSystemConfigByL2Hash(l2Parent.Hash, testSysCfg, nil)
defer l1CfgFetcher.AssertExpectations(t)
l1Info := testutils.RandomBlockInfo(rng)
l1Info.InfoParentHash = l2Parent.L1Origin.Hash
l1Info.InfoNum = l2Parent.L1Origin.Number + 1
l1Info.InfoTime = tc.l1Time
epoch := l1Info.ID()
l1InfoTx, err := L1InfoDepositBytes(0, l1Info, testSysCfg, tc.regolith)
require.NoError(t, err)
l1Fetcher.ExpectFetchReceipts(epoch.Hash, l1Info, nil, nil)
attrBuilder := NewFetchingAttributesBuilder(cfg, l1Fetcher, l1CfgFetcher)
attrs, err := attrBuilder.PreparePayloadAttributes(context.Background(), l2Parent, epoch)
require.NoError(t, err)
require.Equal(t, l1InfoTx, []byte(attrs.Transactions[0]))
})
}
})
} }
func encodeDeposits(deposits []*types.DepositTx) (out []eth.Data, err error) { func encodeDeposits(deposits []*types.DepositTx) (out []eth.Data, err error) {
......
...@@ -26,6 +26,10 @@ var ( ...@@ -26,6 +26,10 @@ var (
L1BlockAddress = predeploys.L1BlockAddr L1BlockAddress = predeploys.L1BlockAddr
) )
const (
RegolithSystemTxGas = 1_000_000
)
// L1BlockInfo presents the information stored in a L1Block.setL1BlockValues call // L1BlockInfo presents the information stored in a L1Block.setL1BlockValues call
type L1BlockInfo struct { type L1BlockInfo struct {
Number uint64 Number uint64
...@@ -115,7 +119,7 @@ func L1InfoDepositTxData(data []byte) (L1BlockInfo, error) { ...@@ -115,7 +119,7 @@ func L1InfoDepositTxData(data []byte) (L1BlockInfo, error) {
// L1InfoDeposit creates a L1 Info deposit transaction based on the L1 block, // L1InfoDeposit creates a L1 Info deposit transaction based on the L1 block,
// and the L2 block-height difference with the start of the epoch. // and the L2 block-height difference with the start of the epoch.
func L1InfoDeposit(seqNumber uint64, block eth.BlockInfo, sysCfg eth.SystemConfig) (*types.DepositTx, error) { func L1InfoDeposit(seqNumber uint64, block eth.BlockInfo, sysCfg eth.SystemConfig, regolith bool) (*types.DepositTx, error) {
infoDat := L1BlockInfo{ infoDat := L1BlockInfo{
Number: block.NumberU64(), Number: block.NumberU64(),
Time: block.Time(), Time: block.Time(),
...@@ -137,7 +141,7 @@ func L1InfoDeposit(seqNumber uint64, block eth.BlockInfo, sysCfg eth.SystemConfi ...@@ -137,7 +141,7 @@ func L1InfoDeposit(seqNumber uint64, block eth.BlockInfo, sysCfg eth.SystemConfi
} }
// Set a very large gas limit with `IsSystemTransaction` to ensure // Set a very large gas limit with `IsSystemTransaction` to ensure
// that the L1 Attributes Transaction does not run out of gas. // that the L1 Attributes Transaction does not run out of gas.
return &types.DepositTx{ out := &types.DepositTx{
SourceHash: source.SourceHash(), SourceHash: source.SourceHash(),
From: L1InfoDepositerAddress, From: L1InfoDepositerAddress,
To: &L1BlockAddress, To: &L1BlockAddress,
...@@ -146,12 +150,18 @@ func L1InfoDeposit(seqNumber uint64, block eth.BlockInfo, sysCfg eth.SystemConfi ...@@ -146,12 +150,18 @@ func L1InfoDeposit(seqNumber uint64, block eth.BlockInfo, sysCfg eth.SystemConfi
Gas: 150_000_000, Gas: 150_000_000,
IsSystemTransaction: true, IsSystemTransaction: true,
Data: data, Data: data,
}, nil }
// With the regolith fork we disable the IsSystemTx functionality, and allocate real gas
if regolith {
out.IsSystemTransaction = false
out.Gas = RegolithSystemTxGas
}
return out, nil
} }
// L1InfoDepositBytes returns a serialized L1-info attributes transaction. // L1InfoDepositBytes returns a serialized L1-info attributes transaction.
func L1InfoDepositBytes(seqNumber uint64, l1Info eth.BlockInfo, sysCfg eth.SystemConfig) ([]byte, error) { func L1InfoDepositBytes(seqNumber uint64, l1Info eth.BlockInfo, sysCfg eth.SystemConfig, regolith bool) ([]byte, error) {
dep, err := L1InfoDeposit(seqNumber, l1Info, sysCfg) dep, err := L1InfoDeposit(seqNumber, l1Info, sysCfg, regolith)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to create L1 info tx: %w", err) return nil, fmt.Errorf("failed to create L1 info tx: %w", err)
} }
......
...@@ -65,7 +65,7 @@ func TestParseL1InfoDepositTxData(t *testing.T) { ...@@ -65,7 +65,7 @@ func TestParseL1InfoDepositTxData(t *testing.T) {
info := testCase.mkInfo(rng) info := testCase.mkInfo(rng)
l1Cfg := testCase.mkL1Cfg(rng, info) l1Cfg := testCase.mkL1Cfg(rng, info)
seqNr := testCase.seqNr(rng) seqNr := testCase.seqNr(rng)
depTx, err := L1InfoDeposit(seqNr, info, l1Cfg) depTx, err := L1InfoDeposit(seqNr, info, l1Cfg, false)
require.NoError(t, err) require.NoError(t, err)
res, err := L1InfoDepositTxData(depTx.Data) res, err := L1InfoDepositTxData(depTx.Data)
require.NoError(t, err, "expected valid deposit info") require.NoError(t, err, "expected valid deposit info")
...@@ -95,11 +95,19 @@ func TestParseL1InfoDepositTxData(t *testing.T) { ...@@ -95,11 +95,19 @@ func TestParseL1InfoDepositTxData(t *testing.T) {
t.Run("invalid selector", func(t *testing.T) { t.Run("invalid selector", func(t *testing.T) {
rng := rand.New(rand.NewSource(1234)) rng := rand.New(rand.NewSource(1234))
info := testutils.MakeBlockInfo(nil)(rng) info := testutils.MakeBlockInfo(nil)(rng)
depTx, err := L1InfoDeposit(randomSeqNr(rng), info, randomL1Cfg(rng, info)) depTx, err := L1InfoDeposit(randomSeqNr(rng), info, randomL1Cfg(rng, info), false)
require.NoError(t, err) require.NoError(t, err)
_, err = rand.Read(depTx.Data[0:4]) _, err = rand.Read(depTx.Data[0:4])
require.NoError(t, err) require.NoError(t, err)
_, err = L1InfoDepositTxData(depTx.Data) _, err = L1InfoDepositTxData(depTx.Data)
require.ErrorContains(t, err, "function signature") require.ErrorContains(t, err, "function signature")
}) })
t.Run("regolith", func(t *testing.T) {
rng := rand.New(rand.NewSource(1234))
info := testutils.MakeBlockInfo(nil)(rng)
depTx, err := L1InfoDeposit(randomSeqNr(rng), info, randomL1Cfg(rng, info), true)
require.NoError(t, err)
require.False(t, depTx.IsSystemTransaction)
require.Equal(t, depTx.Gas, uint64(RegolithSystemTxGas))
})
} }
...@@ -27,7 +27,7 @@ func FuzzParseL1InfoDepositTxDataValid(f *testing.F) { ...@@ -27,7 +27,7 @@ func FuzzParseL1InfoDepositTxDataValid(f *testing.F) {
typeProvider.Fuzz(&sysCfg) typeProvider.Fuzz(&sysCfg)
// Create our deposit tx from our info // Create our deposit tx from our info
depTx, err := L1InfoDeposit(seqNr, &l1Info, sysCfg) depTx, err := L1InfoDeposit(seqNr, &l1Info, sysCfg, false)
require.NoError(t, err, "error creating deposit tx from L1 info") require.NoError(t, err, "error creating deposit tx from L1 info")
// Get our info from out deposit tx // Get our info from out deposit tx
...@@ -71,7 +71,7 @@ func FuzzDecodeDepositTxDataToL1Info(f *testing.F) { ...@@ -71,7 +71,7 @@ func FuzzDecodeDepositTxDataToL1Info(f *testing.F) {
GasLimit: uint64(0), GasLimit: uint64(0),
} }
depTx, err := L1InfoDeposit(res.SequenceNumber, &l1Info, sysCfg) depTx, err := L1InfoDeposit(res.SequenceNumber, &l1Info, sysCfg, false)
require.NoError(t, err, "error creating deposit tx from L1 info") require.NoError(t, err, "error creating deposit tx from L1 info")
require.Equal(t, depTx.Data, fuzzedData) require.Equal(t, depTx.Data, fuzzedData)
}) })
......
...@@ -179,7 +179,8 @@ func (d *Sequencer) RunNextSequencerAction(ctx context.Context) *eth.ExecutionPa ...@@ -179,7 +179,8 @@ func (d *Sequencer) RunNextSequencerAction(ctx context.Context) *eth.ExecutionPa
d.log.Error("sequencer failed to start building new block", "err", err) d.log.Error("sequencer failed to start building new block", "err", err)
d.nextAction = d.timeNow().Add(time.Second) d.nextAction = d.timeNow().Add(time.Second)
} else { } else {
d.log.Info("sequencer started building new block", "payload_id", buildingID) parent, buildingID, _ := d.engine.BuildingPayload() // we should have a new payload ID now that we're building a block
d.log.Info("sequencer started building new block", "payload_id", buildingID, "l2_parent_block", parent, "l2_parent_block_time", parent.Time)
} }
return nil return nil
} }
......
...@@ -245,7 +245,7 @@ func TestSequencerChaosMonkey(t *testing.T) { ...@@ -245,7 +245,7 @@ func TestSequencerChaosMonkey(t *testing.T) {
InfoBaseFee: big.NewInt(1234), InfoBaseFee: big.NewInt(1234),
InfoReceiptRoot: common.Hash{}, InfoReceiptRoot: common.Hash{},
} }
infoDep, err := derive.L1InfoDepositBytes(seqNr, l1Info, cfg.Genesis.SystemConfig) infoDep, err := derive.L1InfoDepositBytes(seqNr, l1Info, cfg.Genesis.SystemConfig, false)
require.NoError(t, err) require.NoError(t, err)
testGasLimit := eth.Uint64Quantity(10_000_000) testGasLimit := eth.Uint64Quantity(10_000_000)
......
...@@ -5,9 +5,11 @@ import ( ...@@ -5,9 +5,11 @@ import (
"errors" "errors"
"fmt" "fmt"
"math/big" "math/big"
"time"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum-optimism/optimism/op-node/eth" "github.com/ethereum-optimism/optimism/op-node/eth"
) )
...@@ -66,6 +68,12 @@ type Config struct { ...@@ -66,6 +68,12 @@ type Config struct {
// Required to identify the L2 network and create p2p signatures unique for this chain. // Required to identify the L2 network and create p2p signatures unique for this chain.
L2ChainID *big.Int `json:"l2_chain_id"` L2ChainID *big.Int `json:"l2_chain_id"`
// RegolithTime sets the activation time of the Regolith network-upgrade:
// a pre-mainnet Bedrock change that addresses findings of the Sherlock contest related to deposit attributes.
// "Regolith" is the loose deposited rock that sits on top of Bedrock.
// Active if RegolithTime != nil && L2 block timestamp >= *RegolithTime, inactive otherwise.
RegolithTime *uint64 `json:"regolith_time,omitempty"`
// Note: below addresses are part of the block-derivation process, // Note: below addresses are part of the block-derivation process,
// and required to be the same network-wide to stay in consensus. // and required to be the same network-wide to stay in consensus.
...@@ -228,4 +236,53 @@ func (c *Config) L1Signer() types.Signer { ...@@ -228,4 +236,53 @@ func (c *Config) L1Signer() types.Signer {
return types.NewLondonSigner(c.L1ChainID) return types.NewLondonSigner(c.L1ChainID)
} }
// IsRegolith returns true if the Regolith hardfork is active at or past the given timestamp.
func (c *Config) IsRegolith(timestamp uint64) bool {
return c.RegolithTime != nil && timestamp >= *c.RegolithTime
}
// Description outputs a banner describing the important parts of rollup configuration in a human-readable form.
// Optionally provide a mapping of L2 chain IDs to network names to label the L2 chain with if not unknown.
// The config should be config.Check()-ed before creating a description.
func (c *Config) Description(l2Chains map[string]string) string {
// Find and report the network the user is running
var banner string
networkL2 := ""
if l2Chains != nil {
networkL2 = l2Chains[c.L2ChainID.String()]
}
if networkL2 == "" {
networkL2 = "unknown L2"
}
networkL1 := params.NetworkNames[c.L1ChainID.String()]
if networkL1 == "" {
networkL1 = "unknown L1"
}
banner += fmt.Sprintf("L2 Chain ID: %v (%s)\n", c.L2ChainID, networkL2)
banner += fmt.Sprintf("L1 Chain ID: %v (%s)\n", c.L1ChainID, networkL1)
// Report the genesis configuration
banner += "Bedrock starting point:\n"
banner += fmt.Sprintf(" L2 starting time: %d ~ %s\n", c.Genesis.L2Time, fmtTime(c.Genesis.L2Time))
banner += fmt.Sprintf(" L2 block: %s %d\n", c.Genesis.L2.Hash, c.Genesis.L2.Number)
banner += fmt.Sprintf(" L1 block: %s %d\n", c.Genesis.L1.Hash, c.Genesis.L1.Number)
// Report the upgrade configuration
banner += "Post-Bedrock Network Upgrades (timestamp based):\n"
banner += fmt.Sprintf(" - Regolith: %s\n", fmtForkTimeOrUnset(c.RegolithTime))
return banner
}
func fmtForkTimeOrUnset(v *uint64) string {
if v == nil {
return "(not configured)"
}
if *v == 0 { // don't output the unix epoch time if it's really just activated at genesis.
return "@ genesis"
}
return fmt.Sprintf("@ %-10v ~ %s", *v, fmtTime(*v))
}
func fmtTime(v uint64) string {
return time.Unix(int64(v), 0).Format(time.UnixDate)
}
type Epoch uint64 type Epoch uint64
...@@ -3,12 +3,14 @@ package rollup ...@@ -3,12 +3,14 @@ package rollup
import ( import (
"context" "context"
"encoding/json" "encoding/json"
"fmt"
"math/big" "math/big"
"math/rand" "math/rand"
"testing" "testing"
"time" "time"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
...@@ -136,6 +138,65 @@ func TestCheckL1BlockRefByNumber(t *testing.T) { ...@@ -136,6 +138,65 @@ func TestCheckL1BlockRefByNumber(t *testing.T) {
assert.Error(t, err) assert.Error(t, err)
} }
// TestRandomConfigDescription tests that the description works for different variations of a random rollup config.
func TestRandomConfigDescription(t *testing.T) {
t.Run("named L2", func(t *testing.T) {
config := randConfig()
out := config.Description(map[string]string{config.L2ChainID.String(): "foobar chain"})
require.Contains(t, out, "foobar chain")
})
t.Run("named L1", func(t *testing.T) {
config := randConfig()
config.L1ChainID = big.NewInt(5)
out := config.Description(map[string]string{config.L2ChainID.String(): "foobar chain"})
require.Contains(t, out, "goerli")
})
t.Run("unnamed", func(t *testing.T) {
config := randConfig()
out := config.Description(nil)
require.Contains(t, out, "(unknown L1)")
require.Contains(t, out, "(unknown L2)")
})
t.Run("regolith unset", func(t *testing.T) {
config := randConfig()
config.RegolithTime = nil
out := config.Description(nil)
require.Contains(t, out, "Regolith: (not configured)")
})
t.Run("regolith genesis", func(t *testing.T) {
config := randConfig()
config.RegolithTime = new(uint64)
out := config.Description(nil)
require.Contains(t, out, "Regolith: @ genesis")
})
t.Run("regolith date", func(t *testing.T) {
config := randConfig()
x := uint64(1677119335)
config.RegolithTime = &x
out := config.Description(nil)
// Don't check human-readable part of the date, it's timezone-dependent.
// Don't make this test fail only in Australia :')
require.Contains(t, out, fmt.Sprintf("Regolith: @ %d ~ ", x))
})
}
// TestRegolithActivation tests the activation condition of the Regolith upgrade.
func TestRegolithActivation(t *testing.T) {
config := randConfig()
config.RegolithTime = nil
require.False(t, config.IsRegolith(0), "false if nil time, even if checking 0")
require.False(t, config.IsRegolith(123456), "false if nil time")
config.RegolithTime = new(uint64)
require.True(t, config.IsRegolith(0), "true at zero")
require.True(t, config.IsRegolith(123456), "true for any")
x := uint64(123)
config.RegolithTime = &x
require.False(t, config.IsRegolith(0))
require.False(t, config.IsRegolith(122))
require.True(t, config.IsRegolith(123))
require.True(t, config.IsRegolith(124))
}
type mockL2Client struct { type mockL2Client struct {
chainID *big.Int chainID *big.Int
Hash common.Hash Hash common.Hash
......
...@@ -16,7 +16,7 @@ WORKDIR /opt/foundry ...@@ -16,7 +16,7 @@ WORKDIR /opt/foundry
# Only diff from upstream docker image is this clone instead # Only diff from upstream docker image is this clone instead
# of COPY. We select a specific commit to use. # of COPY. We select a specific commit to use.
RUN git clone https://github.com/foundry-rs/foundry.git . \ RUN git clone https://github.com/foundry-rs/foundry.git . \
&& git checkout edf15abd648bb96e2bcee342c1d72ec7d1066cd1 && git checkout 8f3fca9c608d58981daaffe11e7f8076644cb753
RUN source $HOME/.profile && \ RUN source $HOME/.profile && \
cargo build --release && \ cargo build --release && \
......
...@@ -9,7 +9,6 @@ ...@@ -9,7 +9,6 @@
"batch-submitter", "batch-submitter",
"bss-core", "bss-core",
"gas-oracle", "gas-oracle",
"indexer",
"integration-tests", "integration-tests",
"l2geth-exporter", "l2geth-exporter",
"l2geth", "l2geth",
...@@ -19,7 +18,6 @@ ...@@ -19,7 +18,6 @@
"ops/docker/js-builder", "ops/docker/js-builder",
"ops/docker/ci-builder", "ops/docker/ci-builder",
"ops/docker/foundry", "ops/docker/foundry",
"proxyd",
"endpoint-monitor" "endpoint-monitor"
], ],
"nohoist": [ "nohoist": [
......
This diff is collapsed.
...@@ -63,6 +63,7 @@ ...@@ -63,6 +63,7 @@
| l2Sender | address | 50 | 0 | 20 | contracts/L1/OptimismPortal.sol:OptimismPortal | | l2Sender | address | 50 | 0 | 20 | contracts/L1/OptimismPortal.sol:OptimismPortal |
| finalizedWithdrawals | mapping(bytes32 => bool) | 51 | 0 | 32 | contracts/L1/OptimismPortal.sol:OptimismPortal | | finalizedWithdrawals | mapping(bytes32 => bool) | 51 | 0 | 32 | contracts/L1/OptimismPortal.sol:OptimismPortal |
| provenWithdrawals | mapping(bytes32 => struct OptimismPortal.ProvenWithdrawal) | 52 | 0 | 32 | contracts/L1/OptimismPortal.sol:OptimismPortal | | provenWithdrawals | mapping(bytes32 => struct OptimismPortal.ProvenWithdrawal) | 52 | 0 | 32 | contracts/L1/OptimismPortal.sol:OptimismPortal |
| paused | bool | 53 | 0 | 1 | contracts/L1/OptimismPortal.sol:OptimismPortal |
======================= =======================
➡ contracts/L1/SystemConfig.sol:SystemConfig ➡ contracts/L1/SystemConfig.sol:SystemConfig
......
...@@ -26,7 +26,7 @@ contract L1ERC721Bridge is ERC721Bridge, Semver { ...@@ -26,7 +26,7 @@ contract L1ERC721Bridge is ERC721Bridge, Semver {
* @param _otherBridge Address of the ERC721 bridge on the other network. * @param _otherBridge Address of the ERC721 bridge on the other network.
*/ */
constructor(address _messenger, address _otherBridge) constructor(address _messenger, address _otherBridge)
Semver(1, 0, 0) Semver(1, 1, 0)
ERC721Bridge(_messenger, _otherBridge) ERC721Bridge(_messenger, _otherBridge)
{} {}
...@@ -83,7 +83,7 @@ contract L1ERC721Bridge is ERC721Bridge, Semver { ...@@ -83,7 +83,7 @@ contract L1ERC721Bridge is ERC721Bridge, Semver {
uint32 _minGasLimit, uint32 _minGasLimit,
bytes calldata _extraData bytes calldata _extraData
) internal override { ) internal override {
require(_remoteToken != address(0), "ERC721Bridge: remote token cannot be address(0)"); require(_remoteToken != address(0), "L1ERC721Bridge: remote token cannot be address(0)");
// Construct calldata for _l2Token.finalizeBridgeERC721(_to, _tokenId) // Construct calldata for _l2Token.finalizeBridgeERC721(_to, _tokenId)
bytes memory message = abi.encodeWithSelector( bytes memory message = abi.encodeWithSelector(
......
...@@ -107,28 +107,6 @@ contract L1StandardBridge is StandardBridge, Semver { ...@@ -107,28 +107,6 @@ contract L1StandardBridge is StandardBridge, Semver {
_initiateETHDeposit(msg.sender, msg.sender, RECEIVE_DEFAULT_GAS_LIMIT, bytes("")); _initiateETHDeposit(msg.sender, msg.sender, RECEIVE_DEFAULT_GAS_LIMIT, bytes(""));
} }
/**
* @custom:legacy
* @notice Finalizes a withdrawal of ERC20 tokens from L2.
*
* @param _l1Token Address of the token on L1.
* @param _l2Token Address of the corresponding token on L2.
* @param _from Address of the withdrawer on L2.
* @param _to Address of the recipient on L1.
* @param _amount Amount of the ERC20 to withdraw.
* @param _extraData Optional data forwarded from L2.
*/
function finalizeERC20Withdrawal(
address _l1Token,
address _l2Token,
address _from,
address _to,
uint256 _amount,
bytes calldata _extraData
) external onlyOtherBridge {
finalizeBridgeERC20(_l1Token, _l2Token, _from, _to, _amount, _extraData);
}
/** /**
* @custom:legacy * @custom:legacy
* @notice Deposits some amount of ETH into the sender's account on L2. * @notice Deposits some amount of ETH into the sender's account on L2.
...@@ -240,10 +218,32 @@ contract L1StandardBridge is StandardBridge, Semver { ...@@ -240,10 +218,32 @@ contract L1StandardBridge is StandardBridge, Semver {
address _to, address _to,
uint256 _amount, uint256 _amount,
bytes calldata _extraData bytes calldata _extraData
) external payable onlyOtherBridge { ) external payable {
finalizeBridgeETH(_from, _to, _amount, _extraData); finalizeBridgeETH(_from, _to, _amount, _extraData);
} }
/**
* @custom:legacy
* @notice Finalizes a withdrawal of ERC20 tokens from L2.
*
* @param _l1Token Address of the token on L1.
* @param _l2Token Address of the corresponding token on L2.
* @param _from Address of the withdrawer on L2.
* @param _to Address of the recipient on L1.
* @param _amount Amount of the ERC20 to withdraw.
* @param _extraData Optional data forwarded from L2.
*/
function finalizeERC20Withdrawal(
address _l1Token,
address _l2Token,
address _from,
address _to,
uint256 _amount,
bytes calldata _extraData
) external {
finalizeBridgeERC20(_l1Token, _l2Token, _from, _to, _amount, _extraData);
}
/** /**
* @custom:legacy * @custom:legacy
* @notice Retrieves the access of the corresponding L2 bridge contract. * @notice Retrieves the access of the corresponding L2 bridge contract.
......
...@@ -58,6 +58,11 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver { ...@@ -58,6 +58,11 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver {
*/ */
L2OutputOracle public immutable L2_ORACLE; L2OutputOracle public immutable L2_ORACLE;
/**
* @notice Address that has the ability to pause and unpause deposits and withdrawals.
*/
address public immutable GUARDIAN;
/** /**
* @notice Address of the L2 account which initiated a withdrawal in this transaction. If the * @notice Address of the L2 account which initiated a withdrawal in this transaction. If the
* of this variable is the default L2 sender address, then we are NOT inside of a call * of this variable is the default L2 sender address, then we are NOT inside of a call
...@@ -75,6 +80,13 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver { ...@@ -75,6 +80,13 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver {
*/ */
mapping(bytes32 => ProvenWithdrawal) public provenWithdrawals; mapping(bytes32 => ProvenWithdrawal) public provenWithdrawals;
/**
* @notice Determines if cross domain messaging is paused. When set to true,
* deposits and withdrawals are paused. This may be removed in the
* future.
*/
bool public paused;
/** /**
* @notice Emitted when a transaction is deposited from L1 to L2. The parameters of this event * @notice Emitted when a transaction is deposited from L1 to L2. The parameters of this event
* are read by the rollup node and used to derive deposit transactions on L2. * are read by the rollup node and used to derive deposit transactions on L2.
...@@ -111,25 +123,74 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver { ...@@ -111,25 +123,74 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver {
event WithdrawalFinalized(bytes32 indexed withdrawalHash, bool success); event WithdrawalFinalized(bytes32 indexed withdrawalHash, bool success);
/** /**
* @custom:semver 1.0.0 * @notice Emitted when the pause is triggered.
*
* @param account Address of the account triggering the pause.
*/
event Paused(address account);
/**
* @notice Emitted when the pause is lifted.
*
* @param account Address of the account triggering the unpause.
*/
event Unpaused(address account);
/**
* @notice Reverts when paused.
*/
modifier whenNotPaused() {
require(paused == false, "OptimismPortal: paused");
_;
}
/**
* @custom:semver 1.1.0
* *
* @param _l2Oracle Address of the L2OutputOracle contract. * @param _l2Oracle Address of the L2OutputOracle contract.
* @param _guardian Address that can pause deposits and withdrawals.
* @param _finalizationPeriodSeconds Output finalization time in seconds. * @param _finalizationPeriodSeconds Output finalization time in seconds.
* @param _paused Sets the contract's pausability state.
*/ */
constructor(L2OutputOracle _l2Oracle, uint256 _finalizationPeriodSeconds) Semver(1, 0, 0) { constructor(
L2OutputOracle _l2Oracle,
uint256 _finalizationPeriodSeconds,
address _guardian,
bool _paused
) Semver(1, 1, 0) {
L2_ORACLE = _l2Oracle; L2_ORACLE = _l2Oracle;
GUARDIAN = _guardian;
FINALIZATION_PERIOD_SECONDS = _finalizationPeriodSeconds; FINALIZATION_PERIOD_SECONDS = _finalizationPeriodSeconds;
initialize(); initialize(_paused);
} }
/** /**
* @notice Initializer. * @notice Initializer.
*/ */
function initialize() public initializer { function initialize(bool _paused) public initializer {
l2Sender = Constants.DEFAULT_L2_SENDER; l2Sender = Constants.DEFAULT_L2_SENDER;
paused = _paused;
__ResourceMetering_init(); __ResourceMetering_init();
} }
/**
* @notice Pause deposits and withdrawals.
*/
function pause() external {
require(msg.sender == GUARDIAN, "OptimismPortal: only guardian can pause");
paused = true;
emit Paused(msg.sender);
}
/**
* @notice Unpause deposits and withdrawals.
*/
function unpause() external {
require(msg.sender == GUARDIAN, "OptimismPortal: only guardian can unpause");
paused = false;
emit Unpaused(msg.sender);
}
/** /**
* @notice Accepts value so that users can send ETH directly to this contract and have the * @notice Accepts value so that users can send ETH directly to this contract and have the
* funds be deposited to their address on L2. This is intended as a convenience * funds be deposited to their address on L2. This is intended as a convenience
...@@ -162,7 +223,7 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver { ...@@ -162,7 +223,7 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver {
uint256 _l2OutputIndex, uint256 _l2OutputIndex,
Types.OutputRootProof calldata _outputRootProof, Types.OutputRootProof calldata _outputRootProof,
bytes[] calldata _withdrawalProof bytes[] calldata _withdrawalProof
) external { ) external whenNotPaused {
// Prevent users from creating a deposit transaction where this address is the message // Prevent users from creating a deposit transaction where this address is the message
// sender on L2. Because this is checked here, we do not need to check again in // sender on L2. Because this is checked here, we do not need to check again in
// `finalizeWithdrawalTransaction`. // `finalizeWithdrawalTransaction`.
...@@ -240,7 +301,10 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver { ...@@ -240,7 +301,10 @@ contract OptimismPortal is Initializable, ResourceMetering, Semver {
* *
* @param _tx Withdrawal transaction to finalize. * @param _tx Withdrawal transaction to finalize.
*/ */
function finalizeWithdrawalTransaction(Types.WithdrawalTransaction memory _tx) external { function finalizeWithdrawalTransaction(Types.WithdrawalTransaction memory _tx)
external
whenNotPaused
{
// Make sure that the l2Sender has not yet been set. The l2Sender is set to a value other // Make sure that the l2Sender has not yet been set. The l2Sender is set to a value other
// than the default value when a withdrawal transaction is being finalized. This check is // than the default value when a withdrawal transaction is being finalized. This check is
// a defacto reentrancy guard. // a defacto reentrancy guard.
......
...@@ -92,8 +92,7 @@ abstract contract ResourceMetering is Initializable { ...@@ -92,8 +92,7 @@ abstract contract ResourceMetering is Initializable {
// spam the L2 system. Fee scheme is very similar to EIP-1559 with minor changes. // spam the L2 system. Fee scheme is very similar to EIP-1559 with minor changes.
int256 gasUsedDelta = int256(uint256(params.prevBoughtGas)) - TARGET_RESOURCE_LIMIT; int256 gasUsedDelta = int256(uint256(params.prevBoughtGas)) - TARGET_RESOURCE_LIMIT;
int256 baseFeeDelta = (int256(uint256(params.prevBaseFee)) * gasUsedDelta) / int256 baseFeeDelta = (int256(uint256(params.prevBaseFee)) * gasUsedDelta) /
TARGET_RESOURCE_LIMIT / (TARGET_RESOURCE_LIMIT * BASE_FEE_MAX_CHANGE_DENOMINATOR);
BASE_FEE_MAX_CHANGE_DENOMINATOR;
// Update base fee by adding the base fee delta and clamp the resulting value between // Update base fee by adding the base fee delta and clamp the resulting value between
// min and max. // min and max.
......
...@@ -26,7 +26,7 @@ contract L2ERC721Bridge is ERC721Bridge, Semver { ...@@ -26,7 +26,7 @@ contract L2ERC721Bridge is ERC721Bridge, Semver {
* @param _otherBridge Address of the ERC721 bridge on the other network. * @param _otherBridge Address of the ERC721 bridge on the other network.
*/ */
constructor(address _messenger, address _otherBridge) constructor(address _messenger, address _otherBridge)
Semver(1, 0, 0) Semver(1, 1, 0)
ERC721Bridge(_messenger, _otherBridge) ERC721Bridge(_messenger, _otherBridge)
{} {}
...@@ -85,12 +85,12 @@ contract L2ERC721Bridge is ERC721Bridge, Semver { ...@@ -85,12 +85,12 @@ contract L2ERC721Bridge is ERC721Bridge, Semver {
uint32 _minGasLimit, uint32 _minGasLimit,
bytes calldata _extraData bytes calldata _extraData
) internal override { ) internal override {
require(_remoteToken != address(0), "ERC721Bridge: remote token cannot be address(0)"); require(_remoteToken != address(0), "L2ERC721Bridge: remote token cannot be address(0)");
// Check that the withdrawal is being initiated by the NFT owner // Check that the withdrawal is being initiated by the NFT owner
require( require(
_from == IOptimismMintableERC721(_localToken).ownerOf(_tokenId), _from == IOptimismMintableERC721(_localToken).ownerOf(_tokenId),
"Withdrawal is not being initiated by NFT owner" "L2ERC721Bridge: Withdrawal is not being initiated by NFT owner"
); );
// Construct calldata for l1ERC721Bridge.finalizeBridgeERC721(_to, _tokenId) // Construct calldata for l1ERC721Bridge.finalizeBridgeERC721(_to, _tokenId)
......
...@@ -111,6 +111,12 @@ contract SystemDictator is OwnableUpgradeable { ...@@ -111,6 +111,12 @@ contract SystemDictator is OwnableUpgradeable {
*/ */
L2OutputOracleDynamicConfig public l2OutputOracleDynamicConfig; L2OutputOracleDynamicConfig public l2OutputOracleDynamicConfig;
/**
* @notice Dynamic configuration for the OptimismPortal. Determines
* if the system should be paused when initialized.
*/
bool public optimismPortalDynamicConfig;
/** /**
* @notice Current step; * @notice Current step;
*/ */
...@@ -160,14 +166,17 @@ contract SystemDictator is OwnableUpgradeable { ...@@ -160,14 +166,17 @@ contract SystemDictator is OwnableUpgradeable {
} }
/** /**
* @notice Allows the owner to update dynamic L2OutputOracle config. * @notice Allows the owner to update dynamic config.
* *
* @param _l2OutputOracleDynamicConfig Dynamic L2OutputOracle config. * @param _l2OutputOracleDynamicConfig Dynamic L2OutputOracle config.
* @param _optimismPortalDynamicConfig Dynamic OptimismPortal config.
*/ */
function updateL2OutputOracleDynamicConfig( function updateDynamicConfig(
L2OutputOracleDynamicConfig memory _l2OutputOracleDynamicConfig L2OutputOracleDynamicConfig memory _l2OutputOracleDynamicConfig,
bool _optimismPortalDynamicConfig
) external onlyOwner { ) external onlyOwner {
l2OutputOracleDynamicConfig = _l2OutputOracleDynamicConfig; l2OutputOracleDynamicConfig = _l2OutputOracleDynamicConfig;
optimismPortalDynamicConfig = _optimismPortalDynamicConfig;
dynamicConfigSet = true; dynamicConfigSet = true;
} }
...@@ -314,7 +323,7 @@ contract SystemDictator is OwnableUpgradeable { ...@@ -314,7 +323,7 @@ contract SystemDictator is OwnableUpgradeable {
config.globalConfig.proxyAdmin.upgradeAndCall( config.globalConfig.proxyAdmin.upgradeAndCall(
payable(config.proxyAddressConfig.optimismPortalProxy), payable(config.proxyAddressConfig.optimismPortalProxy),
address(config.implementationAddressConfig.optimismPortalImpl), address(config.implementationAddressConfig.optimismPortalImpl),
abi.encodeCall(OptimismPortal.initialize, ()) abi.encodeCall(OptimismPortal.initialize, (optimismPortalDynamicConfig))
); );
// Upgrade the L1CrossDomainMessenger. // Upgrade the L1CrossDomainMessenger.
......
...@@ -9,7 +9,12 @@ contract EchidnaFuzzOptimismPortal { ...@@ -9,7 +9,12 @@ contract EchidnaFuzzOptimismPortal {
bool internal failedToComplete; bool internal failedToComplete;
constructor() { constructor() {
portal = new OptimismPortal(L2OutputOracle(address(0)), 10); portal = new OptimismPortal({
_l2Oracle: L2OutputOracle(address(0)),
_guardian: address(0),
_finalizationPeriodSeconds: 10,
_paused: false
});
} }
// A test intended to identify any unexpected halting conditions // A test intended to identify any unexpected halting conditions
......
...@@ -50,7 +50,7 @@ contract CommonTest is Test { ...@@ -50,7 +50,7 @@ contract CommonTest is Test {
FFIInterface ffi; FFIInterface ffi;
function _setUp() public { function setUp() public virtual {
// Give alice and bob some ETH // Give alice and bob some ETH
vm.deal(alice, 1 << 16); vm.deal(alice, 1 << 16);
vm.deal(bob, 1 << 16); vm.deal(bob, 1 << 16);
...@@ -99,6 +99,7 @@ contract L2OutputOracle_Initializer is CommonTest { ...@@ -99,6 +99,7 @@ contract L2OutputOracle_Initializer is CommonTest {
uint256 internal l2BlockTime = 2; uint256 internal l2BlockTime = 2;
uint256 internal startingBlockNumber = 200; uint256 internal startingBlockNumber = 200;
uint256 internal startingTimestamp = 1000; uint256 internal startingTimestamp = 1000;
address guardian;
// Test data // Test data
uint256 initL1Time; uint256 initL1Time;
...@@ -117,8 +118,9 @@ contract L2OutputOracle_Initializer is CommonTest { ...@@ -117,8 +118,9 @@ contract L2OutputOracle_Initializer is CommonTest {
vm.warp(oracle.computeL2Timestamp(_nextBlockNumber) + 1); vm.warp(oracle.computeL2Timestamp(_nextBlockNumber) + 1);
} }
function setUp() public virtual { function setUp() public virtual override {
_setUp(); super.setUp();
guardian = makeAddr("guardian");
// By default the first block has timestamp and number zero, which will cause underflows in the // By default the first block has timestamp and number zero, which will cause underflows in the
// tests, so we'll move forward to these block values. // tests, so we'll move forward to these block values.
...@@ -163,14 +165,19 @@ contract Portal_Initializer is L2OutputOracle_Initializer { ...@@ -163,14 +165,19 @@ contract Portal_Initializer is L2OutputOracle_Initializer {
); );
function setUp() public virtual override { function setUp() public virtual override {
L2OutputOracle_Initializer.setUp(); super.setUp();
opImpl = new OptimismPortal(oracle, 7 days); opImpl = new OptimismPortal({
_l2Oracle: oracle,
_guardian: guardian,
_finalizationPeriodSeconds: 7 days,
_paused: true
});
Proxy proxy = new Proxy(multisig); Proxy proxy = new Proxy(multisig);
vm.prank(multisig); vm.prank(multisig);
proxy.upgradeToAndCall( proxy.upgradeToAndCall(
address(opImpl), address(opImpl),
abi.encodeWithSelector(OptimismPortal.initialize.selector) abi.encodeWithSelector(OptimismPortal.initialize.selector, false)
); );
op = OptimismPortal(payable(address(proxy))); op = OptimismPortal(payable(address(proxy)));
} }
...@@ -224,7 +231,12 @@ contract Messenger_Initializer is L2OutputOracle_Initializer { ...@@ -224,7 +231,12 @@ contract Messenger_Initializer is L2OutputOracle_Initializer {
super.setUp(); super.setUp();
// Deploy the OptimismPortal // Deploy the OptimismPortal
op = new OptimismPortal(oracle, 7 days); op = new OptimismPortal({
_l2Oracle: oracle,
_guardian: guardian,
_finalizationPeriodSeconds: 7 days,
_paused: false
});
vm.label(address(op), "OptimismPortal"); vm.label(address(op), "OptimismPortal");
// Deploy the address manager // Deploy the address manager
......
...@@ -18,7 +18,8 @@ contract XDomainSetter is CrossDomainOwnable { ...@@ -18,7 +18,8 @@ contract XDomainSetter is CrossDomainOwnable {
contract CrossDomainOwnable_Test is CommonTest { contract CrossDomainOwnable_Test is CommonTest {
XDomainSetter setter; XDomainSetter setter;
function setUp() external { function setUp() public override {
super.setUp();
setter = new XDomainSetter(); setter = new XDomainSetter();
} }
......
...@@ -7,7 +7,7 @@ import { DeployerWhitelist } from "../legacy/DeployerWhitelist.sol"; ...@@ -7,7 +7,7 @@ import { DeployerWhitelist } from "../legacy/DeployerWhitelist.sol";
contract DeployerWhitelist_Test is CommonTest { contract DeployerWhitelist_Test is CommonTest {
DeployerWhitelist list; DeployerWhitelist list;
function setUp() external { function setUp() public virtual override {
list = new DeployerWhitelist(); list = new DeployerWhitelist();
} }
......
...@@ -6,10 +6,6 @@ import { Types } from "../libraries/Types.sol"; ...@@ -6,10 +6,6 @@ import { Types } from "../libraries/Types.sol";
import { Encoding } from "../libraries/Encoding.sol"; import { Encoding } from "../libraries/Encoding.sol";
contract Encoding_Test is CommonTest { contract Encoding_Test is CommonTest {
function setUp() external {
_setUp();
}
function testFuzz_nonceVersioning_succeeds(uint240 _nonce, uint16 _version) external { function testFuzz_nonceVersioning_succeeds(uint240 _nonce, uint16 _version) external {
(uint240 nonce, uint16 version) = Encoding.decodeVersionedNonce( (uint240 nonce, uint16 version) = Encoding.decodeVersionedNonce(
Encoding.encodeVersionedNonce(_nonce, _version) Encoding.encodeVersionedNonce(_nonce, _version)
......
...@@ -25,7 +25,8 @@ contract GasPriceOracle_Test is CommonTest { ...@@ -25,7 +25,8 @@ contract GasPriceOracle_Test is CommonTest {
uint256 constant l1FeeOverhead = 310; uint256 constant l1FeeOverhead = 310;
uint256 constant l1FeeScalar = 10; uint256 constant l1FeeScalar = 10;
function setUp() external { function setUp() public virtual override {
super.setUp();
// place the L1Block contract at the predeploy address // place the L1Block contract at the predeploy address
vm.etch(Predeploys.L1_BLOCK_ATTRIBUTES, address(new L1Block()).code); vm.etch(Predeploys.L1_BLOCK_ATTRIBUTES, address(new L1Block()).code);
......
...@@ -9,7 +9,8 @@ contract GovernanceToken_Test is CommonTest { ...@@ -9,7 +9,8 @@ contract GovernanceToken_Test is CommonTest {
address constant rando = address(0x5678); address constant rando = address(0x5678);
GovernanceToken internal gov; GovernanceToken internal gov;
function setUp() external { function setUp() public virtual override {
super.setUp();
vm.prank(owner); vm.prank(owner);
gov = new GovernanceToken(); gov = new GovernanceToken();
} }
......
...@@ -7,10 +7,6 @@ import { Hashing } from "../libraries/Hashing.sol"; ...@@ -7,10 +7,6 @@ import { Hashing } from "../libraries/Hashing.sol";
import { Encoding } from "../libraries/Encoding.sol"; import { Encoding } from "../libraries/Encoding.sol";
contract Hashing_hashDepositSource_Test is CommonTest { contract Hashing_hashDepositSource_Test is CommonTest {
function setUp() external {
_setUp();
}
/** /**
* @notice Tests that hashDepositSource returns the correct hash in a simple case. * @notice Tests that hashDepositSource returns the correct hash in a simple case.
*/ */
...@@ -26,10 +22,6 @@ contract Hashing_hashDepositSource_Test is CommonTest { ...@@ -26,10 +22,6 @@ contract Hashing_hashDepositSource_Test is CommonTest {
} }
contract Hashing_hashCrossDomainMessage_Test is CommonTest { contract Hashing_hashCrossDomainMessage_Test is CommonTest {
function setUp() external {
_setUp();
}
/** /**
* @notice Tests that hashCrossDomainMessage returns the correct hash in a simple case. * @notice Tests that hashCrossDomainMessage returns the correct hash in a simple case.
*/ */
...@@ -54,10 +46,6 @@ contract Hashing_hashCrossDomainMessage_Test is CommonTest { ...@@ -54,10 +46,6 @@ contract Hashing_hashCrossDomainMessage_Test is CommonTest {
} }
contract Hashing_hashWithdrawal_Test is CommonTest { contract Hashing_hashWithdrawal_Test is CommonTest {
function setUp() external {
_setUp();
}
/** /**
* @notice Tests that hashWithdrawal returns the correct hash in a simple case. * @notice Tests that hashWithdrawal returns the correct hash in a simple case.
*/ */
...@@ -79,10 +67,6 @@ contract Hashing_hashWithdrawal_Test is CommonTest { ...@@ -79,10 +67,6 @@ contract Hashing_hashWithdrawal_Test is CommonTest {
} }
contract Hashing_hashOutputRootProof_Test is CommonTest { contract Hashing_hashOutputRootProof_Test is CommonTest {
function setUp() external {
_setUp();
}
/** /**
* @notice Tests that hashOutputRootProof returns the correct hash in a simple case. * @notice Tests that hashOutputRootProof returns the correct hash in a simple case.
*/ */
...@@ -112,10 +96,6 @@ contract Hashing_hashOutputRootProof_Test is CommonTest { ...@@ -112,10 +96,6 @@ contract Hashing_hashOutputRootProof_Test is CommonTest {
} }
contract Hashing_hashDepositTransaction_Test is CommonTest { contract Hashing_hashDepositTransaction_Test is CommonTest {
function setUp() external {
_setUp();
}
/** /**
* @notice Tests that hashDepositTransaction returns the correct hash in a simple case. * @notice Tests that hashDepositTransaction returns the correct hash in a simple case.
*/ */
......
...@@ -9,7 +9,8 @@ contract L1BlockTest is CommonTest { ...@@ -9,7 +9,8 @@ contract L1BlockTest is CommonTest {
address depositor; address depositor;
bytes32 immutable NON_ZERO_HASH = keccak256(abi.encode(1)); bytes32 immutable NON_ZERO_HASH = keccak256(abi.encode(1));
function setUp() external { function setUp() public virtual override {
super.setUp();
lb = new L1Block(); lb = new L1Block();
depositor = lb.DEPOSITOR_ACCOUNT(); depositor = lb.DEPOSITOR_ACCOUNT();
vm.prank(depositor); vm.prank(depositor);
......
...@@ -135,7 +135,7 @@ contract L1ERC721Bridge_Test is Messenger_Initializer { ...@@ -135,7 +135,7 @@ contract L1ERC721Bridge_Test is Messenger_Initializer {
function test_bridgeERC721_remoteTokenZeroAddress_reverts() external { function test_bridgeERC721_remoteTokenZeroAddress_reverts() external {
// Bridge the token. // Bridge the token.
vm.prank(alice); vm.prank(alice);
vm.expectRevert("ERC721Bridge: remote token cannot be address(0)"); vm.expectRevert("L1ERC721Bridge: remote token cannot be address(0)");
bridge.bridgeERC721(address(localToken), address(0), tokenId, 1234, hex"5678"); bridge.bridgeERC721(address(localToken), address(0), tokenId, 1234, hex"5678");
// Token is not locked in the bridge. // Token is not locked in the bridge.
...@@ -212,7 +212,7 @@ contract L1ERC721Bridge_Test is Messenger_Initializer { ...@@ -212,7 +212,7 @@ contract L1ERC721Bridge_Test is Messenger_Initializer {
function test_bridgeERC721To_remoteTokenZeroAddress_reverts() external { function test_bridgeERC721To_remoteTokenZeroAddress_reverts() external {
// Bridge the token. // Bridge the token.
vm.prank(alice); vm.prank(alice);
vm.expectRevert("ERC721Bridge: remote token cannot be address(0)"); vm.expectRevert("L1ERC721Bridge: remote token cannot be address(0)");
bridge.bridgeERC721To(address(localToken), address(0), bob, tokenId, 1234, hex"5678"); bridge.bridgeERC721To(address(localToken), address(0), bob, tokenId, 1234, hex"5678");
// Token is not locked in the bridge. // Token is not locked in the bridge.
......
...@@ -144,7 +144,7 @@ contract L2ERC721Bridge_Test is Messenger_Initializer { ...@@ -144,7 +144,7 @@ contract L2ERC721Bridge_Test is Messenger_Initializer {
function test_bridgeERC721_remoteTokenZeroAddress_reverts() external { function test_bridgeERC721_remoteTokenZeroAddress_reverts() external {
// Bridge the token. // Bridge the token.
vm.prank(alice); vm.prank(alice);
vm.expectRevert("ERC721Bridge: remote token cannot be address(0)"); vm.expectRevert("L2ERC721Bridge: remote token cannot be address(0)");
bridge.bridgeERC721(address(localToken), address(0), tokenId, 1234, hex"5678"); bridge.bridgeERC721(address(localToken), address(0), tokenId, 1234, hex"5678");
// Token is not locked in the bridge. // Token is not locked in the bridge.
...@@ -154,7 +154,7 @@ contract L2ERC721Bridge_Test is Messenger_Initializer { ...@@ -154,7 +154,7 @@ contract L2ERC721Bridge_Test is Messenger_Initializer {
function test_bridgeERC721_wrongOwner_reverts() external { function test_bridgeERC721_wrongOwner_reverts() external {
// Bridge the token. // Bridge the token.
vm.prank(bob); vm.prank(bob);
vm.expectRevert("Withdrawal is not being initiated by NFT owner"); vm.expectRevert("L2ERC721Bridge: Withdrawal is not being initiated by NFT owner");
bridge.bridgeERC721(address(localToken), address(remoteToken), tokenId, 1234, hex"5678"); bridge.bridgeERC721(address(localToken), address(remoteToken), tokenId, 1234, hex"5678");
// Token is not locked in the bridge. // Token is not locked in the bridge.
...@@ -218,7 +218,7 @@ contract L2ERC721Bridge_Test is Messenger_Initializer { ...@@ -218,7 +218,7 @@ contract L2ERC721Bridge_Test is Messenger_Initializer {
function test_bridgeERC721To_remoteTokenZeroAddress_reverts() external { function test_bridgeERC721To_remoteTokenZeroAddress_reverts() external {
// Bridge the token. // Bridge the token.
vm.prank(alice); vm.prank(alice);
vm.expectRevert("ERC721Bridge: remote token cannot be address(0)"); vm.expectRevert("L2ERC721Bridge: remote token cannot be address(0)");
bridge.bridgeERC721To(address(localToken), address(0), bob, tokenId, 1234, hex"5678"); bridge.bridgeERC721To(address(localToken), address(0), bob, tokenId, 1234, hex"5678");
// Token is not locked in the bridge. // Token is not locked in the bridge.
...@@ -228,7 +228,7 @@ contract L2ERC721Bridge_Test is Messenger_Initializer { ...@@ -228,7 +228,7 @@ contract L2ERC721Bridge_Test is Messenger_Initializer {
function test_bridgeERC721To_wrongOwner_reverts() external { function test_bridgeERC721To_wrongOwner_reverts() external {
// Bridge the token. // Bridge the token.
vm.prank(bob); vm.prank(bob);
vm.expectRevert("Withdrawal is not being initiated by NFT owner"); vm.expectRevert("L2ERC721Bridge: Withdrawal is not being initiated by NFT owner");
bridge.bridgeERC721To( bridge.bridgeERC721To(
address(localToken), address(localToken),
address(remoteToken), address(remoteToken),
......
...@@ -21,7 +21,8 @@ contract L2ToL1MessagePasserTest is CommonTest { ...@@ -21,7 +21,8 @@ contract L2ToL1MessagePasserTest is CommonTest {
event WithdrawerBalanceBurnt(uint256 indexed amount); event WithdrawerBalanceBurnt(uint256 indexed amount);
function setUp() public virtual { function setUp() public virtual override {
super.setUp();
messagePasser = new L2ToL1MessagePasser(); messagePasser = new L2ToL1MessagePasser();
} }
......
...@@ -8,7 +8,8 @@ import { Predeploys } from "../libraries/Predeploys.sol"; ...@@ -8,7 +8,8 @@ import { Predeploys } from "../libraries/Predeploys.sol";
contract LegacyERC20ETH_Test is CommonTest { contract LegacyERC20ETH_Test is CommonTest {
LegacyERC20ETH eth; LegacyERC20ETH eth;
function setUp() external { function setUp() public virtual override {
super.setUp();
eth = new LegacyERC20ETH(); eth = new LegacyERC20ETH();
} }
......
...@@ -8,7 +8,8 @@ import { Predeploys } from "../libraries/Predeploys.sol"; ...@@ -8,7 +8,8 @@ import { Predeploys } from "../libraries/Predeploys.sol";
contract LegacyMessagePasser_Test is CommonTest { contract LegacyMessagePasser_Test is CommonTest {
LegacyMessagePasser messagePasser; LegacyMessagePasser messagePasser;
function setUp() external { function setUp() public virtual override {
super.setUp();
messagePasser = new LegacyMessagePasser(); messagePasser = new LegacyMessagePasser();
} }
......
...@@ -5,10 +5,6 @@ import { CommonTest } from "./CommonTest.t.sol"; ...@@ -5,10 +5,6 @@ import { CommonTest } from "./CommonTest.t.sol";
import { MerkleTrie } from "../libraries/trie/MerkleTrie.sol"; import { MerkleTrie } from "../libraries/trie/MerkleTrie.sol";
contract MerkleTrie_get_Test is CommonTest { contract MerkleTrie_get_Test is CommonTest {
function setUp() public {
_setUp();
}
function test_get_validProof1_succeeds() external { function test_get_validProof1_succeeds() external {
bytes32 root = 0xd582f99275e227a1cf4284899e5ff06ee56da8859be71b553397c69151bc942f; bytes32 root = 0xd582f99275e227a1cf4284899e5ff06ee56da8859be71b553397c69151bc942f;
bytes memory key = hex"6b6579326262"; bytes memory key = hex"6b6579326262";
......
...@@ -11,7 +11,9 @@ contract MintManager_Initializer is CommonTest { ...@@ -11,7 +11,9 @@ contract MintManager_Initializer is CommonTest {
GovernanceToken internal gov; GovernanceToken internal gov;
MintManager internal manager; MintManager internal manager;
function setUp() external { function setUp() public virtual override {
super.setUp();
vm.prank(owner); vm.prank(owner);
gov = new GovernanceToken(); gov = new GovernanceToken();
......
...@@ -11,10 +11,83 @@ import { Hashing } from "../libraries/Hashing.sol"; ...@@ -11,10 +11,83 @@ import { Hashing } from "../libraries/Hashing.sol";
import { Proxy } from "../universal/Proxy.sol"; import { Proxy } from "../universal/Proxy.sol";
contract OptimismPortal_Test is Portal_Initializer { contract OptimismPortal_Test is Portal_Initializer {
event Paused(address);
event Unpaused(address);
function test_constructor_succeeds() external { function test_constructor_succeeds() external {
assertEq(op.FINALIZATION_PERIOD_SECONDS(), 7 days); assertEq(op.FINALIZATION_PERIOD_SECONDS(), 7 days);
assertEq(address(op.L2_ORACLE()), address(oracle)); assertEq(address(op.L2_ORACLE()), address(oracle));
assertEq(op.l2Sender(), 0x000000000000000000000000000000000000dEaD); assertEq(op.l2Sender(), 0x000000000000000000000000000000000000dEaD);
assertEq(op.paused(), false);
}
/**
* @notice The OptimismPortal can be paused by the GUARDIAN
*/
function test_pause_succeeds() external {
address guardian = op.GUARDIAN();
assertEq(op.paused(), false);
vm.expectEmit(true, true, true, true, address(op));
emit Paused(guardian);
vm.prank(guardian);
op.pause();
assertEq(op.paused(), true);
}
/**
* @notice The OptimismPortal reverts when an account that is not the
* GUARDIAN calls `pause()`
*/
function test_pause_onlyGuardian_reverts() external {
assertEq(op.paused(), false);
assertTrue(op.GUARDIAN() != alice);
vm.expectRevert("OptimismPortal: only guardian can pause");
vm.prank(alice);
op.pause();
assertEq(op.paused(), false);
}
/**
* @notice The OptimismPortal can be unpaused by the GUARDIAN
*/
function test_unpause_succeeds() external {
address guardian = op.GUARDIAN();
vm.prank(guardian);
op.pause();
assertEq(op.paused(), true);
vm.expectEmit(true, true, true, true, address(op));
emit Unpaused(guardian);
vm.prank(guardian);
op.unpause();
assertEq(op.paused(), false);
}
/**
* @notice The OptimismPortal reverts when an account that is not
* the GUARDIAN calls `unpause()`
*/
function test_unpause_onlyGuardian_reverts() external {
address guardian = op.GUARDIAN();
vm.prank(guardian);
op.pause();
assertEq(op.paused(), true);
assertTrue(op.GUARDIAN() != alice);
vm.expectRevert("OptimismPortal: only guardian can unpause");
vm.prank(alice);
op.unpause();
assertEq(op.paused(), true);
} }
function test_receive_succeeds() external { function test_receive_succeeds() external {
...@@ -343,6 +416,22 @@ contract OptimismPortal_FinalizeWithdrawal_Test is Portal_Initializer { ...@@ -343,6 +416,22 @@ contract OptimismPortal_FinalizeWithdrawal_Test is Portal_Initializer {
assertFalse(op.finalizedWithdrawals(Hashing.hashWithdrawal(_defaultTx))); assertFalse(op.finalizedWithdrawals(Hashing.hashWithdrawal(_defaultTx)));
} }
/**
* @notice Proving withdrawal transactions should revert when paused
*/
function test_proveWithdrawalTransaction_paused_reverts() external {
vm.prank(op.GUARDIAN());
op.pause();
vm.expectRevert("OptimismPortal: paused");
op.proveWithdrawalTransaction({
_tx: _defaultTx,
_l2OutputIndex: _proposedOutputIndex,
_outputRootProof: _outputRootProof,
_withdrawalProof: _withdrawalProof
});
}
// Test: proveWithdrawalTransaction cannot prove a withdrawal with itself (the OptimismPortal) as the target. // Test: proveWithdrawalTransaction cannot prove a withdrawal with itself (the OptimismPortal) as the target.
function test_proveWithdrawalTransaction_onSelfCall_reverts() external { function test_proveWithdrawalTransaction_onSelfCall_reverts() external {
_defaultTx.target = address(op); _defaultTx.target = address(op);
...@@ -539,6 +628,17 @@ contract OptimismPortal_FinalizeWithdrawal_Test is Portal_Initializer { ...@@ -539,6 +628,17 @@ contract OptimismPortal_FinalizeWithdrawal_Test is Portal_Initializer {
assert(address(bob).balance == bobBalanceBefore + 100); assert(address(bob).balance == bobBalanceBefore + 100);
} }
/**
* @notice Finalizing withdrawal transactions should revert when paused
*/
function test_finalizeWithdrawalTransaction_paused_reverts() external {
vm.prank(op.GUARDIAN());
op.pause();
vm.expectRevert("OptimismPortal: paused");
op.finalizeWithdrawalTransaction(_defaultTx);
}
// Test: finalizeWithdrawalTransaction reverts if the withdrawal has not been proven. // Test: finalizeWithdrawalTransaction reverts if the withdrawal has not been proven.
function test_finalizeWithdrawalTransaction_ifWithdrawalNotProven_reverts() external { function test_finalizeWithdrawalTransaction_ifWithdrawalNotProven_reverts() external {
uint256 bobBalanceBefore = address(bob).balance; uint256 bobBalanceBefore = address(bob).balance;
...@@ -956,12 +1056,12 @@ contract OptimismPortalUpgradeable_Test is Portal_Initializer { ...@@ -956,12 +1056,12 @@ contract OptimismPortalUpgradeable_Test is Portal_Initializer {
function test_initialize_cannotInitProxy_reverts() external { function test_initialize_cannotInitProxy_reverts() external {
vm.expectRevert("Initializable: contract is already initialized"); vm.expectRevert("Initializable: contract is already initialized");
OptimismPortal(payable(proxy)).initialize(); OptimismPortal(payable(proxy)).initialize(false);
} }
function test_initialize_cannotInitImpl_reverts() external { function test_initialize_cannotInitImpl_reverts() external {
vm.expectRevert("Initializable: contract is already initialized"); vm.expectRevert("Initializable: contract is already initialized");
OptimismPortal(opImpl).initialize(); OptimismPortal(opImpl).initialize(false);
} }
function test_upgradeToAndCall_upgrading_succeeds() external { function test_upgradeToAndCall_upgrading_succeeds() external {
......
...@@ -21,8 +21,8 @@ contract ResourceMetering_Test is CommonTest { ...@@ -21,8 +21,8 @@ contract ResourceMetering_Test is CommonTest {
MeterUser internal meter; MeterUser internal meter;
uint64 initialBlockNum; uint64 initialBlockNum;
function setUp() external { function setUp() public virtual override {
_setUp(); super.setUp();
meter = new MeterUser(); meter = new MeterUser();
initialBlockNum = uint64(block.number); initialBlockNum = uint64(block.number);
} }
......
...@@ -19,7 +19,7 @@ contract Semver_Test is CommonTest { ...@@ -19,7 +19,7 @@ contract Semver_Test is CommonTest {
/** /**
* @notice Deploy a Semver contract * @notice Deploy a Semver contract
*/ */
function setUp() external { function setUp() public virtual override {
semver = new Semver(7, 8, 0); semver = new Semver(7, 8, 0);
} }
......
...@@ -28,13 +28,13 @@ contract SequencerFeeVault_Test is Bridge_Initializer { ...@@ -28,13 +28,13 @@ contract SequencerFeeVault_Test is Bridge_Initializer {
} }
function test_receive_succeeds() external { function test_receive_succeeds() external {
assertEq(address(vault).balance, 0); uint256 balance = address(vault).balance;
vm.prank(alice); vm.prank(alice);
(bool success, ) = address(vault).call{ value: 100 }(hex""); (bool success, ) = address(vault).call{ value: 100 }(hex"");
assertEq(success, true); assertEq(success, true);
assertEq(address(vault).balance, 100); assertEq(address(vault).balance, balance + 100);
} }
function test_withdraw_notEnough_reverts() external { function test_withdraw_notEnough_reverts() external {
......
...@@ -7,7 +7,8 @@ import { SystemConfig } from "../L1/SystemConfig.sol"; ...@@ -7,7 +7,8 @@ import { SystemConfig } from "../L1/SystemConfig.sol";
contract SystemConfig_Init is CommonTest { contract SystemConfig_Init is CommonTest {
SystemConfig sysConf; SystemConfig sysConf;
function setUp() external { function setUp() public virtual override {
super.setUp();
sysConf = new SystemConfig({ sysConf = new SystemConfig({
_owner: alice, _owner: alice,
_overhead: 2100, _overhead: 2100,
......
...@@ -94,7 +94,7 @@ contract RelayActor is StdUtils { ...@@ -94,7 +94,7 @@ contract RelayActor is StdUtils {
contract XDM_MinGasLimits is Messenger_Initializer { contract XDM_MinGasLimits is Messenger_Initializer {
RelayActor actor; RelayActor actor;
function setUp() public override { function setUp() public virtual override {
// Set up the `L1CrossDomainMessenger` and `OptimismPortal` contracts. // Set up the `L1CrossDomainMessenger` and `OptimismPortal` contracts.
super.setUp(); super.setUp();
......
...@@ -2,9 +2,9 @@ ...@@ -2,9 +2,9 @@
"l1ChainID": 900, "l1ChainID": 900,
"l2ChainID": 901, "l2ChainID": 901,
"l2BlockTime": 2, "l2BlockTime": 2,
"maxSequencerDrift": 10, "maxSequencerDrift": 300,
"sequencerWindowSize": 15, "sequencerWindowSize": 200,
"channelTimeout": 40, "channelTimeout": 120,
"p2pSequencerAddress": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc", "p2pSequencerAddress": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc",
"batchInboxAddress": "0xff00000000000000000000000000000000000000", "batchInboxAddress": "0xff00000000000000000000000000000000000000",
"batchSenderAddress": "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC", "batchSenderAddress": "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC",
......
...@@ -8,17 +8,42 @@ import { ...@@ -8,17 +8,42 @@ import {
} from '../src/deploy-utils' } from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => { const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
const isLiveDeployer =
deployer.toLowerCase() === hre.deployConfig.controller.toLowerCase()
const L2OutputOracleProxy = await getContractFromArtifact( const L2OutputOracleProxy = await getContractFromArtifact(
hre, hre,
'L2OutputOracleProxy' 'L2OutputOracleProxy'
) )
const finalSystemOwner = hre.deployConfig.finalSystemOwner
const finalSystemOwnerCode = await hre.ethers.provider.getCode(
finalSystemOwner
)
if (finalSystemOwnerCode === '0x') {
console.log(
`WARNING: setting OptimismPortal.GUARDIAN to ${finalSystemOwner} and it has no code`
)
if (!isLiveDeployer) {
throw new Error(
'Do not deploy to production networks without the GUARDIAN being a contract'
)
}
}
// Deploy the OptimismPortal implementation as paused to
// ensure that users do not interact with it and instead
// interact with the proxied contract.
// The `finalSystemOwner` is set at the GUARDIAN.
await deploy({ await deploy({
hre, hre,
name: 'OptimismPortal', name: 'OptimismPortal',
args: [ args: [
L2OutputOracleProxy.address, L2OutputOracleProxy.address,
hre.deployConfig.finalizationPeriodSeconds, hre.deployConfig.finalizationPeriodSeconds,
finalSystemOwner,
true, // paused
], ],
postDeployAction: async (contract) => { postDeployAction: async (contract) => {
await assertContractVariable( await assertContractVariable(
...@@ -31,6 +56,11 @@ const deployFn: DeployFunction = async (hre) => { ...@@ -31,6 +56,11 @@ const deployFn: DeployFunction = async (hre) => {
'FINALIZATION_PERIOD_SECONDS', 'FINALIZATION_PERIOD_SECONDS',
hre.deployConfig.finalizationPeriodSeconds hre.deployConfig.finalizationPeriodSeconds
) )
await assertContractVariable(
contract,
'GUARDIAN',
hre.deployConfig.finalSystemOwner
)
}, },
}) })
} }
......
...@@ -171,21 +171,24 @@ const deployFn: DeployFunction = async (hre) => { ...@@ -171,21 +171,24 @@ const deployFn: DeployFunction = async (hre) => {
deployL2StartingTimestamp = l1StartingBlock.timestamp deployL2StartingTimestamp = l1StartingBlock.timestamp
} }
await SystemDictator.updateL2OutputOracleDynamicConfig({ await SystemDictator.updateDynamicConfig(
l2OutputOracleStartingBlockNumber: {
hre.deployConfig.l2OutputOracleStartingBlockNumber, l2OutputOracleStartingBlockNumber:
l2OutputOracleStartingTimestamp: deployL2StartingTimestamp, hre.deployConfig.l2OutputOracleStartingBlockNumber,
}) l2OutputOracleStartingTimestamp: deployL2StartingTimestamp,
},
false // do not pause the the OptimismPortal when initializing
)
} else { } else {
const tx = const tx = await SystemDictator.populateTransaction.updateDynamicConfig(
await SystemDictator.populateTransaction.updateL2OutputOracleDynamicConfig( {
{ l2OutputOracleStartingBlockNumber:
l2OutputOracleStartingBlockNumber: hre.deployConfig.l2OutputOracleStartingBlockNumber,
hre.deployConfig.l2OutputOracleStartingBlockNumber, l2OutputOracleStartingTimestamp:
l2OutputOracleStartingTimestamp: hre.deployConfig.l2OutputOracleStartingTimestamp,
hre.deployConfig.l2OutputOracleStartingTimestamp, },
} true
) )
console.log(`Please update dynamic oracle config...`) console.log(`Please update dynamic oracle config...`)
console.log(`MSD address: ${SystemDictator.address}`) console.log(`MSD address: ${SystemDictator.address}`)
console.log(`JSON:`) console.log(`JSON:`)
...@@ -243,6 +246,12 @@ const deployFn: DeployFunction = async (hre) => { ...@@ -243,6 +246,12 @@ const deployFn: DeployFunction = async (hre) => {
(await hre.ethers.provider.getBalance(L1StandardBridge.address)).eq(0) (await hre.ethers.provider.getBalance(L1StandardBridge.address)).eq(0)
) )
if (isLiveDeployer) {
await assertContractVariable(OptimismPortal, 'paused', false)
} else {
await assertContractVariable(OptimismPortal, 'paused', true)
}
// Check L1CrossDomainMessenger was initialized properly. // Check L1CrossDomainMessenger was initialized properly.
await assertContractVariable(L1CrossDomainMessenger, 'paused', true) await assertContractVariable(L1CrossDomainMessenger, 'paused', true)
try { try {
...@@ -283,6 +292,35 @@ const deployFn: DeployFunction = async (hre) => { ...@@ -283,6 +292,35 @@ const deployFn: DeployFunction = async (hre) => {
}, },
}) })
if (await isStep(SystemDictator, 6)) {
console.log(`
Unpause the OptimismPortal. The GUARDIAN account should be used. In practice
this is the multisig. In test networks, the OptimismPortal is initialized
without being paused.
`)
if (isLiveDeployer) {
console.log('WARNING: OptimismPortal configured to not be paused')
console.log('This should only happen for test environments')
await assertContractVariable(OptimismPortal, 'paused', false)
} else {
const tx = await OptimismPortal.populateTransaction.unpause()
console.log(`Please unpause the OptimismPortal...`)
console.log(`OptimismPortal address: ${OptimismPortal.address}`)
console.log(`JSON:`)
console.log(jsonifyTransaction(tx))
}
await awaitCondition(
async () => {
const paused = await OptimismPortal.paused()
return !paused
},
30000,
1000
)
}
// Step 6 unpauses the new L1CrossDomainMessenger. // Step 6 unpauses the new L1CrossDomainMessenger.
await doStep({ await doStep({
isLiveDeployer, isLiveDeployer,
......
...@@ -19,6 +19,6 @@ This invariant asserts that there is no chain of calls that can be made that wil ...@@ -19,6 +19,6 @@ This invariant asserts that there is no chain of calls that can be made that wil
## Deposits of any value should always succeed unless `_to` = `address(0)` or `_isCreation` = `true`. ## Deposits of any value should always succeed unless `_to` = `address(0)` or `_isCreation` = `true`.
**Test:** [`FuzzOptimismPortal.sol#L37`](../contracts/echidna/FuzzOptimismPortal.sol#L37) **Test:** [`FuzzOptimismPortal.sol#L42`](../contracts/echidna/FuzzOptimismPortal.sol#L42)
All deposits, barring creation transactions and transactions sent to `address(0)`, should always succeed. All deposits, barring creation transactions and transactions sent to `address(0)`, should always succeed.
...@@ -79,7 +79,7 @@ ...@@ -79,7 +79,7 @@
"dotenv": "^16.0.0", "dotenv": "^16.0.0",
"ds-test": "https://github.com/dapphub/ds-test.git#9310e879db8ba3ea6d5c6489a579118fd264a3f5", "ds-test": "https://github.com/dapphub/ds-test.git#9310e879db8ba3ea6d5c6489a579118fd264a3f5",
"ethereum-waffle": "^3.0.0", "ethereum-waffle": "^3.0.0",
"forge-std": "https://github.com/foundry-rs/forge-std.git#a2edd39db95df7e9dd3f9ef9edc8c55fefddb6df", "forge-std": "https://github.com/foundry-rs/forge-std.git#fd86115ed6aba8e234ee0fb86c12fe35eff0b2a0",
"glob": "^7.1.6", "glob": "^7.1.6",
"hardhat-deploy": "^0.11.4", "hardhat-deploy": "^0.11.4",
"solhint": "^3.3.7", "solhint": "^3.3.7",
......
...@@ -525,7 +525,7 @@ const check = { ...@@ -525,7 +525,7 @@ const check = {
signer signer
) )
await assertSemver(L2ERC721Bridge, 'L2ERC721Bridge') await assertSemver(L2ERC721Bridge, 'L2ERC721Bridge', '1.1.0')
const MESSENGER = await L2ERC721Bridge.MESSENGER() const MESSENGER = await L2ERC721Bridge.MESSENGER()
assert(MESSENGER !== hre.ethers.constants.AddressZero) assert(MESSENGER !== hre.ethers.constants.AddressZero)
......
...@@ -30,7 +30,7 @@ export const advancedQueryFilter = async ( ...@@ -30,7 +30,7 @@ export const advancedQueryFilter = async (
const allEvents = await contract.queryFilter( const allEvents = await contract.queryFilter(
options.queryFilter, options.queryFilter,
i, i,
i + step Math.min(i + step, end)
) )
const matching = options.filter const matching = options.filter
? allEvents.filter(options.filter) ? allEvents.filter(options.filter)
......
{
"name": "@eth-optimism/proxyd",
"version": "3.14.1",
"private": true,
"dependencies": {}
}
...@@ -9518,9 +9518,9 @@ forever-agent@~0.6.1: ...@@ -9518,9 +9518,9 @@ forever-agent@~0.6.1:
version "1.2.0" version "1.2.0"
resolved "https://github.com/foundry-rs/forge-std.git#53331f4cb2e313466f72440f3e73af048c454d02" resolved "https://github.com/foundry-rs/forge-std.git#53331f4cb2e313466f72440f3e73af048c454d02"
"forge-std@https://github.com/foundry-rs/forge-std.git#a2edd39db95df7e9dd3f9ef9edc8c55fefddb6df": "forge-std@https://github.com/foundry-rs/forge-std.git#fd86115ed6aba8e234ee0fb86c12fe35eff0b2a0":
version "1.4.0" version "1.4.0"
resolved "https://github.com/foundry-rs/forge-std.git#a2edd39db95df7e9dd3f9ef9edc8c55fefddb6df" resolved "https://github.com/foundry-rs/forge-std.git#fd86115ed6aba8e234ee0fb86c12fe35eff0b2a0"
form-data@^2.2.0: form-data@^2.2.0:
version "2.5.1" version "2.5.1"
......
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