Commit 41f36560 authored by clabby's avatar clabby

Merge branch 'develop' into @clabby/ctb/fix-withdrawal-dos

parents 599101ca c3235dfb
---
'@eth-optimism/contracts-bedrock': patch
---
Updates L2OutputOracle to easily delete multiple outputs at once
---
'@eth-optimism/contracts-bedrock': patch
---
Allows owner and proposer addresses to be the same in L2OutputOracle
......@@ -83,8 +83,7 @@ jobs:
- "packages/contracts/libraries"
- "packages/contracts/standards"
docker-publish:
docker-build:
environment:
DOCKER_BUILDKIT: 1
parameters:
......@@ -100,10 +99,54 @@ jobs:
docker_context:
description: Docker build context
type: string
target:
description: Docker build target
registry:
description: Docker registry
type: string
default: "us-central1-docker.pkg.dev"
repo:
description: Docker repo
type: string
default: "bedrock-goerli-development/images"
machine:
image: ubuntu-2204:2022.07.1
resource_class: xlarge
steps:
- checkout
- run:
command: mkdir -p /tmp/docker_images
- run:
name: Build
command: |
# Check to see if DOCKER_HUB_READ_ONLY_TOKEN is set (i.e. we are in repo) before attempting to use secrets.
# Building should work without this read only login, but may get rate limited.
if [[ -v DOCKER_HUB_READ_ONLY_TOKEN ]]; then
echo "$DOCKER_HUB_READ_ONLY_TOKEN" | docker login -u "$DOCKER_HUB_READ_ONLY_USER" --password-stdin
fi
IMAGE_BASE="<<parameters.registry>>/<<parameters.repo>>/<<parameters.docker_name>>"
DOCKER_TAGS=$(echo -ne <<parameters.docker_tags>> | sed "s/,/\n/g" | sed "s/[^a-zA-Z0-9\n]/-/g" | sed -e "s|^|-t ${IMAGE_BASE}:|")
docker build \
$(echo -ne $DOCKER_TAGS | tr '\n' ' ') \
-f <<parameters.docker_file>> \
<<parameters.docker_context>>
- run:
name: Save
command: |
IMAGE_BASE="<<parameters.registry>>/<<parameters.repo>>/<<parameters.docker_name>>"
DOCKER_LABELS=$(echo -ne <<parameters.docker_tags>> | sed "s/,/\n/g" | sed "s/[^a-zA-Z0-9\n]/-/g")
echo -ne $DOCKER_LABELS | tr ' ' '\n' | xargs -I {} docker save -o /tmp/docker_images/<<parameters.docker_name>>_{}.tar $IMAGE_BASE:{}
- persist_to_workspace:
root: /tmp/docker_images
paths:
- "."
docker-publish:
parameters:
docker_name:
description: Docker image name
type: string
docker_tags:
description: Docker image tags as csv
type: string
default: ""
registry:
description: Docker registry
type: string
......@@ -116,6 +159,13 @@ jobs:
image: ubuntu-2204:2022.07.1
resource_class: xlarge
steps:
- attach_workspace:
at: /tmp/docker_images
- run:
name: Docker load
command: |
DOCKER_LABELS=$(echo -ne <<parameters.docker_tags>> | sed "s/,/\n/g" | sed "s/[^a-zA-Z0-9\n]/-/g")
echo -ne $DOCKER_LABELS | tr ' ' '\n' | xargs -I {} docker load -i /tmp/docker_images/<<parameters.docker_name>>_{}.tar
- gcp-oidc-authenticate
# Below is CircleCI recommended way of specifying nameservers on an Ubuntu box:
# https://support.circleci.com/hc/en-us/articles/7323511028251-How-to-set-custom-DNS-on-Ubuntu-based-images-using-netplan
......@@ -124,34 +174,6 @@ jobs:
- run: sudo sed -i "s/addresses:/ addresses":" [8.8.8.8, 8.8.4.4] /g" /etc/netplan/50-cloud-init.yaml
- run: cat /etc/netplan/50-cloud-init.yaml
- run: sudo netplan apply
- checkout
- when:
condition: <<parameters.target>>
steps:
- run:
name: Build with context
command: |
echo "$DOCKER_HUB_READ_ONLY_TOKEN" | docker login -u "$DOCKER_HUB_READ_ONLY_USER" --password-stdin
IMAGE_BASE="<<parameters.registry>>/<<parameters.repo>>/<<parameters.docker_name>>"
DOCKER_TAGS=$(echo -ne <<parameters.docker_tags>> | sed "s/,/\n/g" | sed "s/[^a-zA-Z0-9\n]/-/g" | sed -e "s|^|-t ${IMAGE_BASE}:|")
docker build \
$(echo -ne $DOCKER_TAGS | tr '\n' ' ') \
-f <<parameters.docker_file>> \
--target <<parameters.target>> \
<<parameters.docker_context>>
- unless:
condition: <<parameters.target>>
steps:
- run:
name: Build
command: |
echo "$DOCKER_HUB_READ_ONLY_TOKEN" | docker login -u "$DOCKER_HUB_READ_ONLY_USER" --password-stdin
IMAGE_BASE="<<parameters.registry>>/<<parameters.repo>>/<<parameters.docker_name>>"
DOCKER_TAGS=$(echo -ne <<parameters.docker_tags>> | sed "s/,/\n/g" | sed "s/[^a-zA-Z0-9\n]/-/g" | sed -e "s|^|-t ${IMAGE_BASE}:|")
docker build \
$(echo -ne $DOCKER_TAGS | tr '\n' ' ') \
-f <<parameters.docker_file>> \
<<parameters.docker_context>>
- run:
name: Publish
command: |
......@@ -690,6 +712,14 @@ jobs:
docker_layer_caching: true
resource_class: xlarge
steps:
- attach_workspace:
at: /tmp/docker_images
- run:
name: Docker Load
command: |
docker load -i /tmp/docker_images/op-batcher_<<parameters.version>>.tar
docker load -i /tmp/docker_images/op-proposer_<<parameters.version>>.tar
docker load -i /tmp/docker_images/op-node_<<parameters.version>>.tar
- run:
command: git clone https://github.com/ethereum-optimism/hive.git .
- go/load-cache
......@@ -701,7 +731,6 @@ jobs:
./hive \
-sim=<<parameters.sim>> \
-sim.loglevel=5 \
-docker.pull=true \
-client=go-ethereum,op-geth_optimism-history,op-proposer_<<parameters.version>>,op-batcher_<<parameters.version>>,op-node_<<parameters.version>> |& tee /tmp/hive.log || echo "failed."
- run:
command: |
......@@ -920,8 +949,8 @@ workflows:
- op-service-tests
- op-e2e-WS-tests
- op-e2e-HTTP-tests
- docker-publish:
name: op-node-publish-dev
- docker-build:
name: op-node-docker-build
docker_file: op-node/Dockerfile
docker_name: op-node
docker_tags: <<pipeline.git.revision>>,<<pipeline.git.branch>>
......@@ -929,7 +958,15 @@ workflows:
context:
- gcr
- docker-publish:
name: op-batcher-publish-dev
name: op-node-docker-publish
docker_name: op-node
docker_tags: <<pipeline.git.revision>>,<<pipeline.git.branch>>
context:
- gcr
requires:
- op-node-docker-build
- docker-build:
name: op-batcher-docker-build
docker_file: op-batcher/Dockerfile
docker_name: op-batcher
docker_tags: <<pipeline.git.revision>>,<<pipeline.git.branch>>
......@@ -937,37 +974,53 @@ workflows:
context:
- gcr
- docker-publish:
name: op-proposer-publish-dev
name: op-batcher-docker-publish
docker_name: op-batcher
docker_tags: <<pipeline.git.revision>>,<<pipeline.git.branch>>
context:
- gcr
requires:
- op-batcher-docker-build
- docker-build:
name: op-proposer-docker-build
docker_file: op-proposer/Dockerfile
docker_name: op-proposer
docker_tags: <<pipeline.git.revision>>,<<pipeline.git.branch>>
docker_context: .
context:
- gcr
- docker-publish:
name: op-proposer-docker-publish
docker_name: op-proposer
docker_tags: <<pipeline.git.revision>>,<<pipeline.git.branch>>
context:
- gcr
requires:
- op-proposer-docker-build
- hive-test:
name: hive-test-rpc
version: <<pipeline.git.revision>>
sim: optimism/rpc
requires:
- op-node-publish-dev
- op-batcher-publish-dev
- op-proposer-publish-dev
- op-node-docker-build
- op-batcher-docker-build
- op-proposer-docker-build
- hive-test:
name: hive-test-p2p
version: <<pipeline.git.revision>>
sim: optimism/p2p
requires:
- op-node-publish-dev
- op-batcher-publish-dev
- op-proposer-publish-dev
- op-node-docker-build
- op-batcher-docker-build
- op-proposer-docker-build
- hive-test:
name: hive-test-l1ops
version: <<pipeline.git.revision>>
sim: optimism/l1ops
requires:
- op-node-publish-dev
- op-batcher-publish-dev
- op-proposer-publish-dev
- op-node-docker-build
- op-batcher-docker-build
- op-proposer-docker-build
release:
jobs:
- docker-tag-op-stack-release:
......
......@@ -61,7 +61,7 @@ def main():
addresses = read_json(addresses_json_path)
else:
log.info('Deploying contracts.')
run_command(['yarn', 'hardhat', '--network', 'devnetL1', 'deploy', '--tags', 'fresh'], env={
run_command(['yarn', 'hardhat', '--network', 'devnetL1', 'deploy'], env={
'CHAIN_ID': '900',
'L1_RPC': 'http://localhost:8545',
'PRIVATE_KEY_DEPLOYER': 'ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'
......
......@@ -606,6 +606,7 @@ google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX
google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.33.2 h1:EQyQC3sa8M+p6Ulc8yy9SWSS2GVwyRc83gAbG8lrl4o=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
......
......@@ -4,9 +4,9 @@ go 1.18
require (
github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3
github.com/ethereum-optimism/optimism/op-node v0.10.0
github.com/ethereum-optimism/optimism/op-proposer v0.10.0
github.com/ethereum-optimism/optimism/op-service v0.10.0
github.com/ethereum-optimism/optimism/op-node v0.10.1
github.com/ethereum-optimism/optimism/op-proposer v0.10.1
github.com/ethereum-optimism/optimism/op-service v0.10.1
github.com/ethereum/go-ethereum v1.10.26
github.com/urfave/cli v1.22.9
)
......@@ -23,7 +23,7 @@ require (
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/deckarep/golang-set v1.8.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect
github.com/ethereum-optimism/optimism/op-bindings v0.10.0 // indirect
github.com/ethereum-optimism/optimism/op-bindings v0.10.1 // indirect
github.com/fjl/memsize v0.0.1 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-stack/stack v1.8.1 // indirect
......
......@@ -106,14 +106,14 @@ github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3 h1:RWHKLhCrQThMfch+QJ1Z
github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3/go.mod h1:QziizLAiF0KqyLdNJYD7O5cpDlaFMNZzlxYNcWsJUxs=
github.com/ethereum-optimism/op-geth v0.0.0-20221104231810-30db39cae2be h1:8TdM3M7FjZkrYeGGX9nEVtDDlZ5RiuHtc0mbi5bGKyY=
github.com/ethereum-optimism/op-geth v0.0.0-20221104231810-30db39cae2be/go.mod h1:1g9UmZgEINqvYfXmWOUCRJX9fxegeOHudVkLCRAXO5Y=
github.com/ethereum-optimism/optimism/op-bindings v0.10.0 h1:M2nwcOXH9YiRDH6UXnzHI+/eAM5UoFWp7HTlAWrZ4Os=
github.com/ethereum-optimism/optimism/op-bindings v0.10.0/go.mod h1:UeTZlpZyhOL3y9Sogzvbn8Z3q1tDmZEv1VmGxMiZYCg=
github.com/ethereum-optimism/optimism/op-node v0.10.0 h1:I6lzMP596P8PfNdPd/z5aO6uWJJABkPKC8jMV3qg9Pc=
github.com/ethereum-optimism/optimism/op-node v0.10.0/go.mod h1:ihuS69UXdeteQENbAZpmEfw4AiVbNrf2ylWRgu1CpEk=
github.com/ethereum-optimism/optimism/op-proposer v0.10.0 h1:G6UpTX20XWGGmPbcMrdjTQqZ6mKj/r3K0u3BaB1jjJU=
github.com/ethereum-optimism/optimism/op-proposer v0.10.0/go.mod h1:6dgDo0DrJnqdMpvi2YFniCCaOa6AzZ1cJfG5jHmYyWQ=
github.com/ethereum-optimism/optimism/op-service v0.10.0 h1:hkWVsVVFf0ybLWwpSqnPasndiFJLk+u++YStaQRRMFY=
github.com/ethereum-optimism/optimism/op-service v0.10.0/go.mod h1:d7IryyBivUjxhQt91nNBxUainSFmRjwf6H/nCNBowvk=
github.com/ethereum-optimism/optimism/op-bindings v0.10.1 h1:OxzYjPmjl5DblgvS0z27M8lZInjdm9Wt6ajozXuMhmw=
github.com/ethereum-optimism/optimism/op-bindings v0.10.1/go.mod h1:UeTZlpZyhOL3y9Sogzvbn8Z3q1tDmZEv1VmGxMiZYCg=
github.com/ethereum-optimism/optimism/op-node v0.10.1 h1:kVBaOEOYLV22XEHRhB7dfdmoXepO0kx/RsZQK+Bpk1Y=
github.com/ethereum-optimism/optimism/op-node v0.10.1/go.mod h1:pup7wiiUs9g8cZKwXeB5tEGCqwUUwFVmej9MmSIm6S8=
github.com/ethereum-optimism/optimism/op-proposer v0.10.1 h1:2akYgVF+a7aGRRwXx9x+rdeq1MjRc0+BgZTgS9kGmsE=
github.com/ethereum-optimism/optimism/op-proposer v0.10.1/go.mod h1:dQr8k0SMo48u79Eyt2vn3AuPVtWEgGdz24MavQiz2Cg=
github.com/ethereum-optimism/optimism/op-service v0.10.1 h1:s8CisVat3ia04Z0mW3IiwZ7V1EInyVe3ODq6UXSyJG4=
github.com/ethereum-optimism/optimism/op-service v0.10.1/go.mod h1:d7IryyBivUjxhQt91nNBxUainSFmRjwf6H/nCNBowvk=
github.com/fjl/memsize v0.0.1 h1:+zhkb+dhUgx0/e+M8sF0QqiouvMQUiKR+QYvdxIOKcQ=
github.com/fjl/memsize v0.0.1/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
......
This diff is collapsed.
......@@ -3,7 +3,7 @@ module github.com/ethereum-optimism/optimism/op-chain-ops
go 1.18
require (
github.com/ethereum-optimism/optimism/op-bindings v0.10.0
github.com/ethereum-optimism/optimism/op-bindings v0.10.1
github.com/ethereum/go-ethereum v1.10.26
github.com/holiman/uint256 v1.2.0
github.com/mattn/go-isatty v0.0.14
......
......@@ -76,8 +76,8 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/ethereum-optimism/op-geth v0.0.0-20221104231810-30db39cae2be h1:8TdM3M7FjZkrYeGGX9nEVtDDlZ5RiuHtc0mbi5bGKyY=
github.com/ethereum-optimism/op-geth v0.0.0-20221104231810-30db39cae2be/go.mod h1:1g9UmZgEINqvYfXmWOUCRJX9fxegeOHudVkLCRAXO5Y=
github.com/ethereum-optimism/optimism/op-bindings v0.10.0 h1:M2nwcOXH9YiRDH6UXnzHI+/eAM5UoFWp7HTlAWrZ4Os=
github.com/ethereum-optimism/optimism/op-bindings v0.10.0/go.mod h1:UeTZlpZyhOL3y9Sogzvbn8Z3q1tDmZEv1VmGxMiZYCg=
github.com/ethereum-optimism/optimism/op-bindings v0.10.1 h1:OxzYjPmjl5DblgvS0z27M8lZInjdm9Wt6ajozXuMhmw=
github.com/ethereum-optimism/optimism/op-bindings v0.10.1/go.mod h1:UeTZlpZyhOL3y9Sogzvbn8Z3q1tDmZEv1VmGxMiZYCg=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fjl/memsize v0.0.1 h1:+zhkb+dhUgx0/e+M8sF0QqiouvMQUiKR+QYvdxIOKcQ=
github.com/fjl/memsize v0.0.1/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0=
......
......@@ -8,12 +8,12 @@ require (
github.com/docker/docker v20.10.21+incompatible
github.com/docker/go-connections v0.4.0
github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3
github.com/ethereum-optimism/optimism/op-batcher v0.10.0
github.com/ethereum-optimism/optimism/op-bindings v0.10.0
github.com/ethereum-optimism/optimism/op-chain-ops v0.10.0
github.com/ethereum-optimism/optimism/op-node v0.10.0
github.com/ethereum-optimism/optimism/op-proposer v0.10.0
github.com/ethereum-optimism/optimism/op-service v0.10.0
github.com/ethereum-optimism/optimism/op-batcher v0.10.1
github.com/ethereum-optimism/optimism/op-bindings v0.10.1
github.com/ethereum-optimism/optimism/op-chain-ops v0.10.1
github.com/ethereum-optimism/optimism/op-node v0.10.1
github.com/ethereum-optimism/optimism/op-proposer v0.10.1
github.com/ethereum-optimism/optimism/op-service v0.10.1
github.com/ethereum/go-ethereum v1.10.26
github.com/libp2p/go-libp2p v0.23.3
github.com/stretchr/testify v1.8.0
......
......@@ -159,16 +159,18 @@ github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3 h1:RWHKLhCrQThMfch+QJ1Z
github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3/go.mod h1:QziizLAiF0KqyLdNJYD7O5cpDlaFMNZzlxYNcWsJUxs=
github.com/ethereum-optimism/op-geth v0.0.0-20221104231810-30db39cae2be h1:8TdM3M7FjZkrYeGGX9nEVtDDlZ5RiuHtc0mbi5bGKyY=
github.com/ethereum-optimism/op-geth v0.0.0-20221104231810-30db39cae2be/go.mod h1:1g9UmZgEINqvYfXmWOUCRJX9fxegeOHudVkLCRAXO5Y=
github.com/ethereum-optimism/optimism/op-batcher v0.10.0 h1:mI3udjjhK7FRoYnr7PNRSRPNldm7Vn1X1Kpl2Mixpkg=
github.com/ethereum-optimism/optimism/op-batcher v0.10.0/go.mod h1:u14Dchn2F0sdWxH/V6wtNVFCQJAfrURb/mwvYP/RiMk=
github.com/ethereum-optimism/optimism/op-bindings v0.10.0 h1:M2nwcOXH9YiRDH6UXnzHI+/eAM5UoFWp7HTlAWrZ4Os=
github.com/ethereum-optimism/optimism/op-bindings v0.10.0/go.mod h1:UeTZlpZyhOL3y9Sogzvbn8Z3q1tDmZEv1VmGxMiZYCg=
github.com/ethereum-optimism/optimism/op-node v0.10.0 h1:I6lzMP596P8PfNdPd/z5aO6uWJJABkPKC8jMV3qg9Pc=
github.com/ethereum-optimism/optimism/op-node v0.10.0/go.mod h1:ihuS69UXdeteQENbAZpmEfw4AiVbNrf2ylWRgu1CpEk=
github.com/ethereum-optimism/optimism/op-proposer v0.10.0 h1:G6UpTX20XWGGmPbcMrdjTQqZ6mKj/r3K0u3BaB1jjJU=
github.com/ethereum-optimism/optimism/op-proposer v0.10.0/go.mod h1:6dgDo0DrJnqdMpvi2YFniCCaOa6AzZ1cJfG5jHmYyWQ=
github.com/ethereum-optimism/optimism/op-service v0.10.0 h1:hkWVsVVFf0ybLWwpSqnPasndiFJLk+u++YStaQRRMFY=
github.com/ethereum-optimism/optimism/op-service v0.10.0/go.mod h1:d7IryyBivUjxhQt91nNBxUainSFmRjwf6H/nCNBowvk=
github.com/ethereum-optimism/optimism/op-batcher v0.10.1 h1:y/PlBJfmJ74UzbPPLntIfCO/l4yAOeCPPyUsiZd7C9Q=
github.com/ethereum-optimism/optimism/op-batcher v0.10.1/go.mod h1:GUWRM7Bhc05H4gUj8ggTZnegVS4bfltZq0gqXh18Fvk=
github.com/ethereum-optimism/optimism/op-bindings v0.10.1 h1:OxzYjPmjl5DblgvS0z27M8lZInjdm9Wt6ajozXuMhmw=
github.com/ethereum-optimism/optimism/op-bindings v0.10.1/go.mod h1:UeTZlpZyhOL3y9Sogzvbn8Z3q1tDmZEv1VmGxMiZYCg=
github.com/ethereum-optimism/optimism/op-chain-ops v0.10.1 h1:SJls5Zs8mXooHItwCFsxCyOhssuGIJCnucjGJjo+eQ0=
github.com/ethereum-optimism/optimism/op-chain-ops v0.10.1/go.mod h1:b3nDzF9sIqDUVRM58P9d1u7+bWPOEMX3gqyCuL0CMjI=
github.com/ethereum-optimism/optimism/op-node v0.10.1 h1:kVBaOEOYLV22XEHRhB7dfdmoXepO0kx/RsZQK+Bpk1Y=
github.com/ethereum-optimism/optimism/op-node v0.10.1/go.mod h1:pup7wiiUs9g8cZKwXeB5tEGCqwUUwFVmej9MmSIm6S8=
github.com/ethereum-optimism/optimism/op-proposer v0.10.1 h1:2akYgVF+a7aGRRwXx9x+rdeq1MjRc0+BgZTgS9kGmsE=
github.com/ethereum-optimism/optimism/op-proposer v0.10.1/go.mod h1:dQr8k0SMo48u79Eyt2vn3AuPVtWEgGdz24MavQiz2Cg=
github.com/ethereum-optimism/optimism/op-service v0.10.1 h1:s8CisVat3ia04Z0mW3IiwZ7V1EInyVe3ODq6UXSyJG4=
github.com/ethereum-optimism/optimism/op-service v0.10.1/go.mod h1:d7IryyBivUjxhQt91nNBxUainSFmRjwf6H/nCNBowvk=
github.com/fjl/memsize v0.0.1 h1:+zhkb+dhUgx0/e+M8sF0QqiouvMQUiKR+QYvdxIOKcQ=
github.com/fjl/memsize v0.0.1/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0=
github.com/flynn/noise v1.0.0 h1:DlTHqmzmvcEiKj+4RYo/imoswx/4r6iBlCMfVtrMXpQ=
......
......@@ -29,6 +29,13 @@ fuzz:
go test -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzUnmarshallLogEvent ./rollup/derive
go test -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzParseFrames ./rollup/derive
go test -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzFrameUnmarshalBinary ./rollup/derive
go test -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzBatchRoundTrip ./rollup/derive
go test -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzDeriveDepositsRoundTrip ./rollup/derive
go test -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzDeriveDepositsBadVersion ./rollup/derive
go test -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzParseL1InfoDepositTxDataValid ./rollup/derive
go test -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzParseL1InfoDepositTxDataBadLength ./rollup/derive
go test -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzRejectCreateBlockBadTimestamp ./rollup/driver
go test -run NOTAREALTEST -v -fuzztime 10s -fuzz FuzzDecodeDepositTxDataToL1Info ./rollup/driver
.PHONY: \
......
......@@ -6,12 +6,13 @@ require (
github.com/btcsuite/btcd v0.23.3
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0
github.com/ethereum-optimism/optimism/op-bindings v0.10.0
github.com/ethereum-optimism/optimism/op-chain-ops v0.10.0
github.com/ethereum-optimism/optimism/op-service v0.10.0
github.com/ethereum-optimism/optimism/op-bindings v0.10.1
github.com/ethereum-optimism/optimism/op-chain-ops v0.10.1
github.com/ethereum-optimism/optimism/op-service v0.10.1
github.com/ethereum/go-ethereum v1.10.26
github.com/golang/snappy v0.0.4
github.com/google/go-cmp v0.5.8
github.com/google/gofuzz v1.2.1-0.20220503160820-4a35382e8fc8
github.com/hashicorp/go-multierror v1.1.1
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d
github.com/holiman/uint256 v1.2.0
......
......@@ -145,12 +145,12 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/ethereum-optimism/op-geth v0.0.0-20221104231810-30db39cae2be h1:8TdM3M7FjZkrYeGGX9nEVtDDlZ5RiuHtc0mbi5bGKyY=
github.com/ethereum-optimism/op-geth v0.0.0-20221104231810-30db39cae2be/go.mod h1:1g9UmZgEINqvYfXmWOUCRJX9fxegeOHudVkLCRAXO5Y=
github.com/ethereum-optimism/optimism/op-bindings v0.10.0 h1:M2nwcOXH9YiRDH6UXnzHI+/eAM5UoFWp7HTlAWrZ4Os=
github.com/ethereum-optimism/optimism/op-bindings v0.10.0/go.mod h1:UeTZlpZyhOL3y9Sogzvbn8Z3q1tDmZEv1VmGxMiZYCg=
github.com/ethereum-optimism/optimism/op-chain-ops v0.10.0 h1:WwxEw+w7FF3aqqnZoN4ipFK+O1bVrf3/qN4jcm15ek0=
github.com/ethereum-optimism/optimism/op-chain-ops v0.10.0/go.mod h1:TYuGpQKaWofyb3ZotSJjmluGgH0c3gNO40de3C9ueks=
github.com/ethereum-optimism/optimism/op-service v0.10.0 h1:hkWVsVVFf0ybLWwpSqnPasndiFJLk+u++YStaQRRMFY=
github.com/ethereum-optimism/optimism/op-service v0.10.0/go.mod h1:d7IryyBivUjxhQt91nNBxUainSFmRjwf6H/nCNBowvk=
github.com/ethereum-optimism/optimism/op-bindings v0.10.1 h1:OxzYjPmjl5DblgvS0z27M8lZInjdm9Wt6ajozXuMhmw=
github.com/ethereum-optimism/optimism/op-bindings v0.10.1/go.mod h1:UeTZlpZyhOL3y9Sogzvbn8Z3q1tDmZEv1VmGxMiZYCg=
github.com/ethereum-optimism/optimism/op-chain-ops v0.10.1 h1:SJls5Zs8mXooHItwCFsxCyOhssuGIJCnucjGJjo+eQ0=
github.com/ethereum-optimism/optimism/op-chain-ops v0.10.1/go.mod h1:b3nDzF9sIqDUVRM58P9d1u7+bWPOEMX3gqyCuL0CMjI=
github.com/ethereum-optimism/optimism/op-service v0.10.1 h1:s8CisVat3ia04Z0mW3IiwZ7V1EInyVe3ODq6UXSyJG4=
github.com/ethereum-optimism/optimism/op-service v0.10.1/go.mod h1:d7IryyBivUjxhQt91nNBxUainSFmRjwf6H/nCNBowvk=
github.com/fjl/memsize v0.0.1 h1:+zhkb+dhUgx0/e+M8sF0QqiouvMQUiKR+QYvdxIOKcQ=
github.com/fjl/memsize v0.0.1/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
......@@ -250,6 +250,8 @@ github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.1-0.20220503160820-4a35382e8fc8 h1:Ep/joEub9YwcjRY6ND3+Y/w0ncE540RtGatVhtZL0/Q=
github.com/google/gofuzz v1.2.1-0.20220503160820-4a35382e8fc8/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gopacket v1.1.17/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM=
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
......
package derive
import (
"testing"
"github.com/ethereum-optimism/optimism/op-node/testutils/fuzzerutils"
fuzz "github.com/google/gofuzz"
"github.com/stretchr/testify/require"
)
// FuzzBatchRoundTrip executes a fuzz test similar to TestBatchRoundTrip, which tests that arbitrary BatchData will be
// encoded and decoded without loss of its original values.
func FuzzBatchRoundTrip(f *testing.F) {
f.Fuzz(func(t *testing.T, fuzzedData []byte) {
// Create our fuzzer wrapper to generate complex values
typeProvider := fuzz.NewFromGoFuzz(fuzzedData).NilChance(0).MaxDepth(10000).NumElements(0, 0x100).AllowUnexportedFields(true)
fuzzerutils.AddFuzzerFunctions(typeProvider)
// Create our batch data from fuzzed data
var batchData BatchData
typeProvider.Fuzz(&batchData)
// Encode our batch data
enc, err := batchData.MarshalBinary()
require.NoError(t, err)
// Decode our encoded batch data
var dec BatchData
err = dec.UnmarshalBinary(enc)
require.NoError(t, err)
// Ensure the round trip encoding of batch data did not result in data loss
require.Equal(t, &batchData, &dec, "round trip batch encoding/decoding did not match original values")
})
}
package derive
import (
"math/big"
"testing"
"github.com/ethereum-optimism/optimism/op-node/testutils"
"github.com/ethereum-optimism/optimism/op-node/testutils/fuzzerutils"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
fuzz "github.com/google/gofuzz"
"github.com/stretchr/testify/require"
)
// fuzzReceipts is similar to makeReceipts except it uses the fuzzer to populate DepositTx fields.
func fuzzReceipts(typeProvider *fuzz.Fuzzer, blockHash common.Hash, depositContractAddr common.Address) (receipts []*types.Receipt, expectedDeposits []*types.DepositTx) {
// Determine how many receipts to generate (capped)
var receiptCount uint64
typeProvider.Fuzz(&receiptCount)
// Cap our receipt count otherwise we might generate for too long and our fuzzer will assume we hung
if receiptCount > 0x10 {
receiptCount = 0x10
}
// Create every receipt we intend to
logIndex := uint(0)
for i := uint64(0); i < receiptCount; i++ {
// Obtain our fuzz parameters for generating this receipt
var txReceiptValues struct {
GoodReceipt bool
DepositLogs []bool
}
typeProvider.Fuzz(&txReceiptValues)
// Generate a list of transaction receipts
var logs []*types.Log
status := types.ReceiptStatusSuccessful
if txReceiptValues.GoodReceipt {
status = types.ReceiptStatusFailed
}
// Determine if this log will be a deposit log or not and generate it accordingly
for _, isDeposit := range txReceiptValues.DepositLogs {
var ev *types.Log
var err error
if isDeposit {
// Generate a user deposit source
source := UserDepositSource{L1BlockHash: blockHash, LogIndex: uint64(logIndex)}
// Fuzz parameters to construct our deposit log
var fuzzedDepositInfo struct {
FromAddr *common.Address
ToAddr *common.Address
Value *big.Int
Gas uint64
Data []byte
Mint *big.Int
}
typeProvider.Fuzz(&fuzzedDepositInfo)
// Create our deposit transaction
dep := &types.DepositTx{
SourceHash: source.SourceHash(),
From: *fuzzedDepositInfo.FromAddr,
To: fuzzedDepositInfo.ToAddr,
Value: fuzzedDepositInfo.Value,
Gas: fuzzedDepositInfo.Gas,
Data: fuzzedDepositInfo.Data,
Mint: fuzzedDepositInfo.Mint,
IsSystemTransaction: false,
}
// Marshal our actual log event
ev, err = MarshalDepositLogEvent(depositContractAddr, dep)
if err != nil {
panic(err)
}
// If we have a good version and our tx succeeded, we add this to our list of expected deposits to
// return.
if status == types.ReceiptStatusSuccessful {
expectedDeposits = append(expectedDeposits, dep)
}
} else {
// If we're generated an unrelated log event (not deposit), fuzz some random parameters to use.
var randomUnrelatedLogInfo struct {
Addr *common.Address
Topics []common.Hash
Data []byte
}
typeProvider.Fuzz(&randomUnrelatedLogInfo)
// Generate the random log
ev = testutils.GenerateLog(*randomUnrelatedLogInfo.Addr, randomUnrelatedLogInfo.Topics, randomUnrelatedLogInfo.Data)
}
ev.TxIndex = uint(i)
ev.Index = logIndex
ev.BlockHash = blockHash
logs = append(logs, ev)
logIndex++
}
// Add our receipt to our list
receipts = append(receipts, &types.Receipt{
Type: types.DynamicFeeTxType,
Status: status,
Logs: logs,
BlockHash: blockHash,
TransactionIndex: uint(i),
})
}
return
}
// FuzzDeriveDepositsRoundTrip tests the derivation of deposits from transaction receipt event logs. It mixes
// valid and invalid deposit transactions and ensures all valid deposits are derived as expected.
// This is a fuzz test corresponding to TestDeriveUserDeposits.
func FuzzDeriveDepositsRoundTrip(f *testing.F) {
f.Fuzz(func(t *testing.T, fuzzedData []byte) {
// Create our fuzzer wrapper to generate complex values
typeProvider := fuzz.NewFromGoFuzz(fuzzedData).NilChance(0).MaxDepth(10000).NumElements(0, 0x100).Funcs(
func(e *big.Int, c fuzz.Continue) {
var temp [32]byte
c.Fuzz(&temp)
e.SetBytes(temp[:])
},
func(e *common.Hash, c fuzz.Continue) {
var temp [32]byte
c.Fuzz(&temp)
e.SetBytes(temp[:])
},
func(e *common.Address, c fuzz.Continue) {
var temp [20]byte
c.Fuzz(&temp)
e.SetBytes(temp[:])
})
// Create a dummy block hash for this block
var blockHash common.Hash
typeProvider.Fuzz(&blockHash)
// Fuzz to generate some random deposit events
receipts, expectedDeposits := fuzzReceipts(typeProvider, blockHash, MockDepositContractAddr)
// Derive our user deposits from the transaction receipts
derivedDeposits, err := UserDeposits(receipts, MockDepositContractAddr)
require.NoError(t, err)
// Ensure all deposits we derived matched what we expected to receive.
require.Equal(t, len(derivedDeposits), len(expectedDeposits))
for i, derivedDeposit := range derivedDeposits {
expectedDeposit := expectedDeposits[i]
require.Equal(t, expectedDeposit, derivedDeposit)
}
})
}
// FuzzDeriveDepositsBadVersion ensures that if a deposit transaction receipt event log specifies an invalid deposit
// version, no deposits should be derived.
func FuzzDeriveDepositsBadVersion(f *testing.F) {
f.Fuzz(func(t *testing.T, fuzzedData []byte) {
// Create our fuzzer wrapper to generate complex values
typeProvider := fuzz.NewFromGoFuzz(fuzzedData).NilChance(0).MaxDepth(10000).NumElements(0, 0x100)
fuzzerutils.AddFuzzerFunctions(typeProvider)
// Create a dummy block hash for this block
var blockHash common.Hash
typeProvider.Fuzz(&blockHash)
// Fuzz to generate some random deposit events
receipts, _ := fuzzReceipts(typeProvider, blockHash, MockDepositContractAddr)
// Loop through all receipt logs and let the fuzzer determine which (if any) to patch.
hasBadDepositVersion := false
for _, receipt := range receipts {
// TODO: Using a hardcoded index (Topics[3]) here is not ideal. The MarshalDepositLogEvent method should
// be spliced apart to be more configurable for these tests.
// Loop for each log in this receipt and check if it has a deposit event from our contract
for _, log := range receipt.Logs {
if log.Address == MockDepositContractAddr && len(log.Topics) >= 4 && log.Topics[0] == DepositEventABIHash {
// Determine if we should set a bad deposit version for this log
var patchBadDeposit bool
typeProvider.Fuzz(&patchBadDeposit)
if patchBadDeposit {
// Generate any topic but the deposit event versions we support.
// TODO: As opposed to keeping this hardcoded, a method such as IsValidVersion(v) should be
// used here.
badTopic := DepositEventVersion0
for badTopic == DepositEventVersion0 {
typeProvider.Fuzz(&badTopic)
}
// Set our bad topic and update our state
log.Topics[3] = badTopic
hasBadDepositVersion = true
}
}
}
}
// Derive our user deposits from the transaction receipts
_, err := UserDeposits(receipts, MockDepositContractAddr)
// If we patched a bad deposit version this iteration, we should expect an error and not be able to proceed
// further
if hasBadDepositVersion {
require.Errorf(t, err, "")
return
}
require.NoError(t, err, "")
})
}
package derive
import (
"testing"
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-node/testutils"
"github.com/ethereum-optimism/optimism/op-node/testutils/fuzzerutils"
fuzz "github.com/google/gofuzz"
"github.com/stretchr/testify/require"
)
// FuzzParseL1InfoDepositTxDataValid is a fuzz test built from TestParseL1InfoDepositTxData, which constructs random
// L1 deposit tx info and derives a tx from it, then derives the info back from the tx, to ensure round-trip
// derivation is upheld. This generates "valid" data and ensures it is always derived back to original values.
func FuzzParseL1InfoDepositTxDataValid(f *testing.F) {
f.Fuzz(func(t *testing.T, fuzzedData []byte) {
// Create our fuzzer wrapper to generate complex values
typeProvider := fuzz.NewFromGoFuzz(fuzzedData).NilChance(0).MaxDepth(10000).NumElements(0, 0x100)
fuzzerutils.AddFuzzerFunctions(typeProvider)
var l1Info testutils.MockBlockInfo
typeProvider.Fuzz(&l1Info)
var seqNr uint64
typeProvider.Fuzz(&seqNr)
var sysCfg eth.SystemConfig
typeProvider.Fuzz(&sysCfg)
// Create our deposit tx from our info
depTx, err := L1InfoDeposit(seqNr, &l1Info, sysCfg)
require.NoError(t, err, "error creating deposit tx from L1 info")
// Get our info from out deposit tx
res, err := L1InfoDepositTxData(depTx.Data)
require.NoError(t, err, "expected valid deposit info")
// Verify all parameters match in our round trip deriving operations
require.Equal(t, res.Number, l1Info.NumberU64())
require.Equal(t, res.Time, l1Info.Time())
require.True(t, res.BaseFee.Sign() >= 0)
require.Equal(t, res.BaseFee.Bytes(), l1Info.BaseFee().Bytes())
require.Equal(t, res.BlockHash, l1Info.Hash())
require.Equal(t, res.SequenceNumber, seqNr)
require.Equal(t, res.BatcherAddr, sysCfg.BatcherAddr)
require.Equal(t, res.L1FeeOverhead, sysCfg.Overhead)
require.Equal(t, res.L1FeeScalar, sysCfg.Scalar)
})
}
// Reverse of the above test. Accepts a random byte string and attempts to extract L1Info from it,
// then attempts to convert that info back into the tx data and compare it with the original input.
func FuzzDecodeDepositTxDataToL1Info(f *testing.F) {
f.Fuzz(func(t *testing.T, fuzzedData []byte) {
// Get our info from out deposit tx
res, err := L1InfoDepositTxData(fuzzedData)
if err != nil {
return
}
l1Info := testutils.MockBlockInfo{
InfoHash: res.BlockHash,
InfoNum: res.Number,
InfoTime: res.Time,
InfoBaseFee: res.BaseFee,
}
sysCfg := eth.SystemConfig{
BatcherAddr: res.BatcherAddr,
Overhead: res.L1FeeOverhead,
Scalar: res.L1FeeScalar,
GasLimit: uint64(0),
}
depTx, err := L1InfoDeposit(res.SequenceNumber, &l1Info, sysCfg)
require.NoError(t, err, "error creating deposit tx from L1 info")
require.Equal(t, depTx.Data, fuzzedData)
})
}
// FuzzParseL1InfoDepositTxDataBadLength is a fuzz test built from TestParseL1InfoDepositTxData, which constructs
// random L1 deposit tx info and derives a tx from it, then derives the info back from the tx, to ensure round-trip
// derivation is upheld. This generates "invalid" data and ensures it always throws an error where expected.
func FuzzParseL1InfoDepositTxDataBadLength(f *testing.F) {
const expectedDepositTxDataLength = 4 + 32 + 32 + 32 + 32 + 32
f.Fuzz(func(t *testing.T, fuzzedData []byte) {
// Derive a transaction from random fuzzed data
_, err := L1InfoDepositTxData(fuzzedData)
// If the data is null, or too short or too long, we expect an error
if fuzzedData == nil || len(fuzzedData) != expectedDepositTxDataLength {
require.Error(t, err)
}
})
}
// On develop
package driver
import (
"context"
"errors"
"math/rand"
"testing"
"github.com/ethereum-optimism/optimism/op-node/eth"
"github.com/ethereum-optimism/optimism/op-node/metrics"
"github.com/ethereum-optimism/optimism/op-node/rollup"
"github.com/ethereum-optimism/optimism/op-node/testutils"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
"github.com/prometheus/client_golang/prometheus"
"github.com/stretchr/testify/require"
)
type TestDummyOutputImpl struct {
willError bool
SequencerIface
}
func (d TestDummyOutputImpl) CreateNewBlock(ctx context.Context, l2Head eth.L2BlockRef, l2SafeHead eth.BlockID, l2Finalized eth.BlockID, l1Origin eth.L1BlockRef) (eth.L2BlockRef, *eth.ExecutionPayload, error) {
// If we're meant to error, return one
if d.willError {
return l2Head, nil, errors.New("the TestDummyOutputImpl.createNewBlock operation failed")
}
payload := eth.ExecutionPayload{
ParentHash: common.Hash{},
FeeRecipient: common.Address{},
StateRoot: eth.Bytes32{},
ReceiptsRoot: eth.Bytes32{},
LogsBloom: eth.Bytes256{},
PrevRandao: eth.Bytes32{},
BlockNumber: 0,
GasLimit: 0,
GasUsed: 0,
Timestamp: 0,
ExtraData: nil,
BaseFeePerGas: eth.Uint256Quantity{},
BlockHash: common.Hash{},
Transactions: []eth.Data{},
}
return l2Head, &payload, nil
}
type TestDummyDerivationPipeline struct {
DerivationPipeline
l2Head eth.L2BlockRef
l2SafeHead eth.L2BlockRef
l2Finalized eth.L2BlockRef
}
func (d TestDummyDerivationPipeline) Reset() {}
func (d TestDummyDerivationPipeline) Step(ctx context.Context) error { return nil }
func (d TestDummyDerivationPipeline) SetUnsafeHead(head eth.L2BlockRef) {}
func (d TestDummyDerivationPipeline) AddUnsafePayload(payload *eth.ExecutionPayload) {}
func (d TestDummyDerivationPipeline) Finalized() eth.L2BlockRef { return d.l2Head }
func (d TestDummyDerivationPipeline) SafeL2Head() eth.L2BlockRef { return d.l2SafeHead }
func (d TestDummyDerivationPipeline) UnsafeL2Head() eth.L2BlockRef { return d.l2Finalized }
type TestDummyL1OriginSelector struct {
retval eth.L1BlockRef
}
func (l TestDummyL1OriginSelector) FindL1Origin(ctx context.Context, l1Head eth.L1BlockRef, l2Head eth.L2BlockRef) (eth.L1BlockRef, error) {
return l.retval, nil
}
// TestRejectCreateBlockBadTimestamp tests that a block creation with invalid timestamps will be caught.
// This does not test:
// - The findL1Origin call (it is hardcoded to be the head)
// - The outputInterface used to create a new block from a given payload.
// - The DerivationPipeline setting unsafe head (a mock provider is used to pretend to set it)
// - Metrics (only mocked enough to let the method proceed)
// - Publishing (network is set to nil so publishing won't occur)
func TestRejectCreateBlockBadTimestamp(t *testing.T) {
// Create our random provider
rng := rand.New(rand.NewSource(rand.Int63()))
// Create our context for methods to execute under
ctx := context.Background()
// Create our fake L1/L2 heads and link them accordingly
l1HeadRef := testutils.RandomBlockRef(rng)
l2HeadRef := testutils.RandomL2BlockRef(rng)
l2l1OriginBlock := l1HeadRef
l2HeadRef.L1Origin = l2l1OriginBlock.ID()
// Create a rollup config
cfg := rollup.Config{
BlockTime: uint64(60),
Genesis: rollup.Genesis{
L1: l1HeadRef.ID(),
L2: l2HeadRef.ID(),
L2Time: 0x7000, // dummy value
},
}
// Patch our timestamp so we fail
l2HeadRef.Time = l2l1OriginBlock.Time - (cfg.BlockTime * 2)
// Create our outputter
outputProvider := TestDummyOutputImpl{willError: false}
// Create our state
s := Driver{
l1State: &L1State{
l1Head: l1HeadRef,
log: log.New(),
metrics: &metrics.Metrics{TransactionsSequencedTotal: prometheus.NewCounter(prometheus.CounterOpts{})},
},
log: log.New(),
l1OriginSelector: TestDummyL1OriginSelector{retval: l1HeadRef},
config: &cfg,
sequencer: outputProvider,
derivation: TestDummyDerivationPipeline{},
metrics: &metrics.Metrics{TransactionsSequencedTotal: prometheus.NewCounter(prometheus.CounterOpts{})},
}
// Create a new block
// - L2Head's L1Origin, its timestamp should be greater than L1 genesis.
// - L2Head timestamp + BlockTime should be greater than or equal to the L1 Time.
err := s.createNewL2Block(ctx)
// Verify the L1Origin's block number is greater than L1 genesis in our config.
if l2l1OriginBlock.Number < s.config.Genesis.L1.Number {
require.NoError(t, err, "L1Origin block number should be greater than the L1 genesis block number")
}
// Verify the new L2 block to create will have a time stamp equal or newer than our L1 origin block we derive from.
if l2HeadRef.Time+cfg.BlockTime < l2l1OriginBlock.Time {
// If not, we expect a specific error.
// TODO: This isn't the cleanest, we should construct + compare the whole error message.
require.NotNil(t, err)
require.Contains(t, err.Error(), "cannot build L2 block on top")
require.Contains(t, err.Error(), "for time")
require.Contains(t, err.Error(), "before L1 origin")
return
}
// If we expected the outputter to error, capture that here
if outputProvider.willError {
require.NotNil(t, err, "outputInterface failed to createNewBlock, so createNewL2Block should also have failed")
return
}
// Otherwise we should have no error.
require.NoError(t, err, "error raised in TestRejectCreateBlockBadTimestamp")
}
// FuzzRejectCreateBlockBadTimestamp is a property test derived from the TestRejectCreateBlockBadTimestamp unit test.
// It fuzzes timestamps and block times to find a configuration to violate error checking.
func FuzzRejectCreateBlockBadTimestamp(f *testing.F) {
f.Fuzz(func(t *testing.T, randSeed int64, l2Time uint64, blockTime uint64, forceOutputFail bool, currentL2HeadTime uint64) {
// Create our random provider
rng := rand.New(rand.NewSource(randSeed))
// Create our context for methods to execute under
ctx := context.Background()
// Create our fake L1/L2 heads and link them accordingly
l1HeadRef := testutils.RandomBlockRef(rng)
l2HeadRef := testutils.RandomL2BlockRef(rng)
l2l1OriginBlock := l1HeadRef
l2HeadRef.L1Origin = l2l1OriginBlock.ID()
// TODO: Cap our block time so it doesn't overflow
if blockTime > 0x100000 {
blockTime = 0x100000
}
// Create a rollup config
cfg := rollup.Config{
BlockTime: blockTime,
Genesis: rollup.Genesis{
L1: l1HeadRef.ID(),
L2: l2HeadRef.ID(),
L2Time: l2Time, // dummy value
},
}
// Patch our timestamp so we fail
l2HeadRef.Time = currentL2HeadTime
// Create our outputter
outputProvider := TestDummyOutputImpl{willError: forceOutputFail}
// Create our state
s := Driver{
l1State: &L1State{
l1Head: l1HeadRef,
log: log.New(),
metrics: &metrics.Metrics{TransactionsSequencedTotal: prometheus.NewCounter(prometheus.CounterOpts{})},
},
log: log.New(),
l1OriginSelector: TestDummyL1OriginSelector{retval: l1HeadRef},
config: &cfg,
sequencer: outputProvider,
derivation: TestDummyDerivationPipeline{},
metrics: &metrics.Metrics{TransactionsSequencedTotal: prometheus.NewCounter(prometheus.CounterOpts{})},
}
// Create a new block
// - L2Head's L1Origin, its timestamp should be greater than L1 genesis.
// - L2Head timestamp + BlockTime should be greater than or equal to the L1 Time.
err := s.createNewL2Block(ctx)
// Verify the L1Origin's timestamp is greater than L1 genesis in our config.
if l2l1OriginBlock.Number < s.config.Genesis.L1.Number {
require.NoError(t, err)
return
}
// Verify the new L2 block to create will have a time stamp equal or newer than our L1 origin block we derive from.
if l2HeadRef.Time+cfg.BlockTime < l2l1OriginBlock.Time {
// If not, we expect a specific error.
// TODO: This isn't the cleanest, we should construct + compare the whole error message.
require.NotNil(t, err)
require.Contains(t, err.Error(), "cannot build L2 block on top")
require.Contains(t, err.Error(), "for time")
require.Contains(t, err.Error(), "before L1 origin")
return
}
// Otherwise we should have no error.
require.Nil(t, err)
// If we expected the outputter to error, capture that here
if outputProvider.willError {
require.NotNil(t, err, "outputInterface failed to createNewBlock, so createNewL2Block should also have failed")
return
}
// Otherwise we should have no error.
require.NoError(t, err, "L1Origin block number should be greater than the L1 genesis block number")
})
}
package fuzzerutils
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
fuzz "github.com/google/gofuzz"
)
// AddFuzzerFunctions takes a fuzz.Fuzzer and adds a list of functions to handle different
// data types in a fuzzing campaign. It adds support for commonly used types throughout the
// application.
func AddFuzzerFunctions(fuzzer *fuzz.Fuzzer) {
fuzzer.Funcs(
func(e *big.Int, c fuzz.Continue) {
var temp [32]byte
c.Fuzz(&temp)
e.SetBytes(temp[:])
},
func(e *common.Hash, c fuzz.Continue) {
var temp [32]byte
c.Fuzz(&temp)
e.SetBytes(temp[:])
},
func(e *common.Address, c fuzz.Continue) {
var temp [20]byte
c.Fuzz(&temp)
e.SetBytes(temp[:])
},
)
}
......@@ -4,9 +4,9 @@ go 1.18
require (
github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3
github.com/ethereum-optimism/optimism/op-bindings v0.10.0
github.com/ethereum-optimism/optimism/op-node v0.10.0
github.com/ethereum-optimism/optimism/op-service v0.10.0
github.com/ethereum-optimism/optimism/op-bindings v0.10.1
github.com/ethereum-optimism/optimism/op-node v0.10.1
github.com/ethereum-optimism/optimism/op-service v0.10.1
github.com/ethereum/go-ethereum v1.10.26
github.com/stretchr/testify v1.8.0
github.com/urfave/cli v1.22.9
......
......@@ -107,12 +107,12 @@ github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3 h1:RWHKLhCrQThMfch+QJ1Z
github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3/go.mod h1:QziizLAiF0KqyLdNJYD7O5cpDlaFMNZzlxYNcWsJUxs=
github.com/ethereum-optimism/op-geth v0.0.0-20221104231810-30db39cae2be h1:8TdM3M7FjZkrYeGGX9nEVtDDlZ5RiuHtc0mbi5bGKyY=
github.com/ethereum-optimism/op-geth v0.0.0-20221104231810-30db39cae2be/go.mod h1:1g9UmZgEINqvYfXmWOUCRJX9fxegeOHudVkLCRAXO5Y=
github.com/ethereum-optimism/optimism/op-bindings v0.10.0 h1:M2nwcOXH9YiRDH6UXnzHI+/eAM5UoFWp7HTlAWrZ4Os=
github.com/ethereum-optimism/optimism/op-bindings v0.10.0/go.mod h1:UeTZlpZyhOL3y9Sogzvbn8Z3q1tDmZEv1VmGxMiZYCg=
github.com/ethereum-optimism/optimism/op-node v0.10.0 h1:I6lzMP596P8PfNdPd/z5aO6uWJJABkPKC8jMV3qg9Pc=
github.com/ethereum-optimism/optimism/op-node v0.10.0/go.mod h1:ihuS69UXdeteQENbAZpmEfw4AiVbNrf2ylWRgu1CpEk=
github.com/ethereum-optimism/optimism/op-service v0.10.0 h1:hkWVsVVFf0ybLWwpSqnPasndiFJLk+u++YStaQRRMFY=
github.com/ethereum-optimism/optimism/op-service v0.10.0/go.mod h1:d7IryyBivUjxhQt91nNBxUainSFmRjwf6H/nCNBowvk=
github.com/ethereum-optimism/optimism/op-bindings v0.10.1 h1:OxzYjPmjl5DblgvS0z27M8lZInjdm9Wt6ajozXuMhmw=
github.com/ethereum-optimism/optimism/op-bindings v0.10.1/go.mod h1:UeTZlpZyhOL3y9Sogzvbn8Z3q1tDmZEv1VmGxMiZYCg=
github.com/ethereum-optimism/optimism/op-node v0.10.1 h1:kVBaOEOYLV22XEHRhB7dfdmoXepO0kx/RsZQK+Bpk1Y=
github.com/ethereum-optimism/optimism/op-node v0.10.1/go.mod h1:pup7wiiUs9g8cZKwXeB5tEGCqwUUwFVmej9MmSIm6S8=
github.com/ethereum-optimism/optimism/op-service v0.10.1 h1:s8CisVat3ia04Z0mW3IiwZ7V1EInyVe3ODq6UXSyJG4=
github.com/ethereum-optimism/optimism/op-service v0.10.1/go.mod h1:d7IryyBivUjxhQt91nNBxUainSFmRjwf6H/nCNBowvk=
github.com/fjl/memsize v0.0.1 h1:+zhkb+dhUgx0/e+M8sF0QqiouvMQUiKR+QYvdxIOKcQ=
github.com/fjl/memsize v0.0.1/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
......
......@@ -5,7 +5,7 @@ GasBenchMark_L1StandardBridge_Deposit:test_depositERC20_benchmark_1() (gas: 1122
GasBenchMark_L1StandardBridge_Deposit:test_depositETH_benchmark_0() (gas: 348176)
GasBenchMark_L1StandardBridge_Deposit:test_depositETH_benchmark_1() (gas: 112253)
GasBenchMark_L1StandardBridge_Finalize:test_finalizeETHWithdrawal_benchmark() (gas: 40502)
GasBenchMark_L2OutputOracle:test_proposeL2Output_benchmark() (gas: 68648)
GasBenchMark_L2OutputOracle:test_proposeL2Output_benchmark() (gas: 68620)
GasBenchMark_OptimismPortal:test_depositTransaction_benchmark() (gas: 74956)
GasBenchMark_OptimismPortal:test_depositTransaction_benchmark_1() (gas: 35693)
CrossDomainMessenger_Test:testFuzz_baseGas(uint32) (runs: 256, μ: 20196, ~: 20196)
......@@ -85,29 +85,30 @@ L2CrossDomainMessenger_Test:test_L2MessengerSendMessage() (gas: 122423)
L2CrossDomainMessenger_Test:test_L2MessengerTwiceSendMessage() (gas: 134632)
L2CrossDomainMessenger_Test:test_L2MessengerXDomainSenderReverts() (gas: 10568)
L2CrossDomainMessenger_Test:test_L2MessengerxDomainMessageSenderResets() (gas: 52615)
L2OutputOracleTest:testCannot_ProposeWithUnmatchedBlockhash() (gas: 26850)
L2OutputOracleTest:testCannot_constructWithBadTimestamp() (gas: 50654)
L2OutputOracleTest:testCannot_deleteL2Output_ifNotOwner() (gas: 25090)
L2OutputOracleTest:testCannot_deleteL2Output_withWrongRoot() (gas: 91399)
L2OutputOracleTest:testCannot_deleteL2Output_withWrongTime() (gas: 87382)
L2OutputOracleTest:testCannot_proposeEmptyOutput() (gas: 24126)
L2OutputOracleTest:testCannot_proposeFutureTimetamp() (gas: 26095)
L2OutputOracleTest:testCannot_proposeL2OutputIfNotProposer() (gas: 23563)
L2OutputOracleTest:testCannot_proposeOnWrongFork() (gas: 26368)
L2OutputOracleTest:testCannot_proposeUnexpectedBlockNumber() (gas: 26003)
L2OutputOracleTest:test_changeProposer() (gas: 55872)
L2OutputOracleTest:test_computeL2Timestamp() (gas: 30215)
L2OutputOracleTest:test_constructor() (gas: 45612)
L2OutputOracleTest:test_deleteOutput() (gas: 77197)
L2OutputOracleTest:test_getL2Output() (gas: 88478)
L2OutputOracleTest:test_latestBlockNumber() (gas: 76240)
L2OutputOracleTest:test_nextBlockNumber() (gas: 15187)
L2OutputOracleTest:test_proposeWithBlockhashAndHeight() (gas: 75044)
L2OutputOracleTest:test_proposingAnotherOutput() (gas: 76869)
L2OutputOracleTest:test_updateOwner() (gas: 46134)
L2OutputOracleUpgradeable_Test:test_cannotInitImpl() (gas: 17403)
L2OutputOracleUpgradeable_Test:test_cannotInitProxy() (gas: 22398)
L2OutputOracleUpgradeable_Test:test_initValuesOnProxy() (gas: 36094)
L2OutputOracleTest:testCannot_ProposeWithUnmatchedBlockhash() (gas: 26776)
L2OutputOracleTest:testCannot_constructWithBadTimestamp() (gas: 50351)
L2OutputOracleTest:testCannot_deleteL2Outputs_afterLatest() (gas: 194584)
L2OutputOracleTest:testCannot_deleteL2Outputs_ifNotOwner() (gas: 18871)
L2OutputOracleTest:testCannot_deleteL2Outputs_nonExistent() (gas: 86711)
L2OutputOracleTest:testCannot_proposeEmptyOutput() (gas: 24085)
L2OutputOracleTest:testCannot_proposeFutureTimetamp() (gas: 26043)
L2OutputOracleTest:testCannot_proposeL2OutputIfNotProposer() (gas: 23515)
L2OutputOracleTest:testCannot_proposeOnWrongFork() (gas: 26371)
L2OutputOracleTest:testCannot_proposeUnexpectedBlockNumber() (gas: 25955)
L2OutputOracleTest:test_changeProposer() (gas: 47212)
L2OutputOracleTest:test_computeL2Timestamp() (gas: 30430)
L2OutputOracleTest:test_constructor() (gas: 45691)
L2OutputOracleTest:test_deleteOutputs_multipleOutputs() (gas: 203841)
L2OutputOracleTest:test_deleteOutputs_singleOutput() (gas: 94524)
L2OutputOracleTest:test_getL2Output() (gas: 84525)
L2OutputOracleTest:test_latestBlockNumber() (gas: 76229)
L2OutputOracleTest:test_nextBlockNumber() (gas: 15120)
L2OutputOracleTest:test_proposeWithBlockhashAndHeight() (gas: 75035)
L2OutputOracleTest:test_proposingAnotherOutput() (gas: 76837)
L2OutputOracleTest:test_updateOwner() (gas: 36063)
L2OutputOracleUpgradeable_Test:test_cannotInitImpl() (gas: 17381)
L2OutputOracleUpgradeable_Test:test_cannotInitProxy() (gas: 22376)
L2OutputOracleUpgradeable_Test:test_initValuesOnProxy() (gas: 38217)
L2OutputOracleUpgradeable_Test:test_upgrading() (gas: 180457)
L2StandardBridge_Test:test_cannotWithdrawEthWithoutSendingIt() (gas: 21749)
L2StandardBridge_Test:test_finalizeBridgeETH_incorrectValueReverts() (gas: 23733)
......@@ -177,7 +178,7 @@ OptimismPortal_Test:test_depositTransaction_withEthValueAndContractContractCreat
OptimismPortal_Test:test_depositTransaction_withEthValueAndEOAContractCreation_success() (gas: 75852)
OptimismPortal_Test:test_depositTransaction_withEthValueFromContract_success() (gas: 83370)
OptimismPortal_Test:test_depositTransaction_withEthValueFromEOA_success() (gas: 83964)
OptimismPortal_Test:test_isBlockFinalized_success() (gas: 113537)
OptimismPortal_Test:test_isBlockFinalized_success() (gas: 104802)
OptimismPortal_Test:test_simple_isBlockFinalized_success() (gas: 24142)
Proxy_Test:test_clashingFunctionSignatures() (gas: 101347)
Proxy_Test:test_implementationKey() (gas: 20887)
......
......@@ -67,17 +67,11 @@ contract L2OutputOracle is OwnableUpgradeable, Semver {
);
/**
* @notice Emitted when an output is deleted.
* @notice Emitted when outputs are deleted.
*
* @param outputRoot The output root.
* @param l1Timestamp The L1 timestamp when proposed.
* @param l2BlockNumber The L2 block number of the output root.
* @param l2BlockNumber First L2 block number deleted.
*/
event OutputDeleted(
bytes32 indexed outputRoot,
uint256 indexed l1Timestamp,
uint256 indexed l2BlockNumber
);
event OutputsDeleted(uint256 indexed l2BlockNumber);
/**
* @notice Emitted when the proposer address is changed.
......@@ -140,7 +134,6 @@ contract L2OutputOracle is OwnableUpgradeable, Semver {
address _proposer,
address _owner
) public initializer {
require(_proposer != _owner, "L2OutputOracle: proposer cannot be the same as the owner");
l2Outputs[STARTING_BLOCK_NUMBER] = Types.OutputProposal(_genesisL2Output, block.timestamp);
latestBlockNumber = STARTING_BLOCK_NUMBER;
__Ownable_init();
......@@ -149,32 +142,35 @@ contract L2OutputOracle is OwnableUpgradeable, Semver {
}
/**
* @notice Deletes the most recent output. This is used to remove the most recent output in the
* event that an erreneous output is submitted. It can only be called by the contract's
* owner, not the proposer. Longer term, this should be replaced with a more robust
* mechanism which will allow deletion of proposals shown to be invalid by a fault
* proof.
* @notice Deletes all output proposals after and including the proposal that corresponds to
* the given block number. Can only be called by the owner, but will be replaced with
* a mechanism that allows a challenger contract to delete proposals.
*
* @param _proposal Represents the output proposal to delete
* @param _l2BlockNumber L2 block number of the first output root to delete.
*/
// solhint-disable-next-line ordering
function deleteL2Output(Types.OutputProposal memory _proposal) external onlyOwner {
Types.OutputProposal memory outputToDelete = l2Outputs[latestBlockNumber];
function deleteL2Outputs(uint256 _l2BlockNumber) external onlyOwner {
// Simple check that accomplishes two things:
// 1. Prevents deleting anything from before the genesis block.
// 2. Prevents deleting anything other than a checkpoint block.
require(
_proposal.outputRoot == outputToDelete.outputRoot,
"L2OutputOracle: output root to delete does not match the latest output proposal"
l2Outputs[_l2BlockNumber].outputRoot != bytes32(0),
"L2OutputOracle: cannot delete a non-existent output"
);
// Prevent deleting beyond latest block number. Above check will miss this case if we
// already deleted an output and then the user tries to delete a later output.
require(
_proposal.timestamp == outputToDelete.timestamp,
"L2OutputOracle: timestamp to delete does not match the latest output proposal"
_l2BlockNumber <= latestBlockNumber,
"L2OutputOracle: cannot delete outputs after the latest block number"
);
emit OutputDeleted(outputToDelete.outputRoot, outputToDelete.timestamp, latestBlockNumber);
// We're setting the latest block number back to the checkpoint block before the given L2
// block number. Next proposal will overwrite the deleted output and following proposals
// will delete any outputs after that.
latestBlockNumber = _l2BlockNumber - SUBMISSION_INTERVAL;
delete l2Outputs[latestBlockNumber];
latestBlockNumber = latestBlockNumber - SUBMISSION_INTERVAL;
emit OutputsDeleted(_l2BlockNumber);
}
/**
......@@ -223,10 +219,10 @@ contract L2OutputOracle is OwnableUpgradeable, Semver {
);
}
emit OutputProposed(_outputRoot, block.timestamp, _l2BlockNumber);
l2Outputs[_l2BlockNumber] = Types.OutputProposal(_outputRoot, block.timestamp);
latestBlockNumber = _l2BlockNumber;
emit OutputProposed(_outputRoot, block.timestamp, _l2BlockNumber);
}
/**
......@@ -244,8 +240,8 @@ contract L2OutputOracle is OwnableUpgradeable, Semver {
returns (Types.OutputProposal memory)
{
require(
_l2BlockNumber >= STARTING_BLOCK_NUMBER,
"L2OutputOracle: block number cannot be less than the starting block number."
_l2BlockNumber <= latestBlockNumber,
"L2OutputOracle: block number cannot be greater than the latest block number"
);
// Find the distance between _l2BlockNumber, and the checkpoint block before it.
......@@ -260,38 +256,23 @@ contract L2OutputOracle is OwnableUpgradeable, Semver {
Types.OutputProposal memory output = l2Outputs[lookupBlockNumber];
require(
output.outputRoot != bytes32(0),
"L2OutputOracle: No output found for that block number."
"L2OutputOracle: no output found for the given block number"
);
return output;
}
/**
* @notice Overrides the standard implementation of transferOwnership
* to add the requirement that the owner and proposer are distinct.
* Can only be called by the current owner.
*/
function transferOwnership(address _newOwner) public override onlyOwner {
require(_newOwner != proposer, "L2OutputOracle: owner cannot be the same as the proposer");
super.transferOwnership(_newOwner);
return output;
}
/**
* @notice Transfers the proposer role to a new account (`newProposer`).
* Can only be called by the current owner.
* @notice Allows the owner to change the proposer address.
*
* @param _proposer New proposer address.
*/
function changeProposer(address _newProposer) public onlyOwner {
require(
_newProposer != address(0),
"L2OutputOracle: new proposer cannot be the zero address"
);
function changeProposer(address _proposer) public onlyOwner {
require(_proposer != address(0), "L2OutputOracle: new proposer cannot be the zero address");
require(
_newProposer != owner(),
"L2OutputOracle: proposer cannot be the same as the owner"
);
emit ProposerChanged(proposer, _proposer);
emit ProposerChanged(proposer, _newProposer);
proposer = _newProposer;
proposer = _proposer;
}
/**
......@@ -309,11 +290,6 @@ contract L2OutputOracle is OwnableUpgradeable, Semver {
* @param _l2BlockNumber The L2 block number of the target block.
*/
function computeL2Timestamp(uint256 _l2BlockNumber) public view returns (uint256) {
require(
_l2BlockNumber >= STARTING_BLOCK_NUMBER,
"L2OutputOracle: block number must be greater than or equal to starting block number"
);
return STARTING_TIMESTAMP + ((_l2BlockNumber - STARTING_BLOCK_NUMBER) * L2_BLOCK_TIME);
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import { L2OutputOracle } from "../L1/L2OutputOracle.sol";
import { OptimismPortal } from "../L1/OptimismPortal.sol";
import { SystemConfig } from "../L1/SystemConfig.sol";
import { L1CrossDomainMessenger } from "../L1/L1CrossDomainMessenger.sol";
import { BaseSystemDictator } from "./BaseSystemDictator.sol";
/**
* @title FreshSystemDictator
* @notice The FreshSystemDictator is responsible for coordinating initialization of a fresh
* deployment of the Optimism system. We expect that all proxies and implementations
* already be deployed before this contract is used.
*/
contract FreshSystemDictator is BaseSystemDictator {
/**
* @param _config System configuration.
*/
constructor(DeployConfig memory _config) BaseSystemDictator(_config) {}
/**
* @notice Upgrades and initializes proxy contracts.
*/
function step1() external onlyOwner step(1) {
// Upgrade and initialize the L2OutputOracle.
config.globalConfig.proxyAdmin.upgradeAndCall(
payable(config.proxyAddressConfig.l2OutputOracleProxy),
address(config.implementationAddressConfig.l2OutputOracleImpl),
abi.encodeCall(
L2OutputOracle.initialize,
(
config.l2OutputOracleConfig.l2OutputOracleGenesisL2Output,
config.l2OutputOracleConfig.l2OutputOracleProposer,
config.l2OutputOracleConfig.l2OutputOracleOwner
)
)
);
// Upgrade and initialize the OptimismPortal.
config.globalConfig.proxyAdmin.upgradeAndCall(
payable(config.proxyAddressConfig.optimismPortalProxy),
address(config.implementationAddressConfig.optimismPortalImpl),
abi.encodeCall(OptimismPortal.initialize, ())
);
// Upgrade and initialize the L1CrossDomainMessenger.
config.globalConfig.proxyAdmin.upgradeAndCall(
payable(config.proxyAddressConfig.l1CrossDomainMessengerProxy),
address(config.implementationAddressConfig.l1CrossDomainMessengerImpl),
abi.encodeCall(L1CrossDomainMessenger.initialize, (config.globalConfig.finalOwner))
);
// Upgrade the L1StandardBridge (no initializer).
config.globalConfig.proxyAdmin.upgrade(
payable(config.proxyAddressConfig.l1StandardBridgeProxy),
address(config.implementationAddressConfig.l1StandardBridgeImpl)
);
// Upgrade the OptimismMintableERC20Factory (no initializer).
config.globalConfig.proxyAdmin.upgrade(
payable(config.proxyAddressConfig.optimismMintableERC20FactoryProxy),
address(config.implementationAddressConfig.optimismMintableERC20FactoryImpl)
);
// Upgrade the L1ERC721Bridge (no initializer).
config.globalConfig.proxyAdmin.upgrade(
payable(config.proxyAddressConfig.l1ERC721BridgeProxy),
address(config.implementationAddressConfig.l1ERC721BridgeImpl)
);
// Upgrade and initialize the SystemConfig.
config.globalConfig.proxyAdmin.upgradeAndCall(
payable(config.proxyAddressConfig.systemConfigProxy),
address(config.implementationAddressConfig.systemConfigImpl),
abi.encodeCall(
SystemConfig.initialize,
(
config.systemConfigConfig.owner,
config.systemConfigConfig.overhead,
config.systemConfigConfig.scalar,
config.systemConfigConfig.batcherHash,
config.systemConfigConfig.gasLimit
)
)
);
}
/**
* @notice Transfers ownership to final owner.
*/
function step2() external onlyOwner step(2) {
// Transfer ownership of the ProxyAdmin to the final owner.
config.globalConfig.proxyAdmin.transferOwnership(config.globalConfig.finalOwner);
}
}
......@@ -17,16 +17,26 @@ import { BaseSystemDictator } from "./BaseSystemDictator.sol";
* proxies and implementations already be deployed before this contract is used.
*/
contract MigrationSystemDictator is BaseSystemDictator {
/**
* @notice Step after which exit 1 can no longer be used.
*/
uint8 constant EXIT_1_NO_RETURN_STEP = 3;
/**
* @notice Step where proxy ownership is transferred.
*/
uint8 constant PROXY_TRANSFER_STEP = 3;
uint8 constant PROXY_TRANSFER_STEP = 4;
/**
* @notice Whether or not the deployment is finalized.
*/
bool public finalized;
/**
* @notice Address of the old L1CrossDomainMessenger implementation.
*/
address public oldL1CrossDomainMessenger;
/**
* @param _config System configuration.
*/
......@@ -63,18 +73,31 @@ contract MigrationSystemDictator is BaseSystemDictator {
* deposit halt flag to tell the Sequencer's DTL to stop accepting deposits.
*/
function step2() external onlyOwner step(2) {
// Store the address of the old L1CrossDomainMessenger implementation. We will need this
// address in the case that we have to exit early.
oldL1CrossDomainMessenger = config.globalConfig.addressManager.getAddress(
"OVM_L1CrossDomainMessenger"
);
// Temporarily brick the L1CrossDomainMessenger by setting its implementation address to
// address(0) which will cause the ResolvedDelegateProxy to revert. Better than pausing
// the L1CrossDomainMessenger via pause() because it can be easily reverted.
config.globalConfig.addressManager.setAddress("OVM_L1CrossDomainMessenger", address(0));
// TODO: Set the deposit halt flag.
// Set the DTL shutoff block, which will tell the DTL to stop syncing new deposits from the
// CanonicalTransactionChain. We do this by setting an address in the AddressManager
// because the DTL already has a reference to the AddressManager and this way we don't also
// need to give it a reference to the SystemDictator.
config.globalConfig.addressManager.setAddress(
"DTL_SHUTOFF_BLOCK",
address(uint160(block.number))
);
}
/**
* @notice Removes deprecated addresses from the AddressManager.
*/
function step3() external onlyOwner step(3) {
function step3() external onlyOwner step(EXIT_1_NO_RETURN_STEP) {
// Remove all deprecated addresses from the AddressManager
string[17] memory deprecated = [
"OVM_CanonicalTransactionChain",
......@@ -104,7 +127,7 @@ contract MigrationSystemDictator is BaseSystemDictator {
/**
* @notice Transfers system ownership to the ProxyAdmin.
*/
function step4() external onlyOwner step(4) {
function step4() external onlyOwner step(PROXY_TRANSFER_STEP) {
// Transfer ownership of the AddressManager to the ProxyAdmin.
config.globalConfig.addressManager.transferOwnership(
address(config.globalConfig.proxyAdmin)
......@@ -120,10 +143,6 @@ contract MigrationSystemDictator is BaseSystemDictator {
* @notice Upgrades and initializes proxy contracts.
*/
function step5() external onlyOwner step(5) {
// Pause the L1CrossDomainMessenger. Now we use the real pause() function because by the
// time we're done here we'll have access to the unpause() function.
L1CrossDomainMessenger(config.proxyAddressConfig.l1CrossDomainMessengerProxy).pause();
// Upgrade and initialize the L2OutputOracle.
config.globalConfig.proxyAdmin.upgradeAndCall(
payable(config.proxyAddressConfig.l2OutputOracleProxy),
......@@ -145,13 +164,34 @@ contract MigrationSystemDictator is BaseSystemDictator {
abi.encodeCall(OptimismPortal.initialize, ())
);
// Upgrade the L1CrossDomainMessenger. No initializer because this is
// already initialized.
// Upgrade the L1CrossDomainMessenger.
config.globalConfig.proxyAdmin.upgrade(
payable(config.proxyAddressConfig.l1CrossDomainMessengerProxy),
address(config.implementationAddressConfig.l1CrossDomainMessengerImpl)
);
// Try to initialize the L1CrossDomainMessenger, only fail if it's already been initialized.
try
L1CrossDomainMessenger(config.proxyAddressConfig.l1CrossDomainMessengerProxy)
.initialize(address(this))
{
// L1CrossDomainMessenger is the one annoying edge case difference between existing
// networks and fresh networks because in existing networks it'll already be
// initialized but in fresh networks it won't be. Try/catch is the easiest and most
// consistent way to handle this because initialized() is not exposed publicly.
} catch Error(string memory reason) {
require(
keccak256(abi.encodePacked(reason)) ==
keccak256("Initializable: contract is already initialized"),
string.concat(
"MigrationSystemDictator: unexpected error initializing L1XDM: ",
reason
)
);
} catch {
revert("MigrationSystemDictator: unexpected error initializing L1XDM (no reason)");
}
// Transfer ETH from the L1StandardBridge to the OptimismPortal.
config.globalConfig.proxyAdmin.upgradeAndCall(
payable(config.proxyAddressConfig.l1StandardBridgeProxy),
......@@ -192,6 +232,9 @@ contract MigrationSystemDictator is BaseSystemDictator {
)
)
);
// Pause the L1CrossDomainMessenger, chance to check that everything is OK.
L1CrossDomainMessenger(config.proxyAddressConfig.l1CrossDomainMessengerProxy).pause();
}
/**
......@@ -229,4 +272,23 @@ contract MigrationSystemDictator is BaseSystemDictator {
finalized = true;
}
/**
* @notice First exit point, can only be called before step 3 is executed.
*/
function exit1() external onlyOwner {
require(
currentStep == EXIT_1_NO_RETURN_STEP,
"MigrationSystemDictator: can only exit1 before step 3 is executed"
);
// Reset the L1CrossDomainMessenger to the old implementation.
config.globalConfig.addressManager.setAddress(
"OVM_L1CrossDomainMessenger",
oldL1CrossDomainMessenger
);
// Unset the DTL shutoff block which will allow the DTL to sync again.
config.globalConfig.addressManager.setAddress("DTL_SHUTOFF_BLOCK", address(0));
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import { stdError } from "forge-std/Test.sol";
import { L2OutputOracle_Initializer, NextImpl } from "./CommonTest.t.sol";
import { L2OutputOracle } from "../L1/L2OutputOracle.sol";
import { Proxy } from "../universal/Proxy.sol";
......@@ -74,13 +75,13 @@ contract L2OutputOracleTest is L2OutputOracle_Initializer {
assertEq(proposal.timestamp, block.timestamp);
// The block number is too low:
vm.expectRevert(
"L2OutputOracle: block number cannot be less than the starting block number."
);
vm.expectRevert(stdError.arithmeticError);
oracle.getL2Output(0);
// The block number is larger than the latest proposed output:
vm.expectRevert("L2OutputOracle: No output found for that block number.");
vm.expectRevert(
"L2OutputOracle: block number cannot be greater than the latest block number"
);
oracle.getL2Output(nextBlockNumber + 1);
}
......@@ -95,9 +96,7 @@ contract L2OutputOracleTest is L2OutputOracle_Initializer {
function test_computeL2Timestamp() external {
// reverts if timestamp is too low
vm.expectRevert(
"L2OutputOracle: block number must be greater than or equal to starting block number"
);
vm.expectRevert(stdError.arithmeticError);
oracle.computeL2Timestamp(startingBlockNumber - 1);
// returns the correct value...
......@@ -132,9 +131,6 @@ contract L2OutputOracleTest is L2OutputOracle_Initializer {
vm.expectRevert("L2OutputOracle: new proposer cannot be the zero address");
oracle.changeProposer(address(0));
vm.expectRevert("L2OutputOracle: proposer cannot be the same as the owner");
oracle.changeProposer(owner);
// Double check proposer has not changed.
assertEq(proposer, oracle.proposer());
......@@ -154,11 +150,6 @@ contract L2OutputOracleTest is L2OutputOracle_Initializer {
assertEq(owner, oracle.owner());
vm.startPrank(owner);
vm.expectRevert("L2OutputOracle: owner cannot be the same as the proposer");
oracle.transferOwnership(proposer);
// Double check owner has not changed.
assertEq(owner, oracle.owner());
vm.expectEmit(true, true, true, true);
emit OwnershipTransferred(owner, newOwner);
oracle.transferOwnership(newOwner);
......@@ -280,36 +271,52 @@ contract L2OutputOracleTest is L2OutputOracle_Initializer {
* Delete Tests - Happy Path *
*****************************/
event OutputDeleted(
bytes32 indexed l2Output,
uint256 indexed l1Timestamp,
uint256 indexed l2BlockNumber
);
event OutputsDeleted(uint256 indexed l2BlockNumber);
function test_deleteOutput() external {
function test_deleteOutputs_singleOutput() external {
test_proposingAnotherOutput();
uint256 latestBlockNumber = oracle.latestBlockNumber();
Types.OutputProposal memory proposalToDelete = oracle.getL2Output(latestBlockNumber);
Types.OutputProposal memory newLatestOutput = oracle.getL2Output(
latestBlockNumber - submissionInterval
);
vm.prank(owner);
vm.expectEmit(true, true, false, false);
emit OutputDeleted(
proposalToDelete.outputRoot,
proposalToDelete.timestamp,
latestBlockNumber
);
oracle.deleteL2Output(proposalToDelete);
emit OutputsDeleted(latestBlockNumber);
oracle.deleteL2Outputs(latestBlockNumber);
// validate latestBlockNumber has been reduced
uint256 latestBlockNumberAfter = oracle.latestBlockNumber();
assertEq(latestBlockNumber - submissionInterval, latestBlockNumberAfter);
// validate that the new latest output is as expected.
Types.OutputProposal memory proposal = oracle.getL2Output(latestBlockNumberAfter);
assertEq(newLatestOutput.outputRoot, proposal.outputRoot);
assertEq(newLatestOutput.timestamp, proposal.timestamp);
}
function test_deleteOutputs_multipleOutputs() external {
test_proposingAnotherOutput();
test_proposingAnotherOutput();
test_proposingAnotherOutput();
uint256 latestBlockNumber = oracle.latestBlockNumber();
Types.OutputProposal memory newLatestOutput = oracle.getL2Output(
latestBlockNumber - submissionInterval * 3
);
vm.prank(owner);
vm.expectEmit(true, true, false, false);
emit OutputsDeleted(latestBlockNumber - submissionInterval * 2);
oracle.deleteL2Outputs(latestBlockNumber - submissionInterval * 2);
// validate latestBlockNumber has been reduced
uint256 latestBlockNumberAfter = oracle.latestBlockNumber();
assertEq(latestBlockNumber - submissionInterval * 3, latestBlockNumberAfter);
// validate that the new latest output is as expected.
Types.OutputProposal memory proposal = oracle.getL2Output(latestBlockNumberAfter);
assertEq(newLatestOutput.outputRoot, proposal.outputRoot);
assertEq(newLatestOutput.timestamp, proposal.timestamp);
}
......@@ -318,40 +325,38 @@ contract L2OutputOracleTest is L2OutputOracle_Initializer {
* Delete Tests - Sad Path *
***************************/
function testCannot_deleteL2Output_ifNotOwner() external {
function testCannot_deleteL2Outputs_ifNotOwner() external {
uint256 latestBlockNumber = oracle.latestBlockNumber();
Types.OutputProposal memory proposal = oracle.getL2Output(latestBlockNumber);
vm.expectRevert("Ownable: caller is not the owner");
oracle.deleteL2Output(proposal);
oracle.deleteL2Outputs(latestBlockNumber);
}
function testCannot_deleteL2Output_withWrongRoot() external {
function testCannot_deleteL2Outputs_nonExistent() external {
test_proposingAnotherOutput();
uint256 previousBlockNumber = oracle.latestBlockNumber() - submissionInterval;
Types.OutputProposal memory proposalToDelete = oracle.getL2Output(previousBlockNumber);
uint256 latestBlockNumber = oracle.latestBlockNumber();
vm.prank(owner);
vm.expectRevert(
"L2OutputOracle: output root to delete does not match the latest output proposal"
);
oracle.deleteL2Output(proposalToDelete);
vm.expectRevert("L2OutputOracle: cannot delete a non-existent output");
oracle.deleteL2Outputs(latestBlockNumber + 1);
}
function testCannot_deleteL2Output_withWrongTime() external {
function testCannot_deleteL2Outputs_afterLatest() external {
// Start by proposing three outputs
test_proposingAnotherOutput();
test_proposingAnotherOutput();
test_proposingAnotherOutput();
// Delete the latest two outputs
uint256 latestBlockNumber = oracle.latestBlockNumber();
Types.OutputProposal memory proposalToDelete = oracle.getL2Output(latestBlockNumber);
vm.prank(owner);
oracle.deleteL2Outputs(latestBlockNumber - submissionInterval * 2);
// Modify the timestamp so that it does not match.
proposalToDelete.timestamp -= 1;
// Now try to delete the same output again
vm.prank(owner);
vm.expectRevert(
"L2OutputOracle: timestamp to delete does not match the latest output proposal"
);
oracle.deleteL2Output(proposalToDelete);
vm.expectRevert("L2OutputOracle: cannot delete outputs after the latest block number");
oracle.deleteL2Outputs(latestBlockNumber - submissionInterval * 2);
}
}
......
......@@ -242,7 +242,9 @@ contract OptimismPortal_Test is Portal_Initializer {
// The checkpointed block should not be finalized until 1 second from now.
assertEq(op.isBlockFinalized(checkpoint), false);
// Nor should a block after it
vm.expectRevert("L2OutputOracle: No output found for that block number.");
vm.expectRevert(
"L2OutputOracle: block number cannot be greater than the latest block number"
);
assertEq(op.isBlockFinalized(checkpoint + 1), false);
// Nor a block before it, even though the finalization period has passed, there is
// not yet a checkpoint block on top of it for which that is true.
......@@ -255,7 +257,9 @@ contract OptimismPortal_Test is Portal_Initializer {
// So should the block before it.
assertEq(op.isBlockFinalized(checkpoint - 1), true);
// But not the block after it.
vm.expectRevert("L2OutputOracle: No output found for that block number.");
vm.expectRevert(
"L2OutputOracle: block number cannot be greater than the latest block number"
);
assertEq(op.isBlockFinalized(checkpoint + 1), false);
}
}
......
......@@ -19,6 +19,6 @@ const deployFn: DeployFunction = async (hre) => {
})
}
deployFn.tags = ['ProxyAdmin', 'fresh', 'migration']
deployFn.tags = ['ProxyAdmin']
export default deployFn
......@@ -3,22 +3,22 @@ import { DeployFunction } from 'hardhat-deploy/dist/types'
import {
assertContractVariable,
deployAndVerifyAndThen,
getDeploymentAddress,
} from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const proxyAdmin = await getDeploymentAddress(hre, 'ProxyAdmin')
const { deployer } = await hre.getNamedAccounts()
await deployAndVerifyAndThen({
hre,
name: 'L1StandardBridgeProxy',
contract: 'Proxy',
args: [proxyAdmin],
name: 'Lib_AddressManager',
contract: 'AddressManager',
args: [],
postDeployAction: async (contract) => {
await assertContractVariable(contract, 'admin', proxyAdmin)
// Owner is temporarily set to the deployer.
await assertContractVariable(contract, 'owner', deployer)
},
})
}
deployFn.tags = ['L1StandardBridgeProxy', 'fresh']
deployFn.tags = ['AddressManager']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import {
assertContractVariable,
deployAndVerifyAndThen,
} from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
await deployAndVerifyAndThen({
hre,
name: 'Proxy__OVM_L1StandardBridge',
contract: 'L1ChugSplashProxy',
args: [deployer],
postDeployAction: async (contract) => {
await assertContractVariable(contract, 'getOwner', deployer)
},
})
}
deployFn.tags = ['L1StandardBridgeProxy']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import {
assertContractVariable,
deployAndVerifyAndThen,
getDeploymentAddress,
} from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const proxyAdmin = await getDeploymentAddress(hre, 'ProxyAdmin')
await deployAndVerifyAndThen({
hre,
name: 'L1CrossDomainMessengerProxy',
contract: 'Proxy',
args: [proxyAdmin],
postDeployAction: async (contract) => {
await assertContractVariable(contract, 'admin', proxyAdmin)
},
})
}
deployFn.tags = ['L1CrossDomainMessengerProxy', 'fresh']
export default deployFn
......@@ -19,6 +19,6 @@ const deployFn: DeployFunction = async (hre) => {
})
}
deployFn.tags = ['L2OutputOracleProxy', 'fresh', 'migration']
deployFn.tags = ['L2OutputOracleProxy']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import {
deployAndVerifyAndThen,
getDeploymentAddress,
} from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const addressManager = await getDeploymentAddress(hre, 'Lib_AddressManager')
await deployAndVerifyAndThen({
hre,
name: 'Proxy__OVM_L1CrossDomainMessenger',
contract: 'ResolvedDelegateProxy',
args: [addressManager, 'OVM_L1CrossDomainMessenger'],
})
}
deployFn.tags = ['L1CrossDomainMessengerProxy']
export default deployFn
......@@ -19,6 +19,6 @@ const deployFn: DeployFunction = async (hre) => {
})
}
deployFn.tags = ['OptimismPortalProxy', 'fresh', 'migration']
deployFn.tags = ['OptimismPortalProxy']
export default deployFn
......@@ -19,6 +19,6 @@ const deployFn: DeployFunction = async (hre) => {
})
}
deployFn.tags = ['OptimismMintableERC20FactoryProxy', 'fresh', 'migration']
deployFn.tags = ['OptimismMintableERC20FactoryProxy']
export default deployFn
......@@ -19,6 +19,6 @@ const deployFn: DeployFunction = async (hre) => {
})
}
deployFn.tags = ['L1ERC721BridgeProxy', 'fresh', 'migration']
deployFn.tags = ['L1ERC721BridgeProxy']
export default deployFn
......@@ -19,6 +19,6 @@ const deployFn: DeployFunction = async (hre) => {
})
}
deployFn.tags = ['SystemConfigProxy', 'fresh', 'migration']
deployFn.tags = ['SystemConfigProxy']
export default deployFn
......@@ -12,6 +12,7 @@ const deployFn: DeployFunction = async (hre) => {
hre,
'OptimismPortalProxy'
)
await deployAndVerifyAndThen({
hre,
name: 'L1CrossDomainMessenger',
......@@ -31,6 +32,6 @@ const deployFn: DeployFunction = async (hre) => {
})
}
deployFn.tags = ['L1CrossDomainMessengerImpl', 'fresh', 'migration']
deployFn.tags = ['L1CrossDomainMessengerImpl']
export default deployFn
import { ethers } from 'ethers'
import { DeployFunction } from 'hardhat-deploy/dist/types'
import { predeploys } from '../src'
......@@ -9,18 +8,10 @@ import {
} from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
let L1CrossDomainMessengerProxy: ethers.Contract
try {
L1CrossDomainMessengerProxy = await getContractFromArtifact(
hre,
'Proxy__OVM_L1CrossDomainMessenger'
)
} catch {
L1CrossDomainMessengerProxy = await getContractFromArtifact(
hre,
'L1CrossDomainMessengerProxy'
)
}
const L1CrossDomainMessengerProxy = await getContractFromArtifact(
hre,
'Proxy__OVM_L1CrossDomainMessenger'
)
await deployAndVerifyAndThen({
hre,
......@@ -41,6 +32,6 @@ const deployFn: DeployFunction = async (hre) => {
})
}
deployFn.tags = ['L1StandardBridgeImpl', 'fresh', 'migration']
deployFn.tags = ['L1StandardBridgeImpl']
export default deployFn
......@@ -91,6 +91,6 @@ const deployFn: DeployFunction = async (hre) => {
})
}
deployFn.tags = ['L2OutputOracleImpl', 'fresh', 'migration']
deployFn.tags = ['L2OutputOracleImpl']
export default deployFn
......@@ -12,6 +12,7 @@ const deployFn: DeployFunction = async (hre) => {
hre,
'L2OutputOracleProxy'
)
await deployAndVerifyAndThen({
hre,
name: 'OptimismPortal',
......@@ -34,6 +35,6 @@ const deployFn: DeployFunction = async (hre) => {
})
}
deployFn.tags = ['OptimismPortalImpl', 'fresh', 'migration']
deployFn.tags = ['OptimismPortalImpl']
export default deployFn
import { ethers } from 'ethers'
import { DeployFunction } from 'hardhat-deploy/dist/types'
import {
......@@ -8,18 +7,10 @@ import {
} from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
let L1StandardBridgeProxy: ethers.Contract
try {
L1StandardBridgeProxy = await getContractFromArtifact(
hre,
'Proxy__OVM_L1StandardBridge'
)
} catch (e) {
L1StandardBridgeProxy = await getContractFromArtifact(
hre,
'L1StandardBridgeProxy'
)
}
const L1StandardBridgeProxy = await getContractFromArtifact(
hre,
'Proxy__OVM_L1StandardBridge'
)
await deployAndVerifyAndThen({
hre,
......@@ -35,6 +26,6 @@ const deployFn: DeployFunction = async (hre) => {
})
}
deployFn.tags = ['OptimismMintableERC20FactoryImpl', 'fresh', 'migration']
deployFn.tags = ['OptimismMintableERC20FactoryImpl']
export default deployFn
import { ethers } from 'ethers'
import { DeployFunction } from 'hardhat-deploy/dist/types'
import { predeploys } from '../src'
......@@ -9,18 +8,10 @@ import {
} from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
let L1CrossDomainMessengerProxy: ethers.Contract
try {
L1CrossDomainMessengerProxy = await getContractFromArtifact(
hre,
'Proxy__OVM_L1CrossDomainMessenger'
)
} catch {
L1CrossDomainMessengerProxy = await getContractFromArtifact(
hre,
'L1CrossDomainMessengerProxy'
)
}
const L1CrossDomainMessengerProxy = await getContractFromArtifact(
hre,
'Proxy__OVM_L1CrossDomainMessenger'
)
await deployAndVerifyAndThen({
hre,
......@@ -36,6 +27,6 @@ const deployFn: DeployFunction = async (hre) => {
})
}
deployFn.tags = ['L1ERC721BridgeImpl', 'fresh', 'migration']
deployFn.tags = ['L1ERC721BridgeImpl']
export default deployFn
......@@ -11,6 +11,7 @@ const deployFn: DeployFunction = async (hre) => {
hre,
'OptimismPortalProxy'
)
await deployAndVerifyAndThen({
hre,
name: 'PortalSender',
......@@ -25,6 +26,6 @@ const deployFn: DeployFunction = async (hre) => {
})
}
deployFn.tags = ['PortalSenderImpl', 'fresh', 'migration']
deployFn.tags = ['PortalSenderImpl']
export default deployFn
import { DeployFunction } from 'hardhat-deploy/dist/types'
import '@eth-optimism/hardhat-deploy-config'
import 'hardhat-deploy'
import {
deployAndVerifyAndThen,
assertDictatorConfig,
makeDictatorConfig,
} from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
const config = await makeDictatorConfig(
hre,
deployer,
hre.deployConfig.finalSystemOwner,
true
)
await deployAndVerifyAndThen({
hre,
name: 'FreshSystemDictator',
args: [config],
postDeployAction: async (contract) => {
await assertDictatorConfig(contract, config)
},
})
}
deployFn.tags = ['FreshSystemDictator', 'fresh']
export default deployFn
......@@ -11,6 +11,7 @@ const deployFn: DeployFunction = async (hre) => {
hre.deployConfig.batchSenderAddress,
32
)
await deployAndVerifyAndThen({
hre,
name: 'SystemConfig',
......@@ -46,6 +47,6 @@ const deployFn: DeployFunction = async (hre) => {
})
}
deployFn.tags = ['SystemConfigImpl', 'fresh', 'migration']
deployFn.tags = ['SystemConfigImpl']
export default deployFn
import assert from 'assert'
import { ethers } from 'ethers'
import { DeployFunction } from 'hardhat-deploy/dist/types'
import '@eth-optimism/hardhat-deploy-config'
import 'hardhat-deploy'
import {
assertContractVariable,
getContractFromArtifact,
} from '../src/deploy-utils'
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
const ProxyAdmin = await getContractFromArtifact(hre, 'ProxyAdmin', {
signerOrProvider: deployer,
})
const FreshSystemDictator = await getContractFromArtifact(
hre,
'FreshSystemDictator',
{
signerOrProvider: deployer,
}
)
if ((await ProxyAdmin.owner()) !== FreshSystemDictator.address) {
console.log(`Transferring proxy admin ownership to the FreshSystemDictator`)
await ProxyAdmin.transferOwnership(FreshSystemDictator.address)
} else {
console.log(`Proxy admin already owned by the FreshSystemDictator`)
}
if ((await FreshSystemDictator.currentStep()) === 1) {
console.log(`Executing step 1`)
await FreshSystemDictator.step1()
// Check L2OutputOracle was initialized properly.
const L2OutputOracle = await getContractFromArtifact(
hre,
'L2OutputOracleProxy',
{
iface: 'L2OutputOracle',
}
)
await assertContractVariable(
L2OutputOracle,
'latestBlockNumber',
hre.deployConfig.l2OutputOracleStartingBlockNumber
)
await assertContractVariable(
L2OutputOracle,
'proposer',
hre.deployConfig.l2OutputOracleProposer
)
await assertContractVariable(
L2OutputOracle,
'owner',
hre.deployConfig.l2OutputOracleOwner
)
if (
hre.deployConfig.l2OutputOracleGenesisL2Output !==
ethers.constants.HashZero
) {
const genesisOutput = await L2OutputOracle.getL2Output(
hre.deployConfig.l2OutputOracleStartingBlockNumber
)
assert(
genesisOutput.outputRoot ===
hre.deployConfig.l2OutputOracleGenesisL2Output,
`L2OutputOracle was not initialized with the correct genesis output root`
)
}
// Check OptimismPortal was initialized properly.
const OptimismPortal = await getContractFromArtifact(
hre,
'OptimismPortalProxy',
{
iface: 'OptimismPortal',
}
)
await assertContractVariable(
OptimismPortal,
'l2Sender',
'0x000000000000000000000000000000000000dEaD'
)
const resourceParams = await OptimismPortal.params()
assert(
resourceParams.prevBaseFee.eq(await OptimismPortal.INITIAL_BASE_FEE()),
`OptimismPortal was not initialized with the correct initial base fee`
)
assert(
resourceParams.prevBoughtGas.eq(0),
`OptimismPortal was not initialized with the correct initial bought gas`
)
assert(
!resourceParams.prevBlockNum.eq(0),
`OptimismPortal was not initialized with the correct initial block number`
)
// Check L1CrossDomainMessenger was initialized properly.
const L1CrossDomainMessenger = await getContractFromArtifact(
hre,
'L1CrossDomainMessengerProxy',
{
iface: 'L1CrossDomainMessenger',
}
)
try {
await L1CrossDomainMessenger.xDomainMessageSender()
assert(false, `L1CrossDomainMessenger was not initialized properly`)
} catch (err) {
assert(
err.message.includes('xDomainMessageSender is not set'),
`L1CrossDomainMessenger was not initialized properly`
)
}
await assertContractVariable(
L1CrossDomainMessenger,
'owner',
hre.deployConfig.finalSystemOwner
)
// Check L1StandardBridge was initialized properly.
const L1StandardBridge = await getContractFromArtifact(
hre,
'L1StandardBridgeProxy',
{
iface: 'L1StandardBridge',
}
)
await assertContractVariable(
L1StandardBridge,
'messenger',
L1CrossDomainMessenger.address
)
// Check OptimismMintableERC20Factory was initialized properly.
const OptimismMintableERC20Factory = await getContractFromArtifact(
hre,
'OptimismMintableERC20FactoryProxy',
{
iface: 'OptimismMintableERC20Factory',
}
)
await assertContractVariable(
OptimismMintableERC20Factory,
'bridge',
L1StandardBridge.address
)
// Check L1ERC721Bridge was initialized properly.
const L1ERC721Bridge = await getContractFromArtifact(
hre,
'L1ERC721BridgeProxy',
{
iface: 'L1ERC721Bridge',
}
)
await assertContractVariable(
L1ERC721Bridge,
'messenger',
L1CrossDomainMessenger.address
)
} else {
console.log(`Step 1 executed`)
}
if ((await FreshSystemDictator.currentStep()) === 2) {
console.log(`Executing step 2`)
await FreshSystemDictator.step2()
// Check the ProxyAdmin owner was changed properly.
await assertContractVariable(
ProxyAdmin,
'owner',
hre.deployConfig.finalSystemOwner
)
} else {
console.log(`Step 2 executed`)
}
}
deployFn.tags = ['FreshSystemDictatorSteps', 'fresh']
export default deployFn
......@@ -73,6 +73,6 @@ const deployFn: DeployFunction = async (hre) => {
})
}
deployFn.tags = ['MigrationSystemDictator', 'migration']
deployFn.tags = ['MigrationSystemDictator']
export default deployFn
......@@ -17,9 +17,24 @@ const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
let isLiveDeployer = false
if (hre.deployConfig.controller === deployer) {
console.log('using a live deployer')
isLiveDeployer = true
let controller = hre.deployConfig.controller
if (controller === ethers.constants.AddressZero) {
if (hre.network.config.live === false) {
console.log(`WARNING!!!`)
console.log(`WARNING!!!`)
console.log(`WARNING!!!`)
console.log(`WARNING!!! A controller address was not provided.`)
console.log(
`WARNING!!! Make sure you are ONLY doing this on a test network.`
)
console.log('using a live deployer')
isLiveDeployer = true
controller = deployer
} else {
throw new Error(
`controller address MUST NOT be the deployer on live networks`
)
}
}
// Set up required contract references.
......@@ -116,6 +131,8 @@ const deployFn: DeployFunction = async (hre) => {
// Transfer ownership of the L1CrossDomainMessenger to MigrationSystemDictator.
if (
(await AddressManager.getAddress('OVM_L1CrossDomainMessenger')) !==
ethers.constants.AddressZero &&
(await L1CrossDomainMessenger.owner()) !== MigrationSystemDictator.address
) {
if (isLiveDeployer) {
......@@ -279,7 +296,7 @@ const deployFn: DeployFunction = async (hre) => {
`OptimismPortal was not initialized with the correct initial block number`
)
assert(
(await hre.ethers.provider.getBalance(OptimismPortal.address)).gt(0)
(await hre.ethers.provider.getBalance(L1StandardBridge.address)).eq(0)
)
// Check L1CrossDomainMessenger was initialized properly.
......@@ -375,6 +392,6 @@ const deployFn: DeployFunction = async (hre) => {
}
}
deployFn.tags = ['MigrationSystemDictatorSteps', 'migration']
deployFn.tags = ['MigrationSystemDictatorSteps']
export default deployFn
......@@ -12,6 +12,9 @@ import './tasks'
const config: HardhatUserConfig = {
networks: {
hardhat: {
live: false,
},
devnetL1: {
live: false,
url: 'http://localhost:8545',
......@@ -38,6 +41,7 @@ const config: HardhatUserConfig = {
chainId: Number(process.env.CHAIN_ID),
url: process.env.L1_RPC || '',
accounts: [process.env.PRIVATE_KEY_DEPLOYER || ethers.constants.HashZero],
live: process.env.VERIFY_CONTRACTS === 'true',
},
'mainnet-forked': {
chainId: 1,
......@@ -340,8 +344,14 @@ const config: HardhatUserConfig = {
],
deployments: {
goerli: ['../contracts/deployments/goerli'],
mainnet: ['../contracts/deployments/mainnet'],
'mainnet-forked': ['../contracts/deployments/mainnet'],
mainnet: [
'../contracts/deployments/mainnet',
'../contracts-periphery/deployments/mainnet',
],
'mainnet-forked': [
'../contracts/deployments/mainnet',
'../contracts-periphery/deployments/mainnet',
],
},
},
solidity: {
......
......@@ -4,11 +4,12 @@ import { task, types } from 'hardhat/config'
import { HardhatRuntimeEnvironment } from 'hardhat/types'
import '@nomiclabs/hardhat-ethers'
import 'hardhat-deploy'
import { Event, Contract, Wallet, providers, utils } from 'ethers'
import {
predeploys,
getContractDefinition,
} from '@eth-optimism/contracts-bedrock'
import { Event, Contract, Wallet, providers, utils } from 'ethers'
import { sleep } from '@eth-optimism/core-utils'
import {
CrossChainMessenger,
......@@ -247,18 +248,32 @@ task('deposit-erc20', 'Deposits WETH9 onto L2.')
await depositTx.wait()
console.log(`ERC20 deposited - ${depositTx.hash}`)
const messageReceipt = await messenger.waitForMessageReceipt(depositTx)
if (messageReceipt.receiptStatus !== 1) {
throw new Error('deposit failed')
// Deposit might get reorged, wait 30s and also log for reorgs.
let prevBlockHash: string = ''
for (let i = 0; i < 30; i++) {
const messageReceipt = await messenger.waitForMessageReceipt(depositTx)
if (messageReceipt.receiptStatus !== 1) {
throw new Error('deposit failed')
}
if (messageReceipt.transactionReceipt.blockHash !== prevBlockHash) {
console.log(
`Block hash changed from ${prevBlockHash} to ${messageReceipt.transactionReceipt.blockHash}`
)
// Wait for stability, we want at least 30 seconds after any reorg
i = 0
}
prevBlockHash = messageReceipt.transactionReceipt.blockHash
await sleep(1000)
}
const l2Balance = await OptimismMintableERC20.balanceOf(address)
if (l2Balance.lt(utils.parseEther('1'))) {
throw new Error('bad deposit')
}
console.log(
`Deposit success - ${messageReceipt.transactionReceipt.transactionHash}`
)
console.log(`Deposit success`)
console.log('Starting withdrawal')
const preBalance = await WETH9.balanceOf(signer.address)
......
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