Commit 069f9c22 authored by Mark Tyneway's avatar Mark Tyneway Committed by Andreas Bigger

op-e2e: major refactor

Refactor op-e2e to use the same deploy script that is used in
production. There is only 1 way to deploy contracts now and
it goes through foundry. This removes the need to maintain
multiple sets of contracts deployments/devnets. It does
add a new tooling dep to the monorepo which is `geth`.
A top level `make` command is provided as well as some
docs in the `op-e2e` package on its usage.

The `devnetL1.json` deployconfig is the source of truth
for deploy configs in `op-e2e` now. In the long term,
we should delineate which config values are safe to alter
in memory and which are not. Some things are still coupled
together in weird ways, but I tried to add assertions where
I could to ensure that future people will get better error
messages.

To install geth, run the command:

`` `
make install-geth
```

To create the genesis state, run the command:

```
make devnet-allocs
```

This will run the forge deploy script against an instance of
`geth --dev` and then call `debug_dumpBlock` which will dump
the state into a format that we can inject at runtime into
the genesis of both `op-e2e` as well as the L1 of the docker
compose setup.

I tried to use `anvil` instead of `geth` but it does not
support this endpoint. It supports a different endpoint
that uses a compressed binary serialization of state,
it is easier to work with the JSON.
parent 3e60c7e1
...@@ -808,10 +808,19 @@ jobs: ...@@ -808,10 +808,19 @@ jobs:
steps: steps:
- checkout - checkout
- check-changed: - check-changed:
patterns: op-(.+),contracts-bedrock,contracts patterns: op-(.+),contracts-bedrock
- run: - run:
name: prep results dir name: prep results dir
command: mkdir -p /tmp/test-results command: mkdir -p /tmp/test-results
- run:
name: install geth
command: make install-geth
- run:
name: git submodules
command: git submodule update --init --recursive
- run:
name: generate L1 state
command: make devnet-allocs
- run: - run:
name: run tests name: run tests
command: | command: |
...@@ -843,7 +852,6 @@ jobs: ...@@ -843,7 +852,6 @@ jobs:
default: this-package-does-not-exist default: this-package-does-not-exist
docker: docker:
- image: us-docker.pkg.dev/oplabs-tools-artifacts/images/ci-builder:latest - image: us-docker.pkg.dev/oplabs-tools-artifacts/images/ci-builder:latest
- image: cimg/postgres:14.1
steps: steps:
- checkout - checkout
- check-changed: - check-changed:
...@@ -858,7 +866,7 @@ jobs: ...@@ -858,7 +866,7 @@ jobs:
name: Test name: Test
command: | command: |
mkdir -p /test-results mkdir -p /test-results
DB_USER=postgres gotestsum --junitfile /test-results/tests.xml gotestsum --junitfile /test-results/tests.xml
working_directory: <<parameters.working_directory>> working_directory: <<parameters.working_directory>>
- when: - when:
condition: condition:
...@@ -869,14 +877,43 @@ jobs: ...@@ -869,14 +877,43 @@ jobs:
command: make <<parameters.binary_name>> command: make <<parameters.binary_name>>
working_directory: <<parameters.working_directory>> working_directory: <<parameters.working_directory>>
indexer-tests:
docker:
- image: us-docker.pkg.dev/oplabs-tools-artifacts/images/ci-builder:latest
- image: cimg/postgres:14.1
steps:
- checkout
- check-changed:
patterns: indexer
- run:
name: Lint
command: golangci-lint run -E goimports,sqlclosecheck,bodyclose,asciicheck,misspell,errorlint --timeout 2m -e "errors.As" -e "errors.Is" ./...
working_directory: indexer
- run:
name: install geth
command: make install-geth
- run:
name: git submodules
command: git submodule update --init --recursive
- run:
name: generate L1 state
command: make devnet-allocs
- store_test_results:
path: /test-results
- run:
name: Test
command: |
mkdir -p /test-results
DB_USER=postgres gotestsum --junitfile /test-results/tests.xml
working_directory: indexer
- run:
name: Build
command: make indexer
working_directory: indexer
devnet: devnet:
machine: machine:
image: ubuntu-2204:2022.10.2 image: ubuntu-2204:2022.10.2
parameters:
deploy:
description: Deploy contracts
default: false
type: boolean
environment: environment:
DOCKER_BUILDKIT: 1 DOCKER_BUILDKIT: 1
steps: steps:
...@@ -886,9 +923,9 @@ jobs: ...@@ -886,9 +923,9 @@ jobs:
- run: - run:
name: Install latest golang name: Install latest golang
command: | command: |
wget https://go.dev/dl/go1.19.linux-amd64.tar.gz wget https://go.dev/dl/go1.20.linux-amd64.tar.gz
sudo rm -rf /usr/local/go sudo rm -rf /usr/local/go
sudo tar -C /usr/local -xzf go1.19.linux-amd64.tar.gz sudo tar -C /usr/local -xzf go1.20.linux-amd64.tar.gz
export PATH=$PATH:/usr/local/go/bin export PATH=$PATH:/usr/local/go/bin
go version go version
- run: - run:
...@@ -900,6 +937,9 @@ jobs: ...@@ -900,6 +937,9 @@ jobs:
echo 'export PATH=$HOME/.foundry/bin:$PATH' >> $BASH_ENV echo 'export PATH=$HOME/.foundry/bin:$PATH' >> $BASH_ENV
source $HOME/.bashrc source $HOME/.bashrc
forge --version forge --version
- run:
name: install geth
command: make install-geth
- run: - run:
name: Install NVM name: Install NVM
command: | command: |
...@@ -911,39 +951,31 @@ jobs: ...@@ -911,39 +951,31 @@ jobs:
command: | command: |
nvm install nvm install
nvm use && node --version && npm --version nvm use && node --version && npm --version
# TODO remove me after ci builder updated
# A github dep clones-with-immutable-args is installed via github
# packages installed via npm via github automatically run postpack scripts
# their postpack script happens to use yarn so we need it here
- run: - run:
name: Install Package managers name: Install pnpm
command: | command: |
npm i pnpm --global npm i pnpm --global
npm i yarn@1 --global - run:
name: git submodules
command: git submodule update --init --recursive
- run: - run:
name: Install and build name: Install and build
command: | command: |
pnpm install && pnpm build pnpm install && pnpm build
- when:
condition:
and:
- equal: [true, <<parameters.deploy>>]
steps:
- run: - run:
name: Bring up the stack name: Bring up the stack
command: | command: make devnet-up
make devnet-up-deploy
- run: - run:
name: Check L2 config name: Check L2 config
command: go run cmd/check-l2/main.go --l2-rpc-url http://localhost:9545 --l1-rpc-url http://localhost:8545 command: go run cmd/check-l2/main.go --l2-rpc-url http://localhost:9545 --l1-rpc-url http://localhost:8545
working_directory: op-chain-ops working_directory: op-chain-ops
- run: - run:
name: Deposit ERC20 through the bridge name: Deposit ERC20 through the bridge
command: timeout 8m npx hardhat deposit-erc20 --network devnetL1 --l1-contracts-json-path ../../.devnet/sdk-addresses.json command: timeout 8m npx hardhat deposit-erc20 --network devnetL1 --l1-contracts-json-path ../../.devnet/addresses.json
working_directory: packages/sdk working_directory: packages/sdk
- run: - run:
name: Deposit ETH through the bridge name: Deposit ETH through the bridge
command: timeout 8m npx hardhat deposit-eth --network devnetL1 --l1-contracts-json-path ../../.devnet/sdk-addresses.json command: timeout 8m npx hardhat deposit-eth --network devnetL1 --l1-contracts-json-path ../../.devnet/addresses.json
working_directory: packages/sdk working_directory: packages/sdk
- run: - run:
name: Dump op-node logs name: Dump op-node logs
...@@ -982,52 +1014,6 @@ jobs: ...@@ -982,52 +1014,6 @@ jobs:
ls -R forge-artifacts || echo "No forge artifacts found" ls -R forge-artifacts || echo "No forge artifacts found"
when: on_fail when: on_fail
working_directory: packages/contracts-bedrock working_directory: packages/contracts-bedrock
- when:
condition:
and:
- equal: [false, <<parameters.deploy>>]
steps:
- run:
name: Bring up the stack
command: |
make devnet-up
- run:
name: Check L2 config
command: go run cmd/check-l2/main.go --l2-rpc-url http://localhost:9545 --l1-rpc-url http://localhost:8545
working_directory: op-chain-ops
- run:
name: Deposit ERC20 through the bridge
command: timeout 10m npx hardhat deposit-erc20 --network devnetL1
working_directory: packages/sdk
- run:
name: Deposit ETH through the bridge
command: timeout 10m npx hardhat deposit-eth --network devnetL1
working_directory: packages/sdk
- run:
name: Dump op-node logs
command: |
docker logs ops-bedrock-op-node-1 || echo "No logs."
when: on_fail
- run:
name: Dump op-geth logs
command: |
docker logs ops-bedrock-l2-1 || echo "No logs."
when: on_fail
- run:
name: Dump l1 logs
command: |
docker logs ops-bedrock-l1-1 || echo "No logs."
when: on_fail
- run:
name: Dump op-batcher logs
command: |
docker logs ops-bedrock-op-batcher-1 || echo "No logs."
when: on_fail
- run:
name: Dump op-proposer logs
command: |
docker logs ops-bedrock-op-proposer-1 || echo "No logs."
when: on_fail
semgrep-scan: semgrep-scan:
parameters: parameters:
...@@ -1231,20 +1217,12 @@ workflows: ...@@ -1231,20 +1217,12 @@ workflows:
- depcheck: - depcheck:
requires: requires:
- pnpm-monorepo - pnpm-monorepo
- devnet: - devnet
name: devnet (with deployed contracts)
deploy: true
- devnet:
name: devnet (with genesis contracts)
deploy: false
- go-lint-test-build: - go-lint-test-build:
name: proxyd-tests name: proxyd-tests
binary_name: proxyd binary_name: proxyd
working_directory: proxyd working_directory: proxyd
- go-lint-test-build: - indexer-tests
name: indexer-tests
binary_name: indexer
working_directory: indexer
- go-lint-test-build: - go-lint-test-build:
name: op-heartbeat tests name: op-heartbeat tests
binary_name: op-heartbeat binary_name: op-heartbeat
......
...@@ -68,6 +68,7 @@ You'll need the following: ...@@ -68,6 +68,7 @@ You'll need the following:
* [Docker Compose](https://docs.docker.com/compose/install/) * [Docker Compose](https://docs.docker.com/compose/install/)
* [Go](https://go.dev/dl/) * [Go](https://go.dev/dl/)
* [Foundry](https://getfoundry.sh) * [Foundry](https://getfoundry.sh)
* [go-ethereum](https://github.com/ethereum/go-ethereum)
### Setup ### Setup
...@@ -80,7 +81,7 @@ cd optimism ...@@ -80,7 +81,7 @@ cd optimism
### Install the Correct Version of NodeJS ### Install the Correct Version of NodeJS
Install node v16.16.0 with [nvm](https://github.com/nvm-sh/nvm) Install the correct node version with [nvm](https://github.com/nvm-sh/nvm)
```bash ```bash
nvm use nvm use
...@@ -112,10 +113,11 @@ Use the above commands to recompile the packages. ...@@ -112,10 +113,11 @@ Use the above commands to recompile the packages.
### Building the rest of the system ### Building the rest of the system
If you want to run an Optimism node OR **if you want to run the integration tests**, you'll need to build the rest of the system. If you want to run an Optimism node OR **if you want to run the integration tests**, you'll need to build the rest of the system.
Note that these environment variables significantly speed up build time.
```bash ```bash
cd ops cd ops-bedrock
export COMPOSE_DOCKER_CLI_BUILD=1 # these environment variables significantly speed up build time export COMPOSE_DOCKER_CLI_BUILD=1
export DOCKER_BUILDKIT=1 export DOCKER_BUILDKIT=1
docker-compose build docker-compose build
``` ```
...@@ -124,7 +126,7 @@ Source code changes can have an impact on more than one container. ...@@ -124,7 +126,7 @@ Source code changes can have an impact on more than one container.
**If you're unsure about which containers to rebuild, just rebuild them all**: **If you're unsure about which containers to rebuild, just rebuild them all**:
```bash ```bash
cd ops cd ops-bedrock
docker-compose down docker-compose down
docker-compose build docker-compose build
docker-compose up docker-compose up
......
...@@ -77,8 +77,9 @@ devnet-up: ...@@ -77,8 +77,9 @@ devnet-up:
PYTHONPATH=./bedrock-devnet python3 ./bedrock-devnet/main.py --monorepo-dir=. PYTHONPATH=./bedrock-devnet python3 ./bedrock-devnet/main.py --monorepo-dir=.
.PHONY: devnet-up .PHONY: devnet-up
# alias for devnet-up
devnet-up-deploy: devnet-up-deploy:
PYTHONPATH=./bedrock-devnet python3 ./bedrock-devnet/main.py --monorepo-dir=. --deploy PYTHONPATH=./bedrock-devnet python3 ./bedrock-devnet/main.py --monorepo-dir=.
.PHONY: devnet-up-deploy .PHONY: devnet-up-deploy
devnet-down: devnet-down:
...@@ -93,6 +94,9 @@ devnet-clean: ...@@ -93,6 +94,9 @@ devnet-clean:
docker volume ls --filter name=ops-bedrock --format='{{.Name}}' | xargs -r docker volume rm docker volume ls --filter name=ops-bedrock --format='{{.Name}}' | xargs -r docker volume rm
.PHONY: devnet-clean .PHONY: devnet-clean
devnet-allocs:
PYTHONPATH=./bedrock-devnet python3 ./bedrock-devnet/main.py --monorepo-dir=. --allocs
devnet-logs: devnet-logs:
@(cd ./ops-bedrock && docker-compose logs -f) @(cd ./ops-bedrock && docker-compose logs -f)
.PHONY: devnet-logs .PHONY: devnet-logs
...@@ -133,3 +137,6 @@ bedrock-markdown-links: ...@@ -133,3 +137,6 @@ bedrock-markdown-links:
docker run --init -it -v `pwd`:/input lycheeverse/lychee --verbose --no-progress --exclude-loopback \ docker run --init -it -v `pwd`:/input lycheeverse/lychee --verbose --no-progress --exclude-loopback \
--exclude twitter.com --exclude explorer.optimism.io --exclude linux-mips.org \ --exclude twitter.com --exclude explorer.optimism.io --exclude linux-mips.org \
--exclude-mail /input/README.md "/input/specs/**/*.md" --exclude-mail /input/README.md "/input/specs/**/*.md"
install-geth:
go install github.com/ethereum/go-ethereum/cmd/geth@v1.12.0
...@@ -9,15 +9,15 @@ import datetime ...@@ -9,15 +9,15 @@ import datetime
import time import time
import shutil import shutil
import http.client import http.client
import multiprocessing
import devnet.log_setup import devnet.log_setup
from devnet.genesis import GENESIS_TMPL
pjoin = os.path.join pjoin = os.path.join
parser = argparse.ArgumentParser(description='Bedrock devnet launcher') parser = argparse.ArgumentParser(description='Bedrock devnet launcher')
parser.add_argument('--monorepo-dir', help='Directory of the monorepo', default=os.getcwd()) parser.add_argument('--monorepo-dir', help='Directory of the monorepo', default=os.getcwd())
parser.add_argument('--deploy', help='Whether the contracts should be predeployed or deployed', type=bool, action=argparse.BooleanOptionalAction) parser.add_argument('--allocs', help='Path to the allocs file', type=bool, action=argparse.BooleanOptionalAction)
log = logging.getLogger() log = logging.getLogger()
...@@ -25,6 +25,7 @@ class Bunch: ...@@ -25,6 +25,7 @@ class Bunch:
def __init__(self, **kwds): def __init__(self, **kwds):
self.__dict__.update(kwds) self.__dict__.update(kwds)
def main(): def main():
args = parser.parse_args() args = parser.parse_args()
...@@ -33,18 +34,23 @@ def main(): ...@@ -33,18 +34,23 @@ def main():
contracts_bedrock_dir = pjoin(monorepo_dir, 'packages', 'contracts-bedrock') contracts_bedrock_dir = pjoin(monorepo_dir, 'packages', 'contracts-bedrock')
deployment_dir = pjoin(contracts_bedrock_dir, 'deployments', 'devnetL1') deployment_dir = pjoin(contracts_bedrock_dir, 'deployments', 'devnetL1')
op_node_dir = pjoin(args.monorepo_dir, 'op-node') op_node_dir = pjoin(args.monorepo_dir, 'op-node')
ops_bedrock_dir=pjoin(monorepo_dir, 'ops-bedrock') ops_bedrock_dir = pjoin(monorepo_dir, 'ops-bedrock')
deploy_config_dir = pjoin(contracts_bedrock_dir, 'deploy-config'),
devnet_config_path = pjoin(contracts_bedrock_dir, 'deploy-config', 'devnetL1.json')
paths = Bunch( paths = Bunch(
mono_repo_dir=monorepo_dir, mono_repo_dir=monorepo_dir,
devnet_dir=devnet_dir, devnet_dir=devnet_dir,
contracts_bedrock_dir=contracts_bedrock_dir, contracts_bedrock_dir=contracts_bedrock_dir,
deployment_dir=deployment_dir, deployment_dir=deployment_dir,
deploy_config_dir=pjoin(contracts_bedrock_dir, 'deploy-config'), l1_deployments_path=pjoin(deployment_dir, '.deploy'),
deploy_config_dir=deploy_config_dir,
devnet_config_path=devnet_config_path,
op_node_dir=op_node_dir, op_node_dir=op_node_dir,
ops_bedrock_dir=ops_bedrock_dir, ops_bedrock_dir=ops_bedrock_dir,
genesis_l1_path=pjoin(devnet_dir, 'genesis-l1.json'), genesis_l1_path=pjoin(devnet_dir, 'genesis-l1.json'),
genesis_l2_path=pjoin(devnet_dir, 'genesis-l2.json'), genesis_l2_path=pjoin(devnet_dir, 'genesis-l2.json'),
allocs_path=pjoin(devnet_dir, 'allocs-l1.json'),
addresses_json_path=pjoin(devnet_dir, 'addresses.json'), addresses_json_path=pjoin(devnet_dir, 'addresses.json'),
sdk_addresses_json_path=pjoin(devnet_dir, 'sdk-addresses.json'), sdk_addresses_json_path=pjoin(devnet_dir, 'sdk-addresses.json'),
rollup_config_path=pjoin(devnet_dir, 'rollup.json') rollup_config_path=pjoin(devnet_dir, 'rollup.json')
...@@ -52,61 +58,64 @@ def main(): ...@@ -52,61 +58,64 @@ def main():
os.makedirs(devnet_dir, exist_ok=True) os.makedirs(devnet_dir, exist_ok=True)
if args.allocs:
devnet_l1_genesis(paths)
return
log.info('Building docker images')
run_command(['docker-compose', 'build', '--progress', 'plain'], cwd=paths.ops_bedrock_dir, env={ run_command(['docker-compose', 'build', '--progress', 'plain'], cwd=paths.ops_bedrock_dir, env={
'PWD': paths.ops_bedrock_dir 'PWD': paths.ops_bedrock_dir
}) })
if args.deploy: log.info('Devnet starting')
log.info('Devnet with upcoming smart contract deployments')
devnet_deploy(paths) devnet_deploy(paths)
else:
log.info('Devnet with smart contracts pre-deployed')
devnet_prestate(paths)
# Bring up the devnet where the L1 contracts are in the genesis state
def devnet_prestate(paths):
date = datetime.datetime.utcnow()
utc_time = hex(calendar.timegm(date.utctimetuple()))
done_file = pjoin(paths.devnet_dir, 'done') def deploy_contracts(paths):
if os.path.exists(done_file): def internal():
log.info('Genesis files already exist') wait_up(8545)
else: wait_for_rpc_server('127.0.0.1:8545')
log.info('Creating genesis files') res = eth_accounts('127.0.0.1:8545')
deploy_config_path = pjoin(paths.deploy_config_dir, 'devnetL1.json')
# read the json file response = json.loads(res)
deploy_config = read_json(deploy_config_path) account = response['result'][0]
deploy_config['l1GenesisBlockTimestamp'] = utc_time
temp_deploy_config = pjoin(paths.devnet_dir, 'deploy-config.json')
write_json(temp_deploy_config, deploy_config)
outfile_l1 = paths.genesis_l1_path fqn = 'scripts/Deploy.s.sol:Deploy'
outfile_l2 = paths.genesis_l2_path run_command([
outfile_rollup = paths.rollup_config_path 'forge', 'script', fqn, '--sender', account,
'--rpc-url', 'http://127.0.0.1:8545', '--broadcast',
'--unlocked'
], env={}, cwd=paths.contracts_bedrock_dir)
run_command(['go', 'run', 'cmd/main.go', 'genesis', 'devnet', '--deploy-config', temp_deploy_config, '--outfile.l1', outfile_l1, '--outfile.l2', outfile_l2, '--outfile.rollup', outfile_rollup], cwd=paths.op_node_dir) shutil.copy(paths.l1_deployments_path, paths.addresses_json_path)
write_json(done_file, {})
log.info('Bringing up L1.') log.info('Syncing contracts.')
run_command(['docker-compose', 'up', '-d', 'l1'], cwd=paths.ops_bedrock_dir, env={ run_command([
'PWD': paths.ops_bedrock_dir 'forge', 'script', fqn, '--sig', 'sync()',
}) '--rpc-url', 'http://127.0.0.1:8545'
wait_up(8545) ], env={}, cwd=paths.contracts_bedrock_dir)
wait_for_rpc_server('127.0.0.1:8545')
log.info('Bringing up L2.') return internal
run_command(['docker-compose', 'up', '-d', 'l2'], cwd=paths.ops_bedrock_dir, env={
'PWD': paths.ops_bedrock_dir
}) def devnet_l1_genesis(paths):
wait_up(9545) log.info('Generating L1 genesis state')
wait_for_rpc_server('127.0.0.1:9545') geth = subprocess.Popen([
'geth', '--dev', '--http', '--http.api', 'eth,debug',
'--verbosity', '4', '--gcmode', 'archive', '--dev.gaslimit', '30000000'
])
forge = multiprocessing.Process(target=deploy_contracts(paths))
forge.start()
forge.join()
res = debug_dumpBlock('127.0.0.1:8545')
response = json.loads(res)
allocs = response['result']
write_json(paths.allocs_path, allocs)
geth.terminate()
log.info('Bringing up the services.')
run_command(['docker-compose', 'up', '-d', 'op-proposer', 'op-batcher'], cwd=paths.ops_bedrock_dir, env={
'PWD': paths.ops_bedrock_dir,
'L2OO_ADDRESS': '0x6900000000000000000000000000000000000000'
})
# Bring up the devnet where the contracts are deployed to L1 # Bring up the devnet where the contracts are deployed to L1
def devnet_deploy(paths): def devnet_deploy(paths):
...@@ -114,7 +123,23 @@ def devnet_deploy(paths): ...@@ -114,7 +123,23 @@ def devnet_deploy(paths):
log.info('L1 genesis already generated.') log.info('L1 genesis already generated.')
else: else:
log.info('Generating L1 genesis.') log.info('Generating L1 genesis.')
write_json(paths.genesis_l1_path, GENESIS_TMPL) if os.path.exists(paths.allocs_path) == False:
devnet_l1_genesis(paths)
devnet_config_backup = pjoin(paths.devnet_dir, 'devnetL1.json.bak')
shutil.copy(paths.devnet_config_path, devnet_config_backup)
deploy_config = read_json(paths.devnet_config_path)
deploy_config['l1GenesisBlockTimestamp'] = '{:#x}'.format(int(time.time()))
write_json(paths.devnet_config_path, deploy_config)
outfile_l1 = pjoin(paths.devnet_dir, 'genesis-l1.json')
run_command([
'go', 'run', 'cmd/main.go', 'genesis', 'l1',
'--deploy-config', paths.devnet_config_path,
'--l1-allocs', paths.allocs_path,
'--l1-deployments', paths.addresses_json_path,
'--outfile.l1', outfile_l1,
], cwd=paths.op_node_dir)
log.info('Starting L1.') log.info('Starting L1.')
run_command(['docker-compose', 'up', '-d', 'l1'], cwd=paths.ops_bedrock_dir, env={ run_command(['docker-compose', 'up', '-d', 'l1'], cwd=paths.ops_bedrock_dir, env={
...@@ -123,56 +148,6 @@ def devnet_deploy(paths): ...@@ -123,56 +148,6 @@ def devnet_deploy(paths):
wait_up(8545) wait_up(8545)
wait_for_rpc_server('127.0.0.1:8545') wait_for_rpc_server('127.0.0.1:8545')
log.info('Generating network config.')
devnet_cfg_orig = pjoin(paths.contracts_bedrock_dir, 'deploy-config', 'devnetL1.json')
devnet_cfg_backup = pjoin(paths.devnet_dir, 'devnetL1.json.bak')
shutil.copy(devnet_cfg_orig, devnet_cfg_backup)
deploy_config = read_json(devnet_cfg_orig)
deploy_config['l1GenesisBlockTimestamp'] = GENESIS_TMPL['timestamp']
deploy_config['l1StartingBlockTag'] = 'earliest'
write_json(devnet_cfg_orig, deploy_config)
fqn = 'scripts/Deploy.s.sol:Deploy'
private_key = '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80'
if os.path.exists(paths.addresses_json_path):
log.info('Contracts already deployed.')
addresses = read_json(paths.addresses_json_path)
else:
log.info('Deploying contracts.')
run_command([
'forge', 'script', fqn, '--private-key', private_key,
'--rpc-url', 'http://127.0.0.1:8545', '--broadcast'
], env={}, cwd=paths.contracts_bedrock_dir)
run_command([
'forge', 'script', fqn, '--private-key', private_key,
'--sig', 'sync()', '--rpc-url', 'http://127.0.0.1:8545', '--broadcast'
], env={}, cwd=paths.contracts_bedrock_dir)
contracts = os.listdir(paths.deployment_dir)
addresses = {}
for c in contracts:
if not c.endswith('.json'):
continue
data = read_json(pjoin(paths.deployment_dir, c))
addresses[c.replace('.json', '')] = data['address']
sdk_addresses = {}
sdk_addresses.update({
'AddressManager': '0x0000000000000000000000000000000000000000',
'StateCommitmentChain': '0x0000000000000000000000000000000000000000',
'CanonicalTransactionChain': '0x0000000000000000000000000000000000000000',
'BondManager': '0x0000000000000000000000000000000000000000',
})
sdk_addresses['L1CrossDomainMessenger'] = addresses['L1CrossDomainMessengerProxy']
sdk_addresses['L1StandardBridge'] = addresses['L1StandardBridgeProxy']
sdk_addresses['OptimismPortal'] = addresses['OptimismPortalProxy']
sdk_addresses['L2OutputOracle'] = addresses['L2OutputOracleProxy']
write_json(paths.addresses_json_path, addresses)
write_json(paths.sdk_addresses_json_path, sdk_addresses)
log.info(f'Wrote sdk addresses to {paths.sdk_addresses_json_path}')
if os.path.exists(paths.genesis_l2_path): if os.path.exists(paths.genesis_l2_path):
log.info('L2 genesis and rollup configs already generated.') log.info('L2 genesis and rollup configs already generated.')
else: else:
...@@ -180,16 +155,14 @@ def devnet_deploy(paths): ...@@ -180,16 +155,14 @@ def devnet_deploy(paths):
run_command([ run_command([
'go', 'run', 'cmd/main.go', 'genesis', 'l2', 'go', 'run', 'cmd/main.go', 'genesis', 'l2',
'--l1-rpc', 'http://localhost:8545', '--l1-rpc', 'http://localhost:8545',
'--deploy-config', devnet_cfg_orig, '--deploy-config', paths.devnet_config_path,
'--deployment-dir', paths.deployment_dir, '--deployment-dir', paths.deployment_dir,
'--outfile.l2', pjoin(paths.devnet_dir, 'genesis-l2.json'), '--outfile.l2', pjoin(paths.devnet_dir, 'genesis-l2.json'),
'--outfile.rollup', pjoin(paths.devnet_dir, 'rollup.json') '--outfile.rollup', pjoin(paths.devnet_dir, 'rollup.json')
], cwd=paths.op_node_dir) ], cwd=paths.op_node_dir)
rollup_config = read_json(paths.rollup_config_path) rollup_config = read_json(paths.rollup_config_path)
addresses = read_json(paths.addresses_json_path)
if os.path.exists(devnet_cfg_backup):
shutil.move(devnet_cfg_backup, devnet_cfg_orig)
log.info('Bringing up L2.') log.info('Bringing up L2.')
run_command(['docker-compose', 'up', '-d', 'l2'], cwd=paths.ops_bedrock_dir, env={ run_command(['docker-compose', 'up', '-d', 'l2'], cwd=paths.ops_bedrock_dir, env={
...@@ -198,16 +171,45 @@ def devnet_deploy(paths): ...@@ -198,16 +171,45 @@ def devnet_deploy(paths):
wait_up(9545) wait_up(9545)
wait_for_rpc_server('127.0.0.1:9545') wait_for_rpc_server('127.0.0.1:9545')
l2_output_oracle = addresses['L2OutputOracleProxy']
log.info(f'Using L2OutputOracle {l2_output_oracle}')
batch_inbox_address = rollup_config['batch_inbox_address']
log.info(f'Using batch inbox {batch_inbox_address}')
log.info('Bringing up everything else.') log.info('Bringing up everything else.')
run_command(['docker-compose', 'up', '-d', 'op-node', 'op-proposer', 'op-batcher'], cwd=paths.ops_bedrock_dir, env={ run_command(['docker-compose', 'up', '-d', 'op-node', 'op-proposer', 'op-batcher'], cwd=paths.ops_bedrock_dir, env={
'PWD': paths.ops_bedrock_dir, 'PWD': paths.ops_bedrock_dir,
'L2OO_ADDRESS': addresses['L2OutputOracleProxy'], 'L2OO_ADDRESS': l2_output_oracle,
'SEQUENCER_BATCH_INBOX_ADDRESS': rollup_config['batch_inbox_address'] 'SEQUENCER_BATCH_INBOX_ADDRESS': batch_inbox_address
}) })
log.info('Devnet ready.') log.info('Devnet ready.')
def eth_accounts(url):
log.info(f'Fetch eth_accounts {url}')
conn = http.client.HTTPConnection(url)
headers = {'Content-type': 'application/json'}
body = '{"id":2, "jsonrpc":"2.0", "method": "eth_accounts", "params":[]}'
conn.request('POST', '/', body, headers)
response = conn.getresponse()
data = response.read().decode()
conn.close()
return data
def debug_dumpBlock(url):
log.info(f'Fetch debug_dumpBlock {url}')
conn = http.client.HTTPConnection(url)
headers = {'Content-type': 'application/json'}
body = '{"id":3, "jsonrpc":"2.0", "method": "debug_dumpBlock", "params":["latest"]}'
conn.request('POST', '/', body, headers)
response = conn.getresponse()
data = response.read().decode()
conn.close()
return data
def wait_for_rpc_server(url): def wait_for_rpc_server(url):
log.info(f'Waiting for RPC server at {url}') log.info(f'Waiting for RPC server at {url}')
......
import time
DEV_ACCOUNTS = [
'3c44cdddb6a900fa2b585dd299e03d12fa4293bc',
'70997970c51812dc3a010c7d01b50e0d17dc79c8',
'f39fd6e51aad88f6f4ce6ab8827279cfffb92266'
]
GENESIS_TMPL = {
'config': {
'chainId': 900,
"homesteadBlock": 0,
"eip150Block": 0,
"eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"eip155Block": 0,
"eip158Block": 0,
"byzantiumBlock": 0,
"constantinopleBlock": 0,
"petersburgBlock": 0,
"istanbulBlock": 0,
"muirGlacierBlock": 0,
"berlinBlock": 0,
"londonBlock": 0,
"arrowGlacierBlock": 0,
"grayGlacierBlock": 0,
"shanghaiBlock": None,
"cancunBlock": None,
'clique': {
'period': 3,
'epoch': 30000
}
},
'nonce': '0x0',
'timestamp': '{:#x}'.format(int(time.time())),
'extraData': '0x0000000000000000000000000000000000000000000000000000000000000000ca062b0fd91172d89bcd4bb084ac4e21972cc4670000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
'gasLimit': '0xE4E1C0',
'difficulty': '0x1',
'mixHash': '0x0000000000000000000000000000000000000000000000000000000000000000',
'coinbase': '0x0000000000000000000000000000000000000000',
'alloc': {
'{:x}'.format(i).ljust(40, '0'): {
'balance': '0x1'
} for i in range(0, 255)
},
'number': '0x0',
'gasUsed': '0x0',
'parentHash': '0x0000000000000000000000000000000000000000000000000000000000000000',
'baseFeePergas': '0x3B9ACA00'
}
GENESIS_TMPL['alloc'].update({
d: {
'balance': '0x200000000000000000000000000000000000000000000000000000000000000'
} for d in DEV_ACCOUNTS
})
...@@ -5,6 +5,7 @@ import ( ...@@ -5,6 +5,7 @@ import (
"github.com/BurntSushi/toml" "github.com/BurntSushi/toml"
"github.com/ethereum-optimism/optimism/indexer/processor"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
) )
...@@ -22,6 +23,7 @@ type Config struct { ...@@ -22,6 +23,7 @@ type Config struct {
type ChainConfig struct { type ChainConfig struct {
// Configure known chains with the l2 chain id // Configure known chains with the l2 chain id
Preset int Preset int
L1Contracts processor.L1Contracts
} }
// RPCsConfig configures the RPC urls // RPCsConfig configures the RPC urls
......
...@@ -7,7 +7,6 @@ import ( ...@@ -7,7 +7,6 @@ import (
"time" "time"
"github.com/ethereum-optimism/optimism/indexer/node" "github.com/ethereum-optimism/optimism/indexer/node"
"github.com/ethereum-optimism/optimism/indexer/processor"
"github.com/ethereum-optimism/optimism/op-service/client/utils" "github.com/ethereum-optimism/optimism/op-service/client/utils"
"github.com/ethereum-optimism/optimism/op-bindings/bindings" "github.com/ethereum-optimism/optimism/op-bindings/bindings"
...@@ -15,6 +14,7 @@ import ( ...@@ -15,6 +14,7 @@ import (
"github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
...@@ -25,7 +25,7 @@ func TestE2EBlockHeaders(t *testing.T) { ...@@ -25,7 +25,7 @@ func TestE2EBlockHeaders(t *testing.T) {
l1Client := testSuite.OpSys.Clients["l1"] l1Client := testSuite.OpSys.Clients["l1"]
l2Client := testSuite.OpSys.Clients["sequencer"] l2Client := testSuite.OpSys.Clients["sequencer"]
l2OutputOracle, err := bindings.NewL2OutputOracleCaller(predeploys.DevL2OutputOracleAddr, l1Client) l2OutputOracle, err := bindings.NewL2OutputOracleCaller(testSuite.OpCfg.L1Deployments.L2OutputOracleProxy, l1Client)
require.NoError(t, err) require.NoError(t, err)
// a minute for total setup to finish // a minute for total setup to finish
...@@ -111,7 +111,10 @@ func TestE2EBlockHeaders(t *testing.T) { ...@@ -111,7 +111,10 @@ func TestE2EBlockHeaders(t *testing.T) {
testCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second) testCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel() defer cancel()
devContracts := processor.DevL1Contracts().ToSlice() devContracts := make([]common.Address, 0)
testSuite.OpCfg.L1Deployments.ForEach(func(name string, address common.Address) {
devContracts = append(devContracts, address)
})
logFilter := ethereum.FilterQuery{FromBlock: big.NewInt(0), ToBlock: big.NewInt(int64(l1Height)), Addresses: devContracts} logFilter := ethereum.FilterQuery{FromBlock: big.NewInt(0), ToBlock: big.NewInt(int64(l1Height)), Addresses: devContracts}
logs, err := l1Client.FilterLogs(testCtx, logFilter) // []types.Log logs, err := l1Client.FilterLogs(testCtx, logFilter) // []types.Log
require.NoError(t, err) require.NoError(t, err)
......
...@@ -28,7 +28,7 @@ func TestE2EBridge(t *testing.T) { ...@@ -28,7 +28,7 @@ func TestE2EBridge(t *testing.T) {
l1Client := testSuite.OpSys.Clients["l1"] l1Client := testSuite.OpSys.Clients["l1"]
l2Client := testSuite.OpSys.Clients["sequencer"] l2Client := testSuite.OpSys.Clients["sequencer"]
l1StandardBridge, err := bindings.NewL1StandardBridge(predeploys.DevL1StandardBridgeAddr, l1Client) l1StandardBridge, err := bindings.NewL1StandardBridge(testSuite.OpCfg.L1Deployments.L1StandardBridgeProxy, l1Client)
require.NoError(t, err) require.NoError(t, err)
l2StandardBridge, err := bindings.NewL2StandardBridge(predeploys.L2StandardBridgeAddr, l2Client) l2StandardBridge, err := bindings.NewL2StandardBridge(predeploys.L2StandardBridgeAddr, l2Client)
......
...@@ -13,6 +13,7 @@ import ( ...@@ -13,6 +13,7 @@ import (
"github.com/ethereum-optimism/optimism/indexer" "github.com/ethereum-optimism/optimism/indexer"
"github.com/ethereum-optimism/optimism/indexer/config" "github.com/ethereum-optimism/optimism/indexer/config"
"github.com/ethereum-optimism/optimism/indexer/database" "github.com/ethereum-optimism/optimism/indexer/database"
"github.com/ethereum-optimism/optimism/indexer/processor"
op_e2e "github.com/ethereum-optimism/optimism/op-e2e" op_e2e "github.com/ethereum-optimism/optimism/op-e2e"
"github.com/ethereum-optimism/optimism/op-node/testlog" "github.com/ethereum-optimism/optimism/op-node/testlog"
...@@ -44,10 +45,17 @@ func createE2ETestSuite(t *testing.T) E2ETestSuite { ...@@ -44,10 +45,17 @@ func createE2ETestSuite(t *testing.T) E2ETestSuite {
// Rollup System Configuration and Start // Rollup System Configuration and Start
opCfg := op_e2e.DefaultSystemConfig(t) opCfg := op_e2e.DefaultSystemConfig(t)
opCfg.DeployConfig.FinalizationPeriodSeconds = 2
opSys, err := opCfg.Start() opSys, err := opCfg.Start()
require.NoError(t, err) require.NoError(t, err)
l1Contracts := processor.L1Contracts{
OptimismPortal: opCfg.L1Deployments.OptimismPortalProxy,
L2OutputOracle: opCfg.L1Deployments.L2OutputOracleProxy,
L1CrossDomainMessenger: opCfg.L1Deployments.L1CrossDomainMessengerProxy,
L1StandardBridge: opCfg.L1Deployments.L1StandardBridgeProxy,
L1ERC721Bridge: opCfg.L1Deployments.L1ERC721BridgeProxy,
}
// Indexer Configuration and Start // Indexer Configuration and Start
indexerCfg := config.Config{ indexerCfg := config.Config{
DB: config.DBConfig{ DB: config.DBConfig{
...@@ -61,6 +69,9 @@ func createE2ETestSuite(t *testing.T) E2ETestSuite { ...@@ -61,6 +69,9 @@ func createE2ETestSuite(t *testing.T) E2ETestSuite {
L2RPC: opSys.Nodes["sequencer"].HTTPEndpoint(), L2RPC: opSys.Nodes["sequencer"].HTTPEndpoint(),
}, },
Logger: logger, Logger: logger,
Chain: config.ChainConfig{
L1Contracts: l1Contracts,
},
} }
db, err := database.NewDB(fmt.Sprintf("postgres://%s@localhost:5432/%s?sslmode=disable", dbUser, dbName)) db, err := database.NewDB(fmt.Sprintf("postgres://%s@localhost:5432/%s?sslmode=disable", dbUser, dbName))
......
...@@ -38,8 +38,7 @@ func NewIndexer(cfg config.Config) (*Indexer, error) { ...@@ -38,8 +38,7 @@ func NewIndexer(cfg config.Config) (*Indexer, error) {
return nil, err return nil, err
} }
// L1 Processor (hardhat devnet contracts). Make this configurable l1Contracts := cfg.Chain.L1Contracts
l1Contracts := processor.DevL1Contracts()
l1EthClient, err := node.DialEthClient(cfg.RPCs.L1RPC) l1EthClient, err := node.DialEthClient(cfg.RPCs.L1RPC)
if err != nil { if err != nil {
return nil, err return nil, err
......
...@@ -46,7 +46,7 @@ func TestBedrockIndexer(t *testing.T) { ...@@ -46,7 +46,7 @@ func TestBedrockIndexer(t *testing.T) {
// wait a couple of blocks // wait a couple of blocks
require.NoError(t, utils.WaitBlock(e2eutils.TimeoutCtx(t, 30*time.Second), l2Client, 10)) require.NoError(t, utils.WaitBlock(e2eutils.TimeoutCtx(t, 30*time.Second), l2Client, 10))
l1SB, err := bindings.NewL1StandardBridge(predeploys.DevL1StandardBridgeAddr, l1Client) l1SB, err := bindings.NewL1StandardBridge(cfg.L1Deployments.L1StandardBridgeProxy, l1Client)
require.NoError(t, err) require.NoError(t, err)
l2SB, err := bindings.NewL2StandardBridge(predeploys.L2StandardBridgeAddr, l2Client) l2SB, err := bindings.NewL2StandardBridge(predeploys.L2StandardBridgeAddr, l2Client)
require.NoError(t, err) require.NoError(t, err)
...@@ -208,7 +208,7 @@ func TestBedrockIndexer(t *testing.T) { ...@@ -208,7 +208,7 @@ func TestBedrockIndexer(t *testing.T) {
require.Nil(t, wd.BedrockFinalizedTxHash) require.Nil(t, wd.BedrockFinalizedTxHash)
// Finalize withdrawal // Finalize withdrawal
err = withdrawals.WaitForFinalizationPeriod(e2eutils.TimeoutCtx(t, 30*time.Second), l1Client, predeploys.DevOptimismPortalAddr, proveReceipt.BlockNumber) err = withdrawals.WaitForFinalizationPeriod(e2eutils.TimeoutCtx(t, 30*time.Second), l1Client, cfg.L1Deployments.OptimismPortalProxy, proveReceipt.BlockNumber)
require.Nil(t, err) require.Nil(t, err)
finReceipt := op_e2e.FinalizeWithdrawal(t, cfg, l1Client, cfg.Secrets.Alice, wdReceipt, wdParams) finReceipt := op_e2e.FinalizeWithdrawal(t, cfg, l1Client, cfg.Secrets.Alice, wdReceipt, wdParams)
......
...@@ -34,16 +34,6 @@ type L1Contracts struct { ...@@ -34,16 +34,6 @@ type L1Contracts struct {
// Remove afterwards? // Remove afterwards?
} }
func DevL1Contracts() L1Contracts {
return L1Contracts{
OptimismPortal: common.HexToAddress("0x6900000000000000000000000000000000000000"),
L2OutputOracle: common.HexToAddress("0x6900000000000000000000000000000000000001"),
L1CrossDomainMessenger: common.HexToAddress("0x6900000000000000000000000000000000000002"),
L1StandardBridge: common.HexToAddress("0x6900000000000000000000000000000000000003"),
L1ERC721Bridge: common.HexToAddress("0x6900000000000000000000000000000000000004"),
}
}
func (c L1Contracts) ToSlice() []common.Address { func (c L1Contracts) ToSlice() []common.Address {
fields := reflect.VisibleFields(reflect.TypeOf(c)) fields := reflect.VisibleFields(reflect.TypeOf(c))
v := reflect.ValueOf(c) v := reflect.ValueOf(c)
......
package predeploys
import "github.com/ethereum/go-ethereum/common"
const (
DevL2OutputOracle = "0x6900000000000000000000000000000000000000"
DevOptimismPortal = "0x6900000000000000000000000000000000000001"
DevL1CrossDomainMessenger = "0x6900000000000000000000000000000000000002"
DevL1StandardBridge = "0x6900000000000000000000000000000000000003"
DevOptimismMintableERC20Factory = "0x6900000000000000000000000000000000000004"
DevAddressManager = "0x6900000000000000000000000000000000000005"
DevProxyAdmin = "0x6900000000000000000000000000000000000006"
DevWETH9 = "0x6900000000000000000000000000000000000007"
DevL1ERC721Bridge = "0x6900000000000000000000000000000000000008"
DevSystemConfig = "0x6900000000000000000000000000000000000009"
)
var (
DevL2OutputOracleAddr = common.HexToAddress(DevL2OutputOracle)
DevOptimismPortalAddr = common.HexToAddress(DevOptimismPortal)
DevL1CrossDomainMessengerAddr = common.HexToAddress(DevL1CrossDomainMessenger)
DevL1StandardBridgeAddr = common.HexToAddress(DevL1StandardBridge)
DevOptimismMintableERC20FactoryAddr = common.HexToAddress(DevOptimismMintableERC20Factory)
DevAddressManagerAddr = common.HexToAddress(DevAddressManager)
DevProxyAdminAddr = common.HexToAddress(DevProxyAdmin)
DevWETH9Addr = common.HexToAddress(DevWETH9)
DevL1ERC721BridgeAddr = common.HexToAddress(DevL1ERC721Bridge)
DevSystemConfigAddr = common.HexToAddress(DevSystemConfig)
DevPredeploys = make(map[string]*common.Address)
)
func init() {
DevPredeploys["L2OutputOracle"] = &DevL2OutputOracleAddr
DevPredeploys["OptimismPortal"] = &DevOptimismPortalAddr
DevPredeploys["L1CrossDomainMessenger"] = &DevL1CrossDomainMessengerAddr
DevPredeploys["L1StandardBridge"] = &DevL1StandardBridgeAddr
DevPredeploys["OptimismMintableERC20Factory"] = &DevOptimismMintableERC20FactoryAddr
DevPredeploys["AddressManager"] = &DevAddressManagerAddr
DevPredeploys["Admin"] = &DevProxyAdminAddr
DevPredeploys["WETH9"] = &DevWETH9Addr
DevPredeploys["L1ERC721Bridge"] = &DevL1ERC721BridgeAddr
DevPredeploys["SystemConfig"] = &DevSystemConfigAddr
}
...@@ -8,8 +8,11 @@ import ( ...@@ -8,8 +8,11 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"reflect"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
gstate "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
...@@ -74,10 +77,15 @@ type DeployConfig struct { ...@@ -74,10 +77,15 @@ type DeployConfig struct {
// L2OutputOracleChallenger is the address of the account that challenges L2 outputs. // L2OutputOracleChallenger is the address of the account that challenges L2 outputs.
L2OutputOracleChallenger common.Address `json:"l2OutputOracleChallenger"` L2OutputOracleChallenger common.Address `json:"l2OutputOracleChallenger"`
// CliqueSignerAddress represents the signer address for the clique consensus engine.
// It is used in the multi-process devnet to sign blocks.
CliqueSignerAddress common.Address `json:"cliqueSignerAddress"`
// L1UseClique represents whether or not to use the clique consensus engine.
L1UseClique bool `json:"l1UseClique"`
L1BlockTime uint64 `json:"l1BlockTime"` L1BlockTime uint64 `json:"l1BlockTime"`
L1GenesisBlockTimestamp hexutil.Uint64 `json:"l1GenesisBlockTimestamp"` L1GenesisBlockTimestamp hexutil.Uint64 `json:"l1GenesisBlockTimestamp"`
L1GenesisBlockNonce hexutil.Uint64 `json:"l1GenesisBlockNonce"` L1GenesisBlockNonce hexutil.Uint64 `json:"l1GenesisBlockNonce"`
CliqueSignerAddress common.Address `json:"cliqueSignerAddress"` // proof of stake genesis if left zeroed.
L1GenesisBlockGasLimit hexutil.Uint64 `json:"l1GenesisBlockGasLimit"` L1GenesisBlockGasLimit hexutil.Uint64 `json:"l1GenesisBlockGasLimit"`
L1GenesisBlockDifficulty *hexutil.Big `json:"l1GenesisBlockDifficulty"` L1GenesisBlockDifficulty *hexutil.Big `json:"l1GenesisBlockDifficulty"`
L1GenesisBlockMixHash common.Hash `json:"l1GenesisBlockMixHash"` L1GenesisBlockMixHash common.Hash `json:"l1GenesisBlockMixHash"`
...@@ -181,7 +189,6 @@ func (d *DeployConfig) Copy() *DeployConfig { ...@@ -181,7 +189,6 @@ func (d *DeployConfig) Copy() *DeployConfig {
if err = json.Unmarshal(raw, &cpy); err != nil { if err = json.Unmarshal(raw, &cpy); err != nil {
panic(err) panic(err)
} }
return &cpy return &cpy
} }
...@@ -311,9 +318,22 @@ func (d *DeployConfig) Check() error { ...@@ -311,9 +318,22 @@ func (d *DeployConfig) Check() error {
return fmt.Errorf("%w: GovernanceToken owner cannot be address(0)", ErrInvalidDeployConfig) return fmt.Errorf("%w: GovernanceToken owner cannot be address(0)", ErrInvalidDeployConfig)
} }
} }
// L2 block time must always be smaller than L1 block time
if d.L1BlockTime < d.L2BlockTime {
return fmt.Errorf("L2 block time (%d) is larger than L1 block time (%d)", d.L2BlockTime, d.L1BlockTime)
}
return nil return nil
} }
// SetDeployments will merge a Deployments into a DeployConfig.
func (d *DeployConfig) SetDeployments(deployments *L1Deployments) {
d.L1StandardBridgeProxy = deployments.L1StandardBridgeProxy
d.L1CrossDomainMessengerProxy = deployments.L1CrossDomainMessengerProxy
d.L1ERC721BridgeProxy = deployments.L1ERC721BridgeProxy
d.SystemConfigProxy = deployments.SystemConfigProxy
d.OptimismPortalProxy = deployments.OptimismPortalProxy
}
// GetDeployedAddresses will get the deployed addresses of deployed L1 contracts // GetDeployedAddresses will get the deployed addresses of deployed L1 contracts
// required for the L2 genesis creation. Legacy systems use the `Proxy__` prefix // required for the L2 genesis creation. Legacy systems use the `Proxy__` prefix
// while modern systems use the `Proxy` suffix. First check for the legacy // while modern systems use the `Proxy` suffix. First check for the legacy
...@@ -373,16 +393,6 @@ func (d *DeployConfig) GetDeployedAddresses(hh *hardhat.Hardhat) error { ...@@ -373,16 +393,6 @@ func (d *DeployConfig) GetDeployedAddresses(hh *hardhat.Hardhat) error {
return nil return nil
} }
// InitDeveloperDeployedAddresses will set the dev addresses on the DeployConfig
func (d *DeployConfig) InitDeveloperDeployedAddresses() error {
d.L1StandardBridgeProxy = predeploys.DevL1StandardBridgeAddr
d.L1CrossDomainMessengerProxy = predeploys.DevL1CrossDomainMessengerAddr
d.L1ERC721BridgeProxy = predeploys.DevL1ERC721BridgeAddr
d.OptimismPortalProxy = predeploys.DevOptimismPortalAddr
d.SystemConfigProxy = predeploys.DevSystemConfigAddr
return nil
}
func (d *DeployConfig) RegolithTime(genesisTime uint64) *uint64 { func (d *DeployConfig) RegolithTime(genesisTime uint64) *uint64 {
if d.L2GenesisRegolithTimeOffset == nil { if d.L2GenesisRegolithTimeOffset == nil {
return nil return nil
...@@ -479,6 +489,64 @@ type L1Deployments struct { ...@@ -479,6 +489,64 @@ type L1Deployments struct {
SystemConfigProxy common.Address `json:"SystemConfigProxy"` SystemConfigProxy common.Address `json:"SystemConfigProxy"`
} }
// GetName will return the name of the contract given an address.
func (d *L1Deployments) GetName(addr common.Address) string {
val := reflect.ValueOf(d)
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
for i := 0; i < val.NumField(); i++ {
if addr == val.Field(i).Interface().(common.Address) {
return val.Type().Field(i).Name
}
}
return ""
}
// Check will ensure that the L1Deployments are sane
func (d *L1Deployments) Check() error {
val := reflect.ValueOf(d)
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
for i := 0; i < val.NumField(); i++ {
name := val.Type().Field(i).Name
// Skip the non production ready contracts
if name == "DisputeGameFactory" || name == "DisputeGameFactoryProxy" {
continue
}
if val.Field(i).Interface().(common.Address) == (common.Address{}) {
return fmt.Errorf("%s is not set", name)
}
}
return nil
}
// ForEach will iterate over each contract in the L1Deployments
func (d *L1Deployments) ForEach(cb func(name string, addr common.Address)) {
val := reflect.ValueOf(d)
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
for i := 0; i < val.NumField(); i++ {
name := val.Type().Field(i).Name
cb(name, val.Field(i).Interface().(common.Address))
}
}
// Copy will copy the L1Deployments struct
func (d *L1Deployments) Copy() *L1Deployments {
cpy := L1Deployments{}
data, err := json.Marshal(d)
if err != nil {
panic(err)
}
if err := json.Unmarshal(data, &cpy); err != nil {
panic(err)
}
return &cpy
}
// NewL1Deployments will create a new L1Deployments from a JSON file on disk // NewL1Deployments will create a new L1Deployments from a JSON file on disk
// at the given path. // at the given path.
func NewL1Deployments(path string) (*L1Deployments, error) { func NewL1Deployments(path string) (*L1Deployments, error) {
...@@ -495,6 +563,20 @@ func NewL1Deployments(path string) (*L1Deployments, error) { ...@@ -495,6 +563,20 @@ func NewL1Deployments(path string) (*L1Deployments, error) {
return &deployments, nil return &deployments, nil
} }
// NewStateDump will read a Dump JSON file from disk
func NewStateDump(path string) (*gstate.Dump, error) {
file, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("dump at %s not found: %w", path, err)
}
var dump gstate.Dump
if err := json.Unmarshal(file, &dump); err != nil {
return nil, fmt.Errorf("cannot unmarshal dump: %w", err)
}
return &dump, nil
}
// NewL2ImmutableConfig will create an ImmutableConfig given an instance of a // NewL2ImmutableConfig will create an ImmutableConfig given an instance of a
// DeployConfig and a block. // DeployConfig and a block.
func NewL2ImmutableConfig(config *DeployConfig, block *types.Block) (immutables.ImmutableConfig, error) { func NewL2ImmutableConfig(config *DeployConfig, block *types.Block) (immutables.ImmutableConfig, error) {
......
...@@ -89,4 +89,9 @@ func TestL1Deployments(t *testing.T) { ...@@ -89,4 +89,9 @@ func TestL1Deployments(t *testing.T) {
require.NotEqual(t, deployments.ProxyAdmin, common.Address{}) require.NotEqual(t, deployments.ProxyAdmin, common.Address{})
require.NotEqual(t, deployments.SystemConfig, common.Address{}) require.NotEqual(t, deployments.SystemConfig, common.Address{})
require.NotEqual(t, deployments.SystemConfigProxy, common.Address{}) require.NotEqual(t, deployments.SystemConfigProxy, common.Address{})
require.Equal(t, "AddressManager", deployments.GetName(deployments.AddressManager))
require.Equal(t, "OptimismPortalProxy", deployments.GetName(deployments.OptimismPortalProxy))
// One that doesn't exist returns empty string
require.Equal(t, "", deployments.GetName(common.Address{19: 0xff}))
} }
...@@ -127,20 +127,22 @@ func NewL1Genesis(config *DeployConfig) (*core.Genesis, error) { ...@@ -127,20 +127,22 @@ func NewL1Genesis(config *DeployConfig) (*core.Genesis, error) {
LondonBlock: big.NewInt(0), LondonBlock: big.NewInt(0),
ArrowGlacierBlock: big.NewInt(0), ArrowGlacierBlock: big.NewInt(0),
GrayGlacierBlock: big.NewInt(0), GrayGlacierBlock: big.NewInt(0),
ShanghaiTime: u64ptr(0),
} }
if config.CliqueSignerAddress != (common.Address{}) { extraData := make([]byte, 0)
if config.L1UseClique {
// warning: clique has an overly strict block header timestamp check against the system wallclock, // warning: clique has an overly strict block header timestamp check against the system wallclock,
// causing blocks to get scheduled as "future block" and not get mined instantly when produced. // causing blocks to get scheduled as "future block" and not get mined instantly when produced.
chainConfig.Clique = &params.CliqueConfig{ chainConfig.Clique = &params.CliqueConfig{
Period: config.L1BlockTime, Period: config.L1BlockTime,
Epoch: 30000, Epoch: 30000,
} }
extraData = append(append(make([]byte, 32), config.CliqueSignerAddress[:]...), make([]byte, crypto.SignatureLength)...)
} else { } else {
chainConfig.MergeNetsplitBlock = big.NewInt(0) chainConfig.MergeNetsplitBlock = big.NewInt(0)
chainConfig.TerminalTotalDifficulty = big.NewInt(0) chainConfig.TerminalTotalDifficulty = big.NewInt(0)
chainConfig.TerminalTotalDifficultyPassed = true chainConfig.TerminalTotalDifficultyPassed = true
chainConfig.ShanghaiTime = u64ptr(0)
} }
gasLimit := config.L1GenesisBlockGasLimit gasLimit := config.L1GenesisBlockGasLimit
...@@ -160,11 +162,6 @@ func NewL1Genesis(config *DeployConfig) (*core.Genesis, error) { ...@@ -160,11 +162,6 @@ func NewL1Genesis(config *DeployConfig) (*core.Genesis, error) {
timestamp = hexutil.Uint64(time.Now().Unix()) timestamp = hexutil.Uint64(time.Now().Unix())
} }
extraData := make([]byte, 0)
if config.CliqueSignerAddress != (common.Address{}) {
extraData = append(append(make([]byte, 32), config.CliqueSignerAddress[:]...), make([]byte, crypto.SignatureLength)...)
}
return &core.Genesis{ return &core.Genesis{
Config: &chainConfig, Config: &chainConfig,
Nonce: uint64(config.L1GenesisBlockNonce), Nonce: uint64(config.L1GenesisBlockNonce),
......
...@@ -18,12 +18,8 @@ var ( ...@@ -18,12 +18,8 @@ var (
codeNamespace = common.HexToAddress("0xc0D3C0d3C0d3C0D3c0d3C0d3c0D3C0d3c0d30000") codeNamespace = common.HexToAddress("0xc0D3C0d3C0d3C0D3c0d3C0d3c0D3C0d3c0d30000")
// l2PredeployNamespace represents the namespace of L2 predeploys // l2PredeployNamespace represents the namespace of L2 predeploys
l2PredeployNamespace = common.HexToAddress("0x4200000000000000000000000000000000000000") l2PredeployNamespace = common.HexToAddress("0x4200000000000000000000000000000000000000")
// l1PredeployNamespace represents the namespace of L1 predeploys
l1PredeployNamespace = common.HexToAddress("0x6900000000000000000000000000000000000000")
// bigL2PredeployNamespace represents the predeploy namespace as a big.Int // bigL2PredeployNamespace represents the predeploy namespace as a big.Int
BigL2PredeployNamespace = new(big.Int).SetBytes(l2PredeployNamespace.Bytes()) BigL2PredeployNamespace = new(big.Int).SetBytes(l2PredeployNamespace.Bytes())
// bigL1PredeployNamespace represents the predeploy namespace as a big.Int
bigL1PredeployNamespace = new(big.Int).SetBytes(l1PredeployNamespace.Bytes())
// bigCodeNamespace represents the predeploy namespace as a big.Int // bigCodeNamespace represents the predeploy namespace as a big.Int
bigCodeNameSpace = new(big.Int).SetBytes(codeNamespace.Bytes()) bigCodeNameSpace = new(big.Int).SetBytes(codeNamespace.Bytes())
// implementationSlot represents the EIP 1967 implementation storage slot // implementationSlot represents the EIP 1967 implementation storage slot
...@@ -67,7 +63,7 @@ var devBalance = hexutil.MustDecodeBig("0x20000000000000000000000000000000000000 ...@@ -67,7 +63,7 @@ var devBalance = hexutil.MustDecodeBig("0x20000000000000000000000000000000000000
// AddressToCodeNamespace takes a predeploy address and computes // AddressToCodeNamespace takes a predeploy address and computes
// the implementation address that the implementation should be deployed at // the implementation address that the implementation should be deployed at
func AddressToCodeNamespace(addr common.Address) (common.Address, error) { func AddressToCodeNamespace(addr common.Address) (common.Address, error) {
if !IsL1DevPredeploy(addr) && !IsL2DevPredeploy(addr) { if !IsL2DevPredeploy(addr) {
return common.Address{}, fmt.Errorf("cannot handle non predeploy: %s", addr) return common.Address{}, fmt.Errorf("cannot handle non predeploy: %s", addr)
} }
bigAddress := new(big.Int).SetBytes(addr[18:]) bigAddress := new(big.Int).SetBytes(addr[18:])
...@@ -75,10 +71,6 @@ func AddressToCodeNamespace(addr common.Address) (common.Address, error) { ...@@ -75,10 +71,6 @@ func AddressToCodeNamespace(addr common.Address) (common.Address, error) {
return common.BigToAddress(num), nil return common.BigToAddress(num), nil
} }
func IsL1DevPredeploy(addr common.Address) bool {
return bytes.Equal(addr[0:2], []byte{0x69, 0x00})
}
func IsL2DevPredeploy(addr common.Address) bool { func IsL2DevPredeploy(addr common.Address) bool {
return bytes.Equal(addr[0:2], []byte{0x42, 0x00}) return bytes.Equal(addr[0:2], []byte{0x42, 0x00})
} }
......
package genesis package genesis
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
"math/big" "math/big"
"strings"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/rlp" gstate "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/trie"
"github.com/ethereum-optimism/optimism/op-bindings/bindings" "github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer"
"github.com/ethereum-optimism/optimism/op-chain-ops/state" "github.com/ethereum-optimism/optimism/op-chain-ops/state"
) )
var ( var (
// proxies represents the set of proxies in front of contracts.
proxies = []string{
"SystemConfigProxy",
"L2OutputOracleProxy",
"L1CrossDomainMessengerProxy",
"L1StandardBridgeProxy",
"OptimismPortalProxy",
"OptimismMintableERC20FactoryProxy",
}
// portalMeteringSlot is the storage slot containing the metering params.
portalMeteringSlot = common.Hash{31: 0x01}
// zeroHash represents the zero value for a hash.
zeroHash = common.Hash{}
// uint128Max is type(uint128).max and is set in the init function. // uint128Max is type(uint128).max and is set in the init function.
uint128Max = new(big.Int) uint128Max = new(big.Int)
// The default values for the ResourceConfig, used as part of // The default values for the ResourceConfig, used as part of
...@@ -62,428 +42,81 @@ func init() { ...@@ -62,428 +42,81 @@ func init() {
// BuildL1DeveloperGenesis will create a L1 genesis block after creating // BuildL1DeveloperGenesis will create a L1 genesis block after creating
// all of the state required for an Optimism network to function. // all of the state required for an Optimism network to function.
func BuildL1DeveloperGenesis(config *DeployConfig) (*core.Genesis, error) { // It is expected that the dump contains all of the required state to bootstrap
if config.L2OutputOracleStartingTimestamp != -1 { // the L1 chain.
return nil, errors.New("l2oo starting timestamp must be -1") func BuildL1DeveloperGenesis(config *DeployConfig, dump *gstate.Dump, l1Deployments *L1Deployments, postProcess bool) (*core.Genesis, error) {
} log.Info("Building developer L1 genesis block")
if config.L1GenesisBlockTimestamp == 0 {
return nil, errors.New("must specify l1 genesis block timestamp")
}
genesis, err := NewL1Genesis(config) genesis, err := NewL1Genesis(config)
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("cannot create L1 developer genesis: %w", err)
}
// Enable shanghai
backend := deployer.NewBackendWithGenesisTimestamp(uint64(config.L1GenesisBlockTimestamp), true)
deployments, err := deployL1Contracts(config, backend)
if err != nil {
return nil, err
}
depsByName := make(map[string]deployer.Deployment)
depsByAddr := make(map[common.Address]deployer.Deployment)
for _, dep := range deployments {
depsByName[dep.Name] = dep
depsByAddr[dep.Address] = dep
}
opts, err := bind.NewKeyedTransactorWithChainID(deployer.TestKey, deployer.ChainID)
if err != nil {
return nil, err
}
portalABI, err := bindings.OptimismPortalMetaData.GetAbi()
if err != nil {
return nil, err
}
// Initialize the OptimismPortal without being paused
data, err := portalABI.Pack("initialize", false)
if err != nil {
return nil, fmt.Errorf("cannot abi encode initialize for OptimismPortal: %w", err)
}
if _, err := upgradeProxy(
backend,
opts,
depsByName["OptimismPortalProxy"].Address,
depsByName["OptimismPortal"].Address,
data,
); err != nil {
return nil, fmt.Errorf("cannot upgrade OptimismPortalProxy: %w", err)
}
sysCfgABI, err := bindings.SystemConfigMetaData.GetAbi()
if err != nil {
return nil, err
}
gasLimit := uint64(config.L2GenesisBlockGasLimit)
if gasLimit == 0 {
gasLimit = defaultGasLimit
}
data, err = sysCfgABI.Pack(
"initialize",
config.FinalSystemOwner,
uint642Big(config.GasPriceOracleOverhead),
uint642Big(config.GasPriceOracleScalar),
config.BatchSenderAddress.Hash(),
gasLimit,
config.P2PSequencerAddress,
defaultResourceConfig,
)
if err != nil {
return nil, fmt.Errorf("cannot abi encode initialize for SystemConfig: %w", err)
}
if _, err := upgradeProxy(
backend,
opts,
depsByName["SystemConfigProxy"].Address,
depsByName["SystemConfig"].Address,
data,
); err != nil {
return nil, fmt.Errorf("cannot upgrade SystemConfigProxy: %w", err)
}
l2ooABI, err := bindings.L2OutputOracleMetaData.GetAbi()
if err != nil {
return nil, err
}
data, err = l2ooABI.Pack(
"initialize",
big.NewInt(0),
uint642Big(uint64(config.L1GenesisBlockTimestamp)),
)
if err != nil {
return nil, fmt.Errorf("cannot abi encode initialize for L2OutputOracle: %w", err)
}
if _, err := upgradeProxy(
backend,
opts,
depsByName["L2OutputOracleProxy"].Address,
depsByName["L2OutputOracle"].Address,
data,
); err != nil {
return nil, err
}
l1XDMABI, err := bindings.L1CrossDomainMessengerMetaData.GetAbi()
if err != nil {
return nil, err
}
data, err = l1XDMABI.Pack("initialize")
if err != nil {
return nil, fmt.Errorf("cannot abi encode initialize for L1CrossDomainMessenger: %w", err)
}
if _, err := upgradeProxy(
backend,
opts,
depsByName["L1CrossDomainMessengerProxy"].Address,
depsByName["L1CrossDomainMessenger"].Address,
data,
); err != nil {
return nil, err
}
if _, err := upgradeProxy(
backend,
opts,
depsByName["L1StandardBridgeProxy"].Address,
depsByName["L1StandardBridge"].Address,
nil,
); err != nil {
return nil, err
}
var lastUpgradeTx *types.Transaction
if lastUpgradeTx, err = upgradeProxy(
backend,
opts,
depsByName["OptimismMintableERC20FactoryProxy"].Address,
depsByName["OptimismMintableERC20Factory"].Address,
nil,
); err != nil {
return nil, err
}
// Commit all the upgrades at once, then wait for the last
// transaction to be mined. The simulator performs async
// processing, and as such we need to wait for the transaction
// receipt to appear before considering the above transactions
// committed to the chain.
backend.Commit()
if _, err := bind.WaitMined(context.Background(), backend, lastUpgradeTx); err != nil {
return nil, err
} }
memDB := state.NewMemoryStateDB(genesis) memDB := state.NewMemoryStateDB(genesis)
if err := SetL1Proxies(memDB, predeploys.DevProxyAdminAddr); err != nil {
return nil, err
}
FundDevAccounts(memDB) FundDevAccounts(memDB)
SetPrecompileBalances(memDB) SetPrecompileBalances(memDB)
for name, proxyAddr := range predeploys.DevPredeploys { if dump != nil {
memDB.SetState(*proxyAddr, ImplementationSlot, depsByName[name].Address.Hash()) for address, account := range dump.Accounts {
name := "<unknown>"
// Special case for WETH since it was not designed to be behind a proxy if l1Deployments != nil {
if name == "WETH9" { if n := l1Deployments.GetName(address); n != "" {
name, _ := state.EncodeStringValue("Wrapped Ether", 0) name = n
symbol, _ := state.EncodeStringValue("WETH", 0)
decimals, _ := state.EncodeUintValue(18, 0)
memDB.SetState(*proxyAddr, common.Hash{}, name)
memDB.SetState(*proxyAddr, common.Hash{31: 0x01}, symbol)
memDB.SetState(*proxyAddr, common.Hash{31: 0x02}, decimals)
}
} }
if config.FundDevAccounts {
FundDevAccounts(memDB)
SetPrecompileBalances(memDB)
} }
log.Info("Setting account", "name", name, "address", address.Hex())
memDB.CreateAccount(address)
memDB.SetNonce(address, account.Nonce)
stateDB, err := backend.Blockchain().State() balance, ok := new(big.Int).SetString(account.Balance, 10)
if err != nil { if !ok {
return nil, err return nil, fmt.Errorf("failed to parse balance for %s", address)
} }
memDB.AddBalance(address, balance)
for _, dep := range deployments { memDB.SetCode(address, account.Code)
st, err := stateDB.StorageTrie(dep.Address) for key, value := range account.Storage {
if err != nil { memDB.SetState(address, key, common.HexToHash(value))
return nil, fmt.Errorf("failed to open storage trie of %s: %w", dep.Address, err)
} }
if st == nil {
return nil, fmt.Errorf("missing account %s in state, address: %s", dep.Name, dep.Address)
} }
iter := trie.NewIterator(st.NodeIterator(nil))
depAddr := dep.Address // This should only be used if we are expecting Optimism specific state to be set
if strings.HasSuffix(dep.Name, "Proxy") { if postProcess {
depAddr = *predeploys.DevPredeploys[strings.TrimSuffix(dep.Name, "Proxy")] if err := PostProcessL1DeveloperGenesis(memDB, l1Deployments); err != nil {
return nil, fmt.Errorf("failed to post process L1 developer genesis: %w", err)
} }
memDB.CreateAccount(depAddr)
memDB.SetCode(depAddr, dep.Bytecode)
for iter.Next() {
_, data, _, err := rlp.Split(iter.Value)
if err != nil {
return nil, err
} }
key := common.BytesToHash(st.GetKey(iter.Key))
value := common.BytesToHash(data)
if depAddr == predeploys.DevOptimismPortalAddr && key == portalMeteringSlot {
// We need to manually set the block number in the resource
// metering storage slot to zero. Otherwise, deposits will
// revert.
copy(value[:24], zeroHash[:])
} }
memDB.SetState(depAddr, key, value)
}
}
return memDB.Genesis(), nil return memDB.Genesis(), nil
} }
func deployL1Contracts(config *DeployConfig, backend *backends.SimulatedBackend) ([]deployer.Deployment, error) { // PostProcessL1DeveloperGenesis will apply post processing to the L1 genesis
constructors := make([]deployer.Constructor, 0) // state. This is required to handle edge cases in the genesis generation.
for _, proxy := range proxies { // `block.number` is used during deployment and without specifically setting
constructors = append(constructors, deployer.Constructor{ // the value to 0, it will cause underflow reverts for deposits in testing.
Name: proxy, func PostProcessL1DeveloperGenesis(stateDB *state.MemoryStateDB, deployments *L1Deployments) error {
}) if stateDB == nil {
return errors.New("cannot post process nil stateDB")
} }
gasLimit := uint64(config.L2GenesisBlockGasLimit) if deployments == nil {
if gasLimit == 0 { return errors.New("cannot post process dump with nil deployments")
gasLimit = defaultGasLimit
} }
constructors = append(constructors, []deployer.Constructor{ if !stateDB.Exist(deployments.OptimismPortalProxy) {
{ return fmt.Errorf("portal proxy doesn't exist at %s", deployments.OptimismPortalProxy)
Name: "SystemConfig",
Args: []interface{}{
config.FinalSystemOwner,
uint642Big(config.GasPriceOracleOverhead),
uint642Big(config.GasPriceOracleScalar),
config.BatchSenderAddress.Hash(), // left-padded 32 bytes value, version is zero anyway
gasLimit,
config.P2PSequencerAddress,
defaultResourceConfig,
},
},
{
Name: "L2OutputOracle",
Args: []interface{}{
uint642Big(config.L2OutputOracleSubmissionInterval),
uint642Big(config.L2BlockTime),
big.NewInt(0),
uint642Big(uint64(config.L1GenesisBlockTimestamp)),
config.L2OutputOracleProposer,
config.L2OutputOracleChallenger,
uint642Big(config.FinalizationPeriodSeconds),
},
},
{
// 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",
Args: []interface{}{
predeploys.DevL2OutputOracleAddr,
config.PortalGuardian,
true, // _paused
predeploys.DevSystemConfigAddr,
},
},
{
Name: "L1CrossDomainMessenger",
},
{
Name: "L1StandardBridge",
},
{
Name: "L1ERC721Bridge",
},
{
Name: "OptimismMintableERC20Factory",
},
{
Name: "AddressManager",
},
{
Name: "ProxyAdmin",
Args: []interface{}{
common.Address{19: 0x01},
},
},
{
Name: "WETH9",
},
}...)
return deployer.Deploy(backend, constructors, l1Deployer)
}
func l1Deployer(backend *backends.SimulatedBackend, opts *bind.TransactOpts, deployment deployer.Constructor) (*types.Transaction, error) {
var tx *types.Transaction
var err error
switch deployment.Name {
case "SystemConfig":
_, tx, _, err = bindings.DeploySystemConfig(
opts,
backend,
deployment.Args[0].(common.Address),
deployment.Args[1].(*big.Int),
deployment.Args[2].(*big.Int),
deployment.Args[3].(common.Hash),
deployment.Args[4].(uint64),
deployment.Args[5].(common.Address),
deployment.Args[6].(bindings.ResourceMeteringResourceConfig),
)
case "L2OutputOracle":
_, tx, _, err = bindings.DeployL2OutputOracle(
opts,
backend,
deployment.Args[0].(*big.Int),
deployment.Args[1].(*big.Int),
deployment.Args[2].(*big.Int),
deployment.Args[3].(*big.Int),
deployment.Args[4].(common.Address),
deployment.Args[5].(common.Address),
deployment.Args[6].(*big.Int),
)
case "OptimismPortal":
_, tx, _, err = bindings.DeployOptimismPortal(
opts,
backend,
deployment.Args[0].(common.Address),
deployment.Args[1].(common.Address),
deployment.Args[2].(bool),
deployment.Args[3].(common.Address),
)
case "L1CrossDomainMessenger":
_, tx, _, err = bindings.DeployL1CrossDomainMessenger(
opts,
backend,
predeploys.DevOptimismPortalAddr,
)
case "L1StandardBridge":
_, tx, _, err = bindings.DeployL1StandardBridge(
opts,
backend,
predeploys.DevL1CrossDomainMessengerAddr,
)
case "OptimismMintableERC20Factory":
_, tx, _, err = bindings.DeployOptimismMintableERC20Factory(
opts,
backend,
predeploys.DevL1StandardBridgeAddr,
)
case "AddressManager":
_, tx, _, err = bindings.DeployAddressManager(
opts,
backend,
)
case "ProxyAdmin":
_, tx, _, err = bindings.DeployProxyAdmin(
opts,
backend,
common.Address{},
)
case "WETH9":
_, tx, _, err = bindings.DeployWETH9(
opts,
backend,
)
case "L1ERC721Bridge":
_, tx, _, err = bindings.DeployL1ERC721Bridge(
opts,
backend,
predeploys.DevL1CrossDomainMessengerAddr,
predeploys.L2ERC721BridgeAddr,
)
default:
if strings.HasSuffix(deployment.Name, "Proxy") {
_, tx, _, err = bindings.DeployProxy(opts, backend, deployer.TestAddress)
} else {
err = fmt.Errorf("unknown contract %s", deployment.Name)
}
} }
layout, err := bindings.GetStorageLayout("OptimismPortal")
if err != nil { if err != nil {
err = fmt.Errorf("cannot deploy %s: %w", deployment.Name, err) return errors.New("failed to get storage layout for OptimismPortal")
} }
return tx, err entry, err := layout.GetStorageLayoutEntry("params")
}
func upgradeProxy(backend *backends.SimulatedBackend, opts *bind.TransactOpts, proxyAddr common.Address, implAddr common.Address, callData []byte) (*types.Transaction, error) {
var tx *types.Transaction
code, err := backend.CodeAt(context.Background(), implAddr, nil)
if err != nil { if err != nil {
return nil, err return errors.New("failed to get storage layout entry for OptimismPortal.params")
}
if len(code) == 0 {
return nil, fmt.Errorf("no code at %s", implAddr)
} }
slot := common.BigToHash(big.NewInt(int64(entry.Slot)))
proxy, err := bindings.NewProxy(proxyAddr, backend) stateDB.SetState(deployments.OptimismPortalProxy, slot, common.Hash{})
if err != nil {
return nil, err return nil
}
if callData == nil {
tx, err = proxy.UpgradeTo(opts, implAddr)
} else {
tx, err = proxy.UpgradeToAndCall(
opts,
implAddr,
callData,
)
}
return tx, err
} }
...@@ -16,11 +16,16 @@ import ( ...@@ -16,11 +16,16 @@ import (
"github.com/ethereum-optimism/optimism/op-chain-ops/deployer" "github.com/ethereum-optimism/optimism/op-chain-ops/deployer"
"github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends" "github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
// TestBuildL1DeveloperGenesis tests that the L1 genesis block can be built
// given a deploy config and an l1-allocs.json and a deploy.json that
// are generated from a deploy config. If new contracts are added, these
// mocks will need to be regenerated.
func TestBuildL1DeveloperGenesis(t *testing.T) { func TestBuildL1DeveloperGenesis(t *testing.T) {
b, err := os.ReadFile("testdata/test-deploy-config-full.json") b, err := os.ReadFile("testdata/test-deploy-config-full.json")
require.NoError(t, err) require.NoError(t, err)
...@@ -29,7 +34,15 @@ func TestBuildL1DeveloperGenesis(t *testing.T) { ...@@ -29,7 +34,15 @@ func TestBuildL1DeveloperGenesis(t *testing.T) {
require.NoError(t, dec.Decode(config)) require.NoError(t, dec.Decode(config))
config.L1GenesisBlockTimestamp = hexutil.Uint64(time.Now().Unix() - 100) config.L1GenesisBlockTimestamp = hexutil.Uint64(time.Now().Unix() - 100)
genesis, err := BuildL1DeveloperGenesis(config) c, err := os.ReadFile("testdata/allocs-l1.json")
require.NoError(t, err)
dump := new(state.Dump)
require.NoError(t, json.NewDecoder(bytes.NewReader(c)).Decode(dump))
deployments, err := NewL1Deployments("testdata/deploy.json")
require.NoError(t, err)
genesis, err := BuildL1DeveloperGenesis(config, dump, nil, false)
require.NoError(t, err) require.NoError(t, err)
sim := backends.NewSimulatedBackend( sim := backends.NewSimulatedBackend(
...@@ -38,9 +51,9 @@ func TestBuildL1DeveloperGenesis(t *testing.T) { ...@@ -38,9 +51,9 @@ func TestBuildL1DeveloperGenesis(t *testing.T) {
) )
callOpts := &bind.CallOpts{} callOpts := &bind.CallOpts{}
oracle, err := bindings.NewL2OutputOracle(predeploys.DevL2OutputOracleAddr, sim) oracle, err := bindings.NewL2OutputOracle(deployments.L2OutputOracleProxy, sim)
require.NoError(t, err) require.NoError(t, err)
portal, err := bindings.NewOptimismPortal(predeploys.DevOptimismPortalAddr, sim) portal, err := bindings.NewOptimismPortal(deployments.OptimismPortalProxy, sim)
require.NoError(t, err) require.NoError(t, err)
proposer, err := oracle.PROPOSER(callOpts) proposer, err := oracle.PROPOSER(callOpts)
...@@ -66,42 +79,30 @@ func TestBuildL1DeveloperGenesis(t *testing.T) { ...@@ -66,42 +79,30 @@ func TestBuildL1DeveloperGenesis(t *testing.T) {
oracleAddr, err := portal.L2ORACLE(callOpts) oracleAddr, err := portal.L2ORACLE(callOpts)
require.NoError(t, err) require.NoError(t, err)
require.EqualValues(t, predeploys.DevL2OutputOracleAddr, oracleAddr) require.EqualValues(t, deployments.L2OutputOracleProxy, oracleAddr)
msgr, err := bindings.NewL1CrossDomainMessenger(predeploys.DevL1CrossDomainMessengerAddr, sim) msgr, err := bindings.NewL1CrossDomainMessenger(deployments.L1CrossDomainMessengerProxy, sim)
require.NoError(t, err) require.NoError(t, err)
portalAddr, err := msgr.PORTAL(callOpts) portalAddr, err := msgr.PORTAL(callOpts)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, predeploys.DevOptimismPortalAddr, portalAddr) require.Equal(t, deployments.OptimismPortalProxy, portalAddr)
bridge, err := bindings.NewL1StandardBridge(predeploys.DevL1StandardBridgeAddr, sim) bridge, err := bindings.NewL1StandardBridge(deployments.L1StandardBridgeProxy, sim)
require.NoError(t, err) require.NoError(t, err)
msgrAddr, err := bridge.Messenger(callOpts) msgrAddr, err := bridge.Messenger(callOpts)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, predeploys.DevL1CrossDomainMessengerAddr, msgrAddr) require.Equal(t, deployments.L1CrossDomainMessengerProxy, msgrAddr)
otherBridge, err := bridge.OTHERBRIDGE(callOpts) otherBridge, err := bridge.OTHERBRIDGE(callOpts)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, predeploys.L2StandardBridgeAddr, otherBridge) require.Equal(t, predeploys.L2StandardBridgeAddr, otherBridge)
factory, err := bindings.NewOptimismMintableERC20(predeploys.DevOptimismMintableERC20FactoryAddr, sim) factory, err := bindings.NewOptimismMintableERC20(deployments.OptimismMintableERC20Factory, sim)
require.NoError(t, err) require.NoError(t, err)
bridgeAddr, err := factory.BRIDGE(callOpts) bridgeAddr, err := factory.BRIDGE(callOpts)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, predeploys.DevL1StandardBridgeAddr, bridgeAddr) require.Equal(t, deployments.L1StandardBridgeProxy, bridgeAddr)
weth9, err := bindings.NewWETH9(predeploys.DevWETH9Addr, sim)
require.NoError(t, err)
decimals, err := weth9.Decimals(callOpts)
require.NoError(t, err)
require.Equal(t, uint8(18), decimals)
symbol, err := weth9.Symbol(callOpts)
require.NoError(t, err)
require.Equal(t, "WETH", symbol)
name, err := weth9.Name(callOpts)
require.NoError(t, err)
require.Equal(t, "Wrapped Ether", name)
sysCfg, err := bindings.NewSystemConfig(predeploys.DevSystemConfigAddr, sim) sysCfg, err := bindings.NewSystemConfig(deployments.SystemConfigProxy, sim)
require.NoError(t, err) require.NoError(t, err)
cfg, err := sysCfg.ResourceConfig(&bind.CallOpts{}) cfg, err := sysCfg.ResourceConfig(&bind.CallOpts{})
require.NoError(t, err) require.NoError(t, err)
......
...@@ -21,6 +21,7 @@ func BuildL2Genesis(config *DeployConfig, l1StartBlock *types.Block) (*core.Gene ...@@ -21,6 +21,7 @@ func BuildL2Genesis(config *DeployConfig, l1StartBlock *types.Block) (*core.Gene
db := state.NewMemoryStateDB(genspec) db := state.NewMemoryStateDB(genspec)
if config.FundDevAccounts { if config.FundDevAccounts {
log.Info("Funding developer accounts in L2 genesis")
FundDevAccounts(db) FundDevAccounts(db)
SetPrecompileBalances(db) SetPrecompileBalances(db)
} }
......
...@@ -73,17 +73,6 @@ func testBuildL2Genesis(t *testing.T, config *genesis.DeployConfig) *core.Genesi ...@@ -73,17 +73,6 @@ func testBuildL2Genesis(t *testing.T, config *genesis.DeployConfig) *core.Genesi
return gen return gen
} }
func TestBuildL2DeveloperGenesis(t *testing.T) {
config, err := genesis.NewDeployConfig("./testdata/test-deploy-config-devnet-l1.json")
require.Nil(t, err)
config.EnableGovernance = false
config.FundDevAccounts = true
err = config.InitDeveloperDeployedAddresses()
require.NoError(t, err)
gen := testBuildL2Genesis(t, config)
require.Equal(t, 2344, len(gen.Alloc))
}
func TestBuildL2MainnetGenesis(t *testing.T) { func TestBuildL2MainnetGenesis(t *testing.T) {
config, err := genesis.NewDeployConfig("./testdata/test-deploy-config-devnet-l1.json") config, err := genesis.NewDeployConfig("./testdata/test-deploy-config-devnet-l1.json")
require.Nil(t, err) require.Nil(t, err)
......
...@@ -16,19 +16,13 @@ import ( ...@@ -16,19 +16,13 @@ import (
// FundDevAccounts will fund each of the development accounts. // FundDevAccounts will fund each of the development accounts.
func FundDevAccounts(db vm.StateDB) { func FundDevAccounts(db vm.StateDB) {
for _, account := range DevAccounts { for _, account := range DevAccounts {
if !db.Exist(account) {
db.CreateAccount(account) db.CreateAccount(account)
}
db.AddBalance(account, devBalance) db.AddBalance(account, devBalance)
} }
} }
// SetL1Proxies will set each of the proxies in the state. It requires
// a Proxy and ProxyAdmin deployment present so that the Proxy bytecode
// can be set in state and the ProxyAdmin can be set as the admin of the
// Proxy.
func SetL1Proxies(db vm.StateDB, proxyAdminAddr common.Address) error {
return setProxies(db, proxyAdminAddr, bigL1PredeployNamespace, 2048)
}
func setProxies(db vm.StateDB, proxyAdminAddr common.Address, namespace *big.Int, count uint64) error { func setProxies(db vm.StateDB, proxyAdminAddr common.Address, namespace *big.Int, count uint64) error {
depBytecode, err := bindings.GetDeployedBytecode("Proxy") depBytecode, err := bindings.GetDeployedBytecode("Proxy")
if err != nil { if err != nil {
......
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"AddressManager": "0x7aeb97910d4070426C00e820De4137D6d2ADf0AD",
"L1CrossDomainMessenger": "0x4562938D6831437F37Dd1BF5BBb7fe7a5F080eac",
"L1CrossDomainMessengerProxy": "0xa88B2060478401a52532814eddE6819d101802E2",
"L1ERC721Bridge": "0x7eC3b539C97c98D503Fb4FeE3dd4ee885DC192cd",
"L1ERC721BridgeProxy": "0xD16dbA9f860146F08C9EE83B891c138667021eC9",
"L1StandardBridge": "0x9a7EcB1c67f88396D252725F3259e9d8027F1562",
"L1StandardBridgeProxy": "0x6fB1869D7141C97Cf28668fA0A338BDC892F53c0",
"L2OutputOracle": "0x7F325Df611b236EE3EF469Da4a74f776c787d1B3",
"L2OutputOracleProxy": "0x48204903b06a64e3c44B0260F875497EA5316A02",
"OptimismMintableERC20Factory": "0x87658463F96977Fc95a068F1A206D2C0CF2db575",
"OptimismMintableERC20FactoryProxy": "0xA051F227dA1f5F1eEcbfCdcd7316cE299A233760",
"OptimismPortal": "0xb8D5D0Fa6E413b5de72C38a0a187731171b4F341",
"OptimismPortalProxy": "0xCF1a8a7F273DA1bd3112750Cc3691B46C541e8B7",
"ProxyAdmin": "0x36F4e85652236f9EdAeba78bBE9d8C2B55Ac5809",
"SystemConfig": "0xEc466e9A46914507c484DCC5cabA1Db787e34913",
"SystemConfigProxy": "0x73317009F4FadAfcDA357F3a082C7B68F5f8732F"
}
\ No newline at end of file
...@@ -6,20 +6,21 @@ ...@@ -6,20 +6,21 @@
"maxSequencerDrift": 20, "maxSequencerDrift": 20,
"sequencerWindowSize": 100, "sequencerWindowSize": 100,
"channelTimeout": 30, "channelTimeout": 30,
"p2pSequencerAddress": "0x0000000000000000000000000000000000000000", "l1UseClique": false,
"cliqueSignerAddress": "0x0000000000000000000000000000000000000000",
"p2pSequencerAddress": "0x9965507d1a55bcc2695c58ba16fb37d819b0a4dc",
"batchInboxAddress": "0x42000000000000000000000000000000000000ff", "batchInboxAddress": "0x42000000000000000000000000000000000000ff",
"batchSenderAddress": "0x0000000000000000000000000000000000000000", "batchSenderAddress": "0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc",
"l2OutputOracleSubmissionInterval": 6, "l2OutputOracleSubmissionInterval": 20,
"l2OutputOracleStartingBlockNumber": 0, "l2OutputOracleStartingBlockNumber": 0,
"l2OutputOracleStartingTimestamp": -1, "l2OutputOracleStartingTimestamp": -1,
"l2OutputOracleProposer": "0x7770000000000000000000000000000000000001", "l2OutputOracleProposer": "0x70997970c51812dc3a010c7d01b50e0d17dc79c8",
"l2OutputOracleChallenger": "0x7770000000000000000000000000000000000002", "l2OutputOracleChallenger": "0x15d34aaf54267db7d7c367839aaf71a00a2c6a65",
"l1BlockTime": 15, "l1BlockTime": 15,
"l1GenesisBlockNonce": "0x0", "l1GenesisBlockNonce": "0x0",
"cliqueSignerAddress": "0x0000000000000000000000000000000000000000",
"l1GenesisBlockGasLimit": "0x1c9c380", "l1GenesisBlockGasLimit": "0x1c9c380",
"l1GenesisBlockDifficulty": "0x1", "l1GenesisBlockDifficulty": "0x1",
"finalSystemOwner": "0x0000000000000000000000000000000000000111", "finalSystemOwner": "0xbcd4042de499d14e55001ccbb24a551f3b954096",
"portalGuardian": "0x0000000000000000000000000000000000000112", "portalGuardian": "0x0000000000000000000000000000000000000112",
"finalizationPeriodSeconds": 2, "finalizationPeriodSeconds": 2,
"l1GenesisBlockMixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", "l1GenesisBlockMixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
......
...@@ -6,6 +6,7 @@ import ( ...@@ -6,6 +6,7 @@ import (
"math/big" "math/big"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum-optimism/optimism/op-bindings/bindings" "github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys" "github.com/ethereum-optimism/optimism/op-bindings/predeploys"
...@@ -158,6 +159,7 @@ func BuildOptimism(immutable ImmutableConfig) (DeploymentResults, error) { ...@@ -158,6 +159,7 @@ func BuildOptimism(immutable ImmutableConfig) (DeploymentResults, error) {
// can be properly set. The bytecode returned in the results is suitable to be // can be properly set. The bytecode returned in the results is suitable to be
// inserted into the state via state surgery. // inserted into the state via state surgery.
func BuildL2(constructors []deployer.Constructor) (DeploymentResults, error) { func BuildL2(constructors []deployer.Constructor) (DeploymentResults, error) {
log.Info("Creating L2 state")
deployments, err := deployer.Deploy(deployer.NewL2Backend(), constructors, l2Deployer) deployments, err := deployer.Deploy(deployer.NewL2Backend(), constructors, l2Deployer)
if err != nil { if err != nil {
return nil, err return nil, err
......
# op-e2e
The end to end tests in this repo depend on genesis state that is
created with the `bedrock-devnet` package. To create this state,
run the following commands from the root of the repository:
```bash
make install-geth
make devnet-allocs
```
This will leave artifacts in the `.devnet` directory that will be
read into `op-e2e` at runtime. The default deploy configuration
used for starting all `op-e2e` based tests can be found in
`packages/contracts-bedrock/deploy-config/devnetL1.json`. There
are some values that are safe to change in memory in `op-e2e` at
runtime, but others cannot be changed or else it will result in
broken tests. Any changes to `devnetL1.json` should result in
rebuilding the `.devnet` artifacts before the new values will
be present in the `op-e2e` tests.
...@@ -21,6 +21,8 @@ func TestBatchInLastPossibleBlocks(gt *testing.T) { ...@@ -21,6 +21,8 @@ func TestBatchInLastPossibleBlocks(gt *testing.T) {
t := NewDefaultTesting(gt) t := NewDefaultTesting(gt)
dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams) dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams)
dp.DeployConfig.SequencerWindowSize = 4 dp.DeployConfig.SequencerWindowSize = 4
dp.DeployConfig.L2BlockTime = 2
sd := e2eutils.Setup(t, dp, defaultAlloc) sd := e2eutils.Setup(t, dp, defaultAlloc)
log := testlog.Logger(t, log.LvlDebug) log := testlog.Logger(t, log.LvlDebug)
......
...@@ -26,6 +26,7 @@ func TestBatcher(gt *testing.T) { ...@@ -26,6 +26,7 @@ func TestBatcher(gt *testing.T) {
MaxSequencerDrift: 20, // larger than L1 block time we simulate in this test (12) MaxSequencerDrift: 20, // larger than L1 block time we simulate in this test (12)
SequencerWindowSize: 24, SequencerWindowSize: 24,
ChannelTimeout: 20, ChannelTimeout: 20,
L1BlockTime: 12,
} }
dp := e2eutils.MakeDeployParams(t, p) dp := e2eutils.MakeDeployParams(t, p)
sd := e2eutils.Setup(t, dp, defaultAlloc) sd := e2eutils.Setup(t, dp, defaultAlloc)
...@@ -345,6 +346,7 @@ func TestExtendedTimeWithoutL1Batches(gt *testing.T) { ...@@ -345,6 +346,7 @@ func TestExtendedTimeWithoutL1Batches(gt *testing.T) {
MaxSequencerDrift: 20, // larger than L1 block time we simulate in this test (12) MaxSequencerDrift: 20, // larger than L1 block time we simulate in this test (12)
SequencerWindowSize: 24, SequencerWindowSize: 24,
ChannelTimeout: 20, ChannelTimeout: 20,
L1BlockTime: 12,
} }
dp := e2eutils.MakeDeployParams(t, p) dp := e2eutils.MakeDeployParams(t, p)
sd := e2eutils.Setup(t, dp, defaultAlloc) sd := e2eutils.Setup(t, dp, defaultAlloc)
...@@ -402,6 +404,7 @@ func TestBigL2Txs(gt *testing.T) { ...@@ -402,6 +404,7 @@ func TestBigL2Txs(gt *testing.T) {
MaxSequencerDrift: 100, MaxSequencerDrift: 100,
SequencerWindowSize: 1000, SequencerWindowSize: 1000,
ChannelTimeout: 200, // give enough space to buffer large amounts of data before submitting it ChannelTimeout: 200, // give enough space to buffer large amounts of data before submitting it
L1BlockTime: 12,
} }
dp := e2eutils.MakeDeployParams(t, p) dp := e2eutils.MakeDeployParams(t, p)
sd := e2eutils.Setup(t, dp, defaultAlloc) sd := e2eutils.Setup(t, dp, defaultAlloc)
......
...@@ -126,6 +126,7 @@ func (p *L2Proposer) sendTx(t Testing, data []byte) { ...@@ -126,6 +126,7 @@ func (p *L2Proposer) sendTx(t Testing, data []byte) {
require.NoError(t, err, "need to sign tx") require.NoError(t, err, "need to sign tx")
err = p.l1.SendTransaction(t.Ctx(), tx) err = p.l1.SendTransaction(t.Ctx(), tx)
log.Info("Proposer sent tx", "hash", tx.Hash(), "to", p.contractAddr)
require.NoError(t, err, "need to send tx") require.NoError(t, err, "need to send tx")
p.lastTx = tx.Hash() p.lastTx = tx.Hash()
......
package actions package actions
import ( import (
"math/big"
"testing" "testing"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
...@@ -55,9 +55,10 @@ func TestProposer(gt *testing.T) { ...@@ -55,9 +55,10 @@ func TestProposer(gt *testing.T) {
sequencer.ActL1SafeSignal(t) sequencer.ActL1SafeSignal(t)
sequencer.ActL1FinalizedSignal(t) sequencer.ActL1FinalizedSignal(t)
require.Equal(t, sequencer.SyncStatus().UnsafeL2, sequencer.SyncStatus().FinalizedL2) require.Equal(t, sequencer.SyncStatus().UnsafeL2, sequencer.SyncStatus().FinalizedL2)
require.True(t, proposer.CanPropose(t))
// make proposals until there is nothing left to propose // make proposals until there is nothing left to propose
for proposer.CanPropose(t) { for proposer.CanPropose(t) {
// and propose it to L1
proposer.ActMakeProposalTx(t) proposer.ActMakeProposalTx(t)
// include proposal on L1 // include proposal on L1
miner.ActL1StartBlock(12)(t) miner.ActL1StartBlock(12)(t)
...@@ -72,11 +73,15 @@ func TestProposer(gt *testing.T) { ...@@ -72,11 +73,15 @@ func TestProposer(gt *testing.T) {
// check that L1 stored the expected output root // check that L1 stored the expected output root
outputOracleContract, err := bindings.NewL2OutputOracle(sd.DeploymentsL1.L2OutputOracleProxy, miner.EthClient()) outputOracleContract, err := bindings.NewL2OutputOracle(sd.DeploymentsL1.L2OutputOracleProxy, miner.EthClient())
require.NoError(t, err) require.NoError(t, err)
block := sequencer.SyncStatus().FinalizedL2 blockNumber, err := outputOracleContract.LatestBlockNumber(&bind.CallOpts{})
outputOnL1, err := outputOracleContract.GetL2OutputAfter(nil, new(big.Int).SetUint64(block.Number)) require.NoError(t, err)
require.Greater(t, int64(blockNumber.Uint64()), int64(0), "latest block number must be greater than 0")
block, err := seqEngine.EthClient().BlockByNumber(t.Ctx(), blockNumber)
require.NoError(t, err)
outputOnL1, err := outputOracleContract.GetL2OutputAfter(&bind.CallOpts{}, blockNumber)
require.NoError(t, err) require.NoError(t, err)
require.Less(t, block.Time, outputOnL1.Timestamp.Uint64(), "output is registered with L1 timestamp of proposal tx, past L2 block") require.Less(t, block.Time(), outputOnL1.Timestamp.Uint64(), "output is registered with L1 timestamp of proposal tx, past L2 block")
outputComputed, err := sequencer.RollupClient().OutputAtBlock(t.Ctx(), block.Number) outputComputed, err := sequencer.RollupClient().OutputAtBlock(t.Ctx(), blockNumber.Uint64())
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, eth.Bytes32(outputOnL1.OutputRoot), outputComputed.OutputRoot, "output roots must match") require.Equal(t, eth.Bytes32(outputOnL1.OutputRoot), outputComputed.OutputRoot, "output roots must match")
} }
...@@ -37,6 +37,7 @@ func TestL2Sequencer_SequencerDrift(gt *testing.T) { ...@@ -37,6 +37,7 @@ func TestL2Sequencer_SequencerDrift(gt *testing.T) {
MaxSequencerDrift: 20, // larger than L1 block time we simulate in this test (12) MaxSequencerDrift: 20, // larger than L1 block time we simulate in this test (12)
SequencerWindowSize: 24, SequencerWindowSize: 24,
ChannelTimeout: 20, ChannelTimeout: 20,
L1BlockTime: 12,
} }
dp := e2eutils.MakeDeployParams(t, p) dp := e2eutils.MakeDeployParams(t, p)
sd := e2eutils.Setup(t, dp, defaultAlloc) sd := e2eutils.Setup(t, dp, defaultAlloc)
......
...@@ -34,6 +34,7 @@ func TestL2Verifier_SequenceWindow(gt *testing.T) { ...@@ -34,6 +34,7 @@ func TestL2Verifier_SequenceWindow(gt *testing.T) {
MaxSequencerDrift: 10, MaxSequencerDrift: 10,
SequencerWindowSize: 24, SequencerWindowSize: 24,
ChannelTimeout: 10, ChannelTimeout: 10,
L1BlockTime: 15,
} }
dp := e2eutils.MakeDeployParams(t, p) dp := e2eutils.MakeDeployParams(t, p)
sd := e2eutils.Setup(t, dp, defaultAlloc) sd := e2eutils.Setup(t, dp, defaultAlloc)
......
...@@ -25,6 +25,7 @@ func TestBatcherKeyRotation(gt *testing.T) { ...@@ -25,6 +25,7 @@ func TestBatcherKeyRotation(gt *testing.T) {
t := NewDefaultTesting(gt) t := NewDefaultTesting(gt)
dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams) dp := e2eutils.MakeDeployParams(t, defaultRollupTestParams)
dp.DeployConfig.L2BlockTime = 2
sd := e2eutils.Setup(t, dp, defaultAlloc) sd := e2eutils.Setup(t, dp, defaultAlloc)
log := testlog.Logger(t, log.LvlDebug) log := testlog.Logger(t, log.LvlDebug)
miner, seqEngine, sequencer := setupSequencerTest(t, sd, log) miner, seqEngine, sequencer := setupSequencerTest(t, sd, log)
...@@ -74,6 +75,10 @@ func TestBatcherKeyRotation(gt *testing.T) { ...@@ -74,6 +75,10 @@ func TestBatcherKeyRotation(gt *testing.T) {
sysCfgOwner, err := bind.NewKeyedTransactorWithChainID(dp.Secrets.SysCfgOwner, sd.RollupCfg.L1ChainID) sysCfgOwner, err := bind.NewKeyedTransactorWithChainID(dp.Secrets.SysCfgOwner, sd.RollupCfg.L1ChainID)
require.NoError(t, err) require.NoError(t, err)
owner, err := sysCfgContract.Owner(&bind.CallOpts{})
require.NoError(t, err)
require.Equal(t, dp.Addresses.SysCfgOwner, owner, "system config owner mismatch")
// Change the batch sender key to Bob! // Change the batch sender key to Bob!
tx, err := sysCfgContract.SetBatcherHash(sysCfgOwner, dp.Addresses.Bob.Hash()) tx, err := sysCfgContract.SetBatcherHash(sysCfgOwner, dp.Addresses.Bob.Hash())
require.NoError(t, err) require.NoError(t, err)
...@@ -81,7 +86,12 @@ func TestBatcherKeyRotation(gt *testing.T) { ...@@ -81,7 +86,12 @@ func TestBatcherKeyRotation(gt *testing.T) {
miner.ActL1StartBlock(12)(t) miner.ActL1StartBlock(12)(t)
miner.ActL1IncludeTx(dp.Addresses.SysCfgOwner)(t) miner.ActL1IncludeTx(dp.Addresses.SysCfgOwner)(t)
miner.ActL1EndBlock(t) miner.ActL1EndBlock(t)
receipt, err := miner.EthClient().TransactionReceipt(t.Ctx(), tx.Hash())
require.NoError(t, err)
cfgChangeL1BlockNum := miner.l1Chain.CurrentBlock().Number.Uint64() cfgChangeL1BlockNum := miner.l1Chain.CurrentBlock().Number.Uint64()
require.Equal(t, cfgChangeL1BlockNum, receipt.BlockNumber.Uint64())
// sequence L2 blocks, and submit with new batcher // sequence L2 blocks, and submit with new batcher
sequencer.ActL1HeadSignal(t) sequencer.ActL1HeadSignal(t)
...@@ -91,17 +101,30 @@ func TestBatcherKeyRotation(gt *testing.T) { ...@@ -91,17 +101,30 @@ func TestBatcherKeyRotation(gt *testing.T) {
miner.ActL1IncludeTx(dp.Addresses.Bob)(t) miner.ActL1IncludeTx(dp.Addresses.Bob)(t)
miner.ActL1EndBlock(t) miner.ActL1EndBlock(t)
// check that the first L2 payload that adopted the L1 block with the batcher key change indeed changed the batcher key in the system config // check that the first L2 payload that adopted the L1 block with the batcher key change
// indeed changed the batcher key in the system config
engCl := seqEngine.EngineClient(t, sd.RollupCfg) engCl := seqEngine.EngineClient(t, sd.RollupCfg)
payload, err := engCl.PayloadByNumber(t.Ctx(), sequencer.L2Safe().Number+12) // 12 new L2 blocks: 5 with origin before L1 block with batch, 6 with origin of L1 block with batch, 1 with new origin that changed the batcher // 12 new L2 blocks: 5 with origin before L1 block with batch, 6 with origin of L1 block
// with batch, 1 with new origin that changed the batcher
for i := 0; i <= 12; i++ {
payload, err := engCl.PayloadByNumber(t.Ctx(), sequencer.L2Safe().Number+uint64(i))
require.NoError(t, err) require.NoError(t, err)
ref, err := derive.PayloadToBlockRef(payload, &sd.RollupCfg.Genesis) ref, err := derive.PayloadToBlockRef(payload, &sd.RollupCfg.Genesis)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, ref.L1Origin.Number, cfgChangeL1BlockNum, "L2 block with L1 origin that included config change") if i < 6 {
require.Equal(t, ref.L1Origin.Number, cfgChangeL1BlockNum-2)
require.Equal(t, ref.SequenceNumber, uint64(i))
} else if i < 12 {
require.Equal(t, ref.L1Origin.Number, cfgChangeL1BlockNum-1)
require.Equal(t, ref.SequenceNumber, uint64(i-6))
} else {
require.Equal(t, ref.L1Origin.Number, cfgChangeL1BlockNum)
require.Equal(t, ref.SequenceNumber, uint64(0), "first L2 block with this origin") require.Equal(t, ref.SequenceNumber, uint64(0), "first L2 block with this origin")
sysCfg, err := derive.PayloadToSystemConfig(payload, sd.RollupCfg) sysCfg, err := derive.PayloadToSystemConfig(payload, sd.RollupCfg)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, dp.Addresses.Bob, sysCfg.BatcherAddr, "bob should be batcher now") require.Equal(t, dp.Addresses.Bob, sysCfg.BatcherAddr, "bob should be batcher now")
}
}
// sync from L1 // sync from L1
sequencer.ActL2PipelineFull(t) sequencer.ActL2PipelineFull(t)
......
...@@ -19,7 +19,7 @@ import ( ...@@ -19,7 +19,7 @@ import (
"github.com/ethereum-optimism/optimism/op-bindings/bindings" "github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys" "github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils" "github.com/ethereum-optimism/optimism/op-e2e/config"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive" "github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum-optimism/optimism/op-node/withdrawals" "github.com/ethereum-optimism/optimism/op-node/withdrawals"
) )
...@@ -31,11 +31,11 @@ type L1Bindings struct { ...@@ -31,11 +31,11 @@ type L1Bindings struct {
L2OutputOracle *bindings.L2OutputOracle L2OutputOracle *bindings.L2OutputOracle
} }
func NewL1Bindings(t Testing, l1Cl *ethclient.Client, deployments *e2eutils.DeploymentsL1) *L1Bindings { func NewL1Bindings(t Testing, l1Cl *ethclient.Client) *L1Bindings {
optimismPortal, err := bindings.NewOptimismPortal(deployments.OptimismPortalProxy, l1Cl) optimismPortal, err := bindings.NewOptimismPortal(config.L1Deployments.OptimismPortalProxy, l1Cl)
require.NoError(t, err) require.NoError(t, err)
l2OutputOracle, err := bindings.NewL2OutputOracle(deployments.L2OutputOracleProxy, l1Cl) l2OutputOracle, err := bindings.NewL2OutputOracle(config.L1Deployments.L2OutputOracleProxy, l1Cl)
require.NoError(t, err) require.NoError(t, err)
return &L1Bindings{ return &L1Bindings{
...@@ -319,7 +319,7 @@ func (s *CrossLayerUser) ActDeposit(t Testing) { ...@@ -319,7 +319,7 @@ func (s *CrossLayerUser) ActDeposit(t Testing) {
// estimate gas used by deposit // estimate gas used by deposit
gas, err := s.L2.env.EthCl.EstimateGas(t.Ctx(), ethereum.CallMsg{ gas, err := s.L2.env.EthCl.EstimateGas(t.Ctx(), ethereum.CallMsg{
From: s.L2.address, From: s.L2.address,
To: s.L2.txToAddr, To: &toAddr,
Value: depositTransferValue, // TODO: estimate gas does not support minting yet Value: depositTransferValue, // TODO: estimate gas does not support minting yet
Data: s.L2.txCallData, Data: s.L2.txCallData,
AccessList: nil, AccessList: nil,
......
...@@ -52,6 +52,9 @@ func runCrossLayerUserTest(gt *testing.T, test regolithScheduledTest) { ...@@ -52,6 +52,9 @@ func runCrossLayerUserTest(gt *testing.T, test regolithScheduledTest) {
sd := e2eutils.Setup(t, dp, defaultAlloc) sd := e2eutils.Setup(t, dp, defaultAlloc)
log := testlog.Logger(t, log.LvlDebug) log := testlog.Logger(t, log.LvlDebug)
require.Equal(t, dp.Secrets.Addresses().Batcher, dp.DeployConfig.BatchSenderAddress)
require.Equal(t, dp.Secrets.Addresses().Proposer, dp.DeployConfig.L2OutputOracleProposer)
miner, seqEngine, seq := setupSequencerTest(t, sd, log) miner, seqEngine, seq := setupSequencerTest(t, sd, log)
batcher := NewL2Batcher(log, sd.RollupCfg, &BatcherCfg{ batcher := NewL2Batcher(log, sd.RollupCfg, &BatcherCfg{
MinL1TxSize: 0, MinL1TxSize: 0,
...@@ -77,7 +80,7 @@ func runCrossLayerUserTest(gt *testing.T, test regolithScheduledTest) { ...@@ -77,7 +80,7 @@ func runCrossLayerUserTest(gt *testing.T, test regolithScheduledTest) {
EthCl: l1Cl, EthCl: l1Cl,
Signer: types.LatestSigner(sd.L1Cfg.Config), Signer: types.LatestSigner(sd.L1Cfg.Config),
AddressCorpora: addresses, AddressCorpora: addresses,
Bindings: NewL1Bindings(t, l1Cl, &sd.DeploymentsL1), Bindings: NewL1Bindings(t, l1Cl),
} }
l2UserEnv := &BasicUserEnv[*L2Bindings]{ l2UserEnv := &BasicUserEnv[*L2Bindings]{
EthCl: l2Cl, EthCl: l2Cl,
......
...@@ -74,13 +74,13 @@ func TestERC20BridgeDeposits(t *testing.T) { ...@@ -74,13 +74,13 @@ func TestERC20BridgeDeposits(t *testing.T) {
require.NotNil(t, event) require.NotNil(t, event)
// Approve WETH9 with the bridge // Approve WETH9 with the bridge
tx, err = WETH9.Approve(opts, predeploys.DevL1StandardBridgeAddr, new(big.Int).SetUint64(math.MaxUint64)) tx, err = WETH9.Approve(opts, cfg.L1Deployments.L1StandardBridgeProxy, new(big.Int).SetUint64(math.MaxUint64))
require.NoError(t, err) require.NoError(t, err)
_, err = waitForTransaction(tx.Hash(), l1Client, 6*time.Duration(cfg.DeployConfig.L1BlockTime)*time.Second) _, err = waitForTransaction(tx.Hash(), l1Client, 6*time.Duration(cfg.DeployConfig.L1BlockTime)*time.Second)
require.NoError(t, err) require.NoError(t, err)
// Bridge the WETH9 // Bridge the WETH9
l1StandardBridge, err := bindings.NewL1StandardBridge(predeploys.DevL1StandardBridgeAddr, l1Client) l1StandardBridge, err := bindings.NewL1StandardBridge(cfg.L1Deployments.L1StandardBridgeProxy, l1Client)
require.NoError(t, err) require.NoError(t, err)
tx, err = l1StandardBridge.BridgeERC20(opts, weth9Address, event.LocalToken, big.NewInt(100), 100000, []byte{}) tx, err = l1StandardBridge.BridgeERC20(opts, weth9Address, event.LocalToken, big.NewInt(100), 100000, []byte{})
require.NoError(t, err) require.NoError(t, err)
...@@ -90,7 +90,7 @@ func TestERC20BridgeDeposits(t *testing.T) { ...@@ -90,7 +90,7 @@ func TestERC20BridgeDeposits(t *testing.T) {
t.Log("Deposit through L1StandardBridge", "gas used", depositReceipt.GasUsed) t.Log("Deposit through L1StandardBridge", "gas used", depositReceipt.GasUsed)
// compute the deposit transaction hash + poll for it // compute the deposit transaction hash + poll for it
portal, err := bindings.NewOptimismPortal(predeploys.DevOptimismPortalAddr, l1Client) portal, err := bindings.NewOptimismPortal(cfg.L1Deployments.OptimismPortalProxy, l1Client)
require.NoError(t, err) require.NoError(t, err)
depIt, err := portal.FilterTransactionDeposited(&bind.FilterOpts{Start: 0}, nil, nil, nil) depIt, err := portal.FilterTransactionDeposited(&bind.FilterOpts{Start: 0}, nil, nil, nil)
......
package config
import (
"flag"
"fmt"
"os"
"path/filepath"
"testing"
"time"
"github.com/ethereum-optimism/optimism/op-chain-ops/genesis"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/state"
)
var (
// All of the following variables are set in the init function
// and read from JSON files on disk that are generated by the
// foundry deploy script. The are globally exported to be used
// in end to end tests.
// L1Allocs represents the L1 genesis block state.
L1Allocs *state.Dump
// L1Deployments maps contract names to accounts in the L1
// genesis block state.
L1Deployments *genesis.L1Deployments
// DeployConfig represents the deploy config used by the system.
DeployConfig *genesis.DeployConfig
)
// Init testing to enable test flags
var _ = func() bool {
testing.Init()
return true
}()
func init() {
var l1AllocsPath, l1DeploymentsPath, deployConfigPath string
cwd, err := os.Getwd()
if err != nil {
panic(err)
}
root, err := findMonorepoRoot(cwd)
if err != nil {
panic(err)
}
defaultL1AllocsPath := filepath.Join(root, ".devnet", "allocs-l1.json")
defaultL1DeploymentsPath := filepath.Join(root, ".devnet", "addresses.json")
defaultDeployConfigPath := filepath.Join(root, "packages", "contracts-bedrock", "deploy-config", "devnetL1.json")
flag.StringVar(&l1AllocsPath, "l1-allocs", defaultL1AllocsPath, "")
flag.StringVar(&l1DeploymentsPath, "l1-deployments", defaultL1DeploymentsPath, "")
flag.StringVar(&deployConfigPath, "deploy-config", defaultDeployConfigPath, "")
flag.Parse()
if err := allExist(l1AllocsPath, l1DeploymentsPath, deployConfigPath); err != nil {
return
}
L1Allocs, err = genesis.NewStateDump(l1AllocsPath)
if err != nil {
panic(err)
}
L1Deployments, err = genesis.NewL1Deployments(l1DeploymentsPath)
if err != nil {
panic(err)
}
DeployConfig, err = genesis.NewDeployConfig(deployConfigPath)
if err != nil {
panic(err)
}
// Do not use clique in the in memory tests. Otherwise block building
// would be much more complex.
DeployConfig.L1UseClique = false
// Set the L1 genesis block timestamp to now
DeployConfig.L1GenesisBlockTimestamp = hexutil.Uint64(time.Now().Unix())
DeployConfig.FundDevAccounts = true
// Speed up the in memory tests
DeployConfig.L1BlockTime = 2
DeployConfig.L2BlockTime = 1
if L1Deployments != nil {
DeployConfig.SetDeployments(L1Deployments)
}
}
func allExist(filenames ...string) error {
for _, filename := range filenames {
if _, err := os.Stat(filename); err != nil {
fmt.Printf("file %s does not exist, skipping genesis generation\n", filename)
return err
}
}
return nil
}
// findMonorepoRoot will recursively search upwards for a go.mod file.
// This depends on the structure of the monorepo having a go.mod file at the root.
func findMonorepoRoot(startDir string) (string, error) {
dir, err := filepath.Abs(startDir)
if err != nil {
return "", err
}
for {
modulePath := filepath.Join(dir, "go.mod")
if _, err := os.Stat(modulePath); err == nil {
return dir, nil
}
parentDir := filepath.Dir(dir)
// Check if we reached the filesystem root
if parentDir == dir {
break
}
dir = parentDir
}
return "", fmt.Errorf("monorepo root not found")
}
...@@ -11,6 +11,7 @@ func TestCollectAddresses(t *testing.T) { ...@@ -11,6 +11,7 @@ func TestCollectAddresses(t *testing.T) {
MaxSequencerDrift: 40, MaxSequencerDrift: 40,
SequencerWindowSize: 120, SequencerWindowSize: 120,
ChannelTimeout: 120, ChannelTimeout: 120,
L1BlockTime: 15,
} }
dp := MakeDeployParams(t, tp) dp := MakeDeployParams(t, tp)
alloc := &AllocParams{PrefundTestUsers: true} alloc := &AllocParams{PrefundTestUsers: true}
......
...@@ -14,14 +14,16 @@ import ( ...@@ -14,14 +14,16 @@ import (
// DefaultMnemonicConfig is the default mnemonic used in testing. // DefaultMnemonicConfig is the default mnemonic used in testing.
// We prefer a mnemonic rather than direct private keys to make it easier // We prefer a mnemonic rather than direct private keys to make it easier
// to export all testing keys in external tooling for use during debugging. // to export all testing keys in external tooling for use during debugging.
// If these values are changed, it is subject to breaking tests. They
// must be in sync with the values in the DeployConfig used to create the system.
var DefaultMnemonicConfig = &MnemonicConfig{ var DefaultMnemonicConfig = &MnemonicConfig{
Mnemonic: "test test test test test test test test test test test junk", Mnemonic: "test test test test test test test test test test test junk",
Deployer: "m/44'/60'/0'/0/1", CliqueSigner: "m/44'/60'/0'/0/0",
CliqueSigner: "m/44'/60'/0'/0/2", Proposer: "m/44'/60'/0'/0/1",
Proposer: "m/44'/60'/0'/0/3", Batcher: "m/44'/60'/0'/0/2",
Batcher: "m/44'/60'/0'/0/4", Deployer: "m/44'/60'/0'/0/3",
Alice: "m/44'/60'/0'/0/4",
SequencerP2P: "m/44'/60'/0'/0/5", SequencerP2P: "m/44'/60'/0'/0/5",
Alice: "m/44'/60'/0'/0/6",
Bob: "m/44'/60'/0'/0/7", Bob: "m/44'/60'/0'/0/7",
Mallory: "m/44'/60'/0'/0/8", Mallory: "m/44'/60'/0'/0/8",
SysCfgOwner: "m/44'/60'/0'/0/9", SysCfgOwner: "m/44'/60'/0'/0/9",
...@@ -32,8 +34,8 @@ var DefaultMnemonicConfig = &MnemonicConfig{ ...@@ -32,8 +34,8 @@ var DefaultMnemonicConfig = &MnemonicConfig{
type MnemonicConfig struct { type MnemonicConfig struct {
Mnemonic string Mnemonic string
Deployer string
CliqueSigner string CliqueSigner string
Deployer string
SysCfgOwner string SysCfgOwner string
// rollup actors // rollup actors
...@@ -97,8 +99,8 @@ func (m *MnemonicConfig) Secrets() (*Secrets, error) { ...@@ -97,8 +99,8 @@ func (m *MnemonicConfig) Secrets() (*Secrets, error) {
return &Secrets{ return &Secrets{
Deployer: deployer, Deployer: deployer,
CliqueSigner: cliqueSigner,
SysCfgOwner: sysCfgOwner, SysCfgOwner: sysCfgOwner,
CliqueSigner: cliqueSigner,
Proposer: proposer, Proposer: proposer,
Batcher: batcher, Batcher: batcher,
SequencerP2P: sequencerP2P, SequencerP2P: sequencerP2P,
......
...@@ -9,12 +9,11 @@ import ( ...@@ -9,12 +9,11 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rpc"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum-optimism/optimism/op-chain-ops/genesis" "github.com/ethereum-optimism/optimism/op-chain-ops/genesis"
"github.com/ethereum-optimism/optimism/op-e2e/config"
"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"
) )
...@@ -31,10 +30,6 @@ func WriteDefaultJWT(t TestingBase) string { ...@@ -31,10 +30,6 @@ func WriteDefaultJWT(t TestingBase) string {
return jwtPath return jwtPath
} }
func uint64ToBig(in uint64) *hexutil.Big {
return (*hexutil.Big)(new(big.Int).SetUint64(in))
}
// DeployParams bundles the deployment parameters to generate further testing inputs with. // DeployParams bundles the deployment parameters to generate further testing inputs with.
type DeployParams struct { type DeployParams struct {
DeployConfig *genesis.DeployConfig DeployConfig *genesis.DeployConfig
...@@ -56,73 +51,18 @@ func MakeDeployParams(t require.TestingT, tp *TestParams) *DeployParams { ...@@ -56,73 +51,18 @@ func MakeDeployParams(t require.TestingT, tp *TestParams) *DeployParams {
secrets, err := mnemonicCfg.Secrets() secrets, err := mnemonicCfg.Secrets()
require.NoError(t, err) require.NoError(t, err)
addresses := secrets.Addresses() addresses := secrets.Addresses()
deployConfig := &genesis.DeployConfig{
L1ChainID: 901,
L2ChainID: 902,
L2BlockTime: 2,
MaxSequencerDrift: tp.MaxSequencerDrift,
SequencerWindowSize: tp.SequencerWindowSize,
ChannelTimeout: tp.ChannelTimeout,
P2PSequencerAddress: addresses.SequencerP2P,
BatchInboxAddress: common.Address{0: 0x42, 19: 0xff}, // tbd
BatchSenderAddress: addresses.Batcher,
L2OutputOracleSubmissionInterval: 6,
L2OutputOracleStartingTimestamp: -1,
L2OutputOracleProposer: addresses.Proposer,
L2OutputOracleChallenger: common.Address{}, // tbd
FinalSystemOwner: addresses.SysCfgOwner,
L1BlockTime: tp.L1BlockTime, deployConfig := config.DeployConfig.Copy()
L1GenesisBlockNonce: 0, deployConfig.MaxSequencerDrift = tp.MaxSequencerDrift
CliqueSignerAddress: common.Address{}, // proof of stake, no clique deployConfig.SequencerWindowSize = tp.SequencerWindowSize
L1GenesisBlockTimestamp: hexutil.Uint64(time.Now().Unix()), deployConfig.ChannelTimeout = tp.ChannelTimeout
L1GenesisBlockGasLimit: 30_000_000, deployConfig.L1BlockTime = tp.L1BlockTime
L1GenesisBlockDifficulty: uint64ToBig(1), deployConfig.L2GenesisRegolithTimeOffset = nil
L1GenesisBlockMixHash: common.Hash{},
L1GenesisBlockCoinbase: common.Address{},
L1GenesisBlockNumber: 0,
L1GenesisBlockGasUsed: 0,
L1GenesisBlockParentHash: common.Hash{},
L1GenesisBlockBaseFeePerGas: uint64ToBig(1000_000_000), // 1 gwei
FinalizationPeriodSeconds: 12,
L2GenesisBlockNonce: 0, require.NoError(t, deployConfig.Check())
L2GenesisBlockGasLimit: 30_000_000, require.Equal(t, addresses.Batcher, deployConfig.BatchSenderAddress)
L2GenesisBlockDifficulty: uint64ToBig(0), require.Equal(t, addresses.Proposer, deployConfig.L2OutputOracleProposer)
L2GenesisBlockMixHash: common.Hash{}, require.Equal(t, addresses.SequencerP2P, deployConfig.P2PSequencerAddress)
L2GenesisBlockNumber: 0,
L2GenesisBlockGasUsed: 0,
L2GenesisBlockParentHash: common.Hash{},
L2GenesisBlockBaseFeePerGas: uint64ToBig(1000_000_000),
GasPriceOracleOverhead: 2100,
GasPriceOracleScalar: 1000_000,
DeploymentWaitConfirmations: 1,
SequencerFeeVaultRecipient: common.Address{19: 1},
BaseFeeVaultRecipient: common.Address{19: 2},
L1FeeVaultRecipient: common.Address{19: 3},
BaseFeeVaultMinimumWithdrawalAmount: uint64ToBig(1000_000_000), // 1 gwei
L1FeeVaultMinimumWithdrawalAmount: uint64ToBig(1000_000_000), // 1 gwei
SequencerFeeVaultMinimumWithdrawalAmount: uint64ToBig(1000_000_000), // 1 gwei
BaseFeeVaultWithdrawalNetwork: genesis.WithdrawalNetwork("local"), // L2 withdrawal network
L1FeeVaultWithdrawalNetwork: genesis.WithdrawalNetwork("local"), // L2 withdrawal network
SequencerFeeVaultWithdrawalNetwork: genesis.WithdrawalNetwork("local"), // L2 withdrawal network
EIP1559Elasticity: 10,
EIP1559Denominator: 50,
FundDevAccounts: false,
}
// Configure the DeployConfig with the expected developer L1
// addresses.
if err := deployConfig.InitDeveloperDeployedAddresses(); err != nil {
panic(err)
}
return &DeployParams{ return &DeployParams{
DeployConfig: deployConfig, DeployConfig: deployConfig,
...@@ -132,23 +72,12 @@ func MakeDeployParams(t require.TestingT, tp *TestParams) *DeployParams { ...@@ -132,23 +72,12 @@ func MakeDeployParams(t require.TestingT, tp *TestParams) *DeployParams {
} }
} }
// DeploymentsL1 captures the L1 addresses used in the deployment,
// commonly just the developer predeploys during testing,
// but later deployed contracts may be used in some tests too.
type DeploymentsL1 struct {
L1CrossDomainMessengerProxy common.Address
L1StandardBridgeProxy common.Address
L2OutputOracleProxy common.Address
OptimismPortalProxy common.Address
SystemConfigProxy common.Address
}
// SetupData bundles the L1, L2, rollup and deployment configuration data: everything for a full test setup. // SetupData bundles the L1, L2, rollup and deployment configuration data: everything for a full test setup.
type SetupData struct { type SetupData struct {
L1Cfg *core.Genesis L1Cfg *core.Genesis
L2Cfg *core.Genesis L2Cfg *core.Genesis
RollupCfg *rollup.Config RollupCfg *rollup.Config
DeploymentsL1 DeploymentsL1 DeploymentsL1 *genesis.L1Deployments
} }
// AllocParams defines genesis allocations to apply on top of the genesis generated by deploy parameters. // AllocParams defines genesis allocations to apply on top of the genesis generated by deploy parameters.
...@@ -169,8 +98,14 @@ func Ether(v uint64) *big.Int { ...@@ -169,8 +98,14 @@ func Ether(v uint64) *big.Int {
// Setup computes the testing setup configurations from deployment configuration and optional allocation parameters. // Setup computes the testing setup configurations from deployment configuration and optional allocation parameters.
func Setup(t require.TestingT, deployParams *DeployParams, alloc *AllocParams) *SetupData { func Setup(t require.TestingT, deployParams *DeployParams, alloc *AllocParams) *SetupData {
deployConf := deployParams.DeployConfig deployConf := deployParams.DeployConfig.Copy()
l1Genesis, err := genesis.BuildL1DeveloperGenesis(deployConf) deployConf.L1GenesisBlockTimestamp = hexutil.Uint64(time.Now().Unix())
require.NoError(t, deployConf.Check())
l1Deployments := config.L1Deployments.Copy()
require.NoError(t, l1Deployments.Check())
l1Genesis, err := genesis.BuildL1DeveloperGenesis(deployConf, config.L1Allocs, l1Deployments, true)
require.NoError(t, err, "failed to create l1 genesis") require.NoError(t, err, "failed to create l1 genesis")
if alloc.PrefundTestUsers { if alloc.PrefundTestUsers {
for _, addr := range deployParams.Addresses.All() { for _, addr := range deployParams.Addresses.All() {
...@@ -218,18 +153,12 @@ func Setup(t require.TestingT, deployParams *DeployParams, alloc *AllocParams) * ...@@ -218,18 +153,12 @@ func Setup(t require.TestingT, deployParams *DeployParams, alloc *AllocParams) *
L1ChainID: new(big.Int).SetUint64(deployConf.L1ChainID), L1ChainID: new(big.Int).SetUint64(deployConf.L1ChainID),
L2ChainID: new(big.Int).SetUint64(deployConf.L2ChainID), L2ChainID: new(big.Int).SetUint64(deployConf.L2ChainID),
BatchInboxAddress: deployConf.BatchInboxAddress, BatchInboxAddress: deployConf.BatchInboxAddress,
DepositContractAddress: predeploys.DevOptimismPortalAddr, DepositContractAddress: deployConf.OptimismPortalProxy,
L1SystemConfigAddress: predeploys.DevSystemConfigAddr, L1SystemConfigAddress: deployConf.SystemConfigProxy,
RegolithTime: deployConf.RegolithTime(uint64(deployConf.L1GenesisBlockTimestamp)), RegolithTime: deployConf.RegolithTime(uint64(deployConf.L1GenesisBlockTimestamp)),
} }
deploymentsL1 := DeploymentsL1{ require.NoError(t, rollupCfg.Check())
L1CrossDomainMessengerProxy: predeploys.DevL1CrossDomainMessengerAddr,
L1StandardBridgeProxy: predeploys.DevL1StandardBridgeAddr,
L2OutputOracleProxy: predeploys.DevL2OutputOracleAddr,
OptimismPortalProxy: predeploys.DevOptimismPortalAddr,
SystemConfigProxy: predeploys.DevSystemConfigAddr,
}
// Sanity check that the config is correct // Sanity check that the config is correct
require.Equal(t, deployParams.Secrets.Addresses().Batcher, deployParams.DeployConfig.BatchSenderAddress) require.Equal(t, deployParams.Secrets.Addresses().Batcher, deployParams.DeployConfig.BatchSenderAddress)
...@@ -240,7 +169,7 @@ func Setup(t require.TestingT, deployParams *DeployParams, alloc *AllocParams) * ...@@ -240,7 +169,7 @@ func Setup(t require.TestingT, deployParams *DeployParams, alloc *AllocParams) *
L1Cfg: l1Genesis, L1Cfg: l1Genesis,
L2Cfg: l2Genesis, L2Cfg: l2Genesis,
RollupCfg: rollupCfg, RollupCfg: rollupCfg,
DeploymentsL1: deploymentsL1, DeploymentsL1: l1Deployments,
} }
} }
...@@ -252,46 +181,3 @@ func SystemConfigFromDeployConfig(deployConfig *genesis.DeployConfig) eth.System ...@@ -252,46 +181,3 @@ func SystemConfigFromDeployConfig(deployConfig *genesis.DeployConfig) eth.System
GasLimit: uint64(deployConfig.L2GenesisBlockGasLimit), GasLimit: uint64(deployConfig.L2GenesisBlockGasLimit),
} }
} }
// ForkedDeployConfig returns a deploy config that's suitable for use with a
// forked L1.
func ForkedDeployConfig(t require.TestingT, mnemonicCfg *MnemonicConfig, startBlock *types.Block) *genesis.DeployConfig {
startTag := rpc.BlockNumberOrHashWithHash(startBlock.Hash(), true)
secrets, err := mnemonicCfg.Secrets()
require.NoError(t, err)
addrs := secrets.Addresses()
marshalable := genesis.MarshalableRPCBlockNumberOrHash(startTag)
out := &genesis.DeployConfig{
L1StartingBlockTag: &marshalable,
L1ChainID: 1,
L2ChainID: 10,
L2BlockTime: 2,
MaxSequencerDrift: 3600,
SequencerWindowSize: 100,
ChannelTimeout: 40,
P2PSequencerAddress: addrs.SequencerP2P,
BatchInboxAddress: common.HexToAddress("0xff00000000000000000000000000000000000000"),
BatchSenderAddress: addrs.Batcher,
FinalSystemOwner: addrs.SysCfgOwner,
L1GenesisBlockDifficulty: uint64ToBig(0),
L1GenesisBlockBaseFeePerGas: uint64ToBig(0),
L2OutputOracleSubmissionInterval: 10,
L2OutputOracleStartingTimestamp: int(startBlock.Time()),
L2OutputOracleProposer: addrs.Proposer,
L2OutputOracleChallenger: addrs.Deployer,
L2GenesisBlockGasLimit: hexutil.Uint64(15_000_000),
// taken from devnet, need to check this
L2GenesisBlockBaseFeePerGas: uint64ToBig(0x3B9ACA00),
L2GenesisBlockDifficulty: uint64ToBig(0),
L1BlockTime: 12,
CliqueSignerAddress: addrs.CliqueSigner,
FinalizationPeriodSeconds: 2,
DeploymentWaitConfirmations: 1,
EIP1559Elasticity: 10,
EIP1559Denominator: 50,
GasPriceOracleOverhead: 2100,
GasPriceOracleScalar: 1_000_000,
FundDevAccounts: true,
}
return out
}
...@@ -8,6 +8,7 @@ import ( ...@@ -8,6 +8,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys" "github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum-optimism/optimism/op-e2e/config"
) )
func TestWriteDefaultJWT(t *testing.T) { func TestWriteDefaultJWT(t *testing.T) {
...@@ -22,6 +23,7 @@ func TestSetup(t *testing.T) { ...@@ -22,6 +23,7 @@ func TestSetup(t *testing.T) {
MaxSequencerDrift: 40, MaxSequencerDrift: 40,
SequencerWindowSize: 120, SequencerWindowSize: 120,
ChannelTimeout: 120, ChannelTimeout: 120,
L1BlockTime: 15,
} }
dp := MakeDeployParams(t, tp) dp := MakeDeployParams(t, tp)
alloc := &AllocParams{PrefundTestUsers: true} alloc := &AllocParams{PrefundTestUsers: true}
...@@ -32,6 +34,6 @@ func TestSetup(t *testing.T) { ...@@ -32,6 +34,6 @@ func TestSetup(t *testing.T) {
require.Contains(t, sd.L2Cfg.Alloc, dp.Addresses.Alice) require.Contains(t, sd.L2Cfg.Alloc, dp.Addresses.Alice)
require.Equal(t, sd.L2Cfg.Alloc[dp.Addresses.Alice].Balance, Ether(1e12)) require.Equal(t, sd.L2Cfg.Alloc[dp.Addresses.Alice].Balance, Ether(1e12))
require.Contains(t, sd.L1Cfg.Alloc, predeploys.DevOptimismPortalAddr) require.Contains(t, sd.L1Cfg.Alloc, config.L1Deployments.OptimismPortalProxy)
require.Contains(t, sd.L2Cfg.Alloc, predeploys.L1BlockAddr) require.Contains(t, sd.L2Cfg.Alloc, predeploys.L1BlockAddr)
} }
...@@ -26,6 +26,11 @@ import ( ...@@ -26,6 +26,11 @@ import (
"github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/node"
) )
var (
// errTimeout represents a timeout
errTimeout = errors.New("timeout")
)
func waitForL1OriginOnL2(l1BlockNum uint64, client *ethclient.Client, timeout time.Duration) (*types.Block, error) { func waitForL1OriginOnL2(l1BlockNum uint64, client *ethclient.Client, timeout time.Duration) (*types.Block, error) {
timeoutCh := time.After(timeout) timeoutCh := time.After(timeout)
ctx, cancel := context.WithTimeout(context.Background(), timeout) ctx, cancel := context.WithTimeout(context.Background(), timeout)
...@@ -56,7 +61,7 @@ func waitForL1OriginOnL2(l1BlockNum uint64, client *ethclient.Client, timeout ti ...@@ -56,7 +61,7 @@ func waitForL1OriginOnL2(l1BlockNum uint64, client *ethclient.Client, timeout ti
case err := <-headSub.Err(): case err := <-headSub.Err():
return nil, fmt.Errorf("error in head subscription: %w", err) return nil, fmt.Errorf("error in head subscription: %w", err)
case <-timeoutCh: case <-timeoutCh:
return nil, errors.New("timeout") return nil, errTimeout
} }
} }
} }
...@@ -77,7 +82,11 @@ func waitForTransaction(hash common.Hash, client *ethclient.Client, timeout time ...@@ -77,7 +82,11 @@ func waitForTransaction(hash common.Hash, client *ethclient.Client, timeout time
select { select {
case <-timeoutCh: case <-timeoutCh:
return nil, errors.New("timeout") tip, err := client.BlockByNumber(context.Background(), nil)
if err != nil {
return nil, err
}
return nil, fmt.Errorf("receipt for transaction %s not found. tip block number is %d: %w", hash.Hex(), tip.NumberU64(), errTimeout)
case <-ticker.C: case <-ticker.C:
} }
} }
...@@ -104,7 +113,7 @@ func waitForBlock(number *big.Int, client *ethclient.Client, timeout time.Durati ...@@ -104,7 +113,7 @@ func waitForBlock(number *big.Int, client *ethclient.Client, timeout time.Durati
case err := <-headSub.Err(): case err := <-headSub.Err():
return nil, fmt.Errorf("error in head subscription: %w", err) return nil, fmt.Errorf("error in head subscription: %w", err)
case <-timeoutCh: case <-timeoutCh:
return nil, errors.New("timeout") return nil, errTimeout
} }
} }
} }
......
...@@ -9,6 +9,7 @@ import ( ...@@ -9,6 +9,7 @@ import (
"testing" "testing"
"github.com/ethereum-optimism/optimism/op-chain-ops/genesis" "github.com/ethereum-optimism/optimism/op-chain-ops/genesis"
"github.com/ethereum-optimism/optimism/op-e2e/config"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils"
"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"
...@@ -49,7 +50,8 @@ type OpGeth struct { ...@@ -49,7 +50,8 @@ type OpGeth struct {
func NewOpGeth(t *testing.T, ctx context.Context, cfg *SystemConfig) (*OpGeth, error) { func NewOpGeth(t *testing.T, ctx context.Context, cfg *SystemConfig) (*OpGeth, error) {
logger := testlog.Logger(t, log.LvlCrit) logger := testlog.Logger(t, log.LvlCrit)
l1Genesis, err := genesis.BuildL1DeveloperGenesis(cfg.DeployConfig)
l1Genesis, err := genesis.BuildL1DeveloperGenesis(cfg.DeployConfig, config.L1Allocs, config.L1Deployments, true)
require.Nil(t, err) require.Nil(t, err)
l1Block := l1Genesis.ToBlock() l1Block := l1Genesis.ToBlock()
......
...@@ -33,6 +33,7 @@ import ( ...@@ -33,6 +33,7 @@ import (
"github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
mocknet "github.com/libp2p/go-libp2p/p2p/net/mock" mocknet "github.com/libp2p/go-libp2p/p2p/net/mock"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
...@@ -42,6 +43,7 @@ import ( ...@@ -42,6 +43,7 @@ import (
batchermetrics "github.com/ethereum-optimism/optimism/op-batcher/metrics" batchermetrics "github.com/ethereum-optimism/optimism/op-batcher/metrics"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys" "github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum-optimism/optimism/op-chain-ops/genesis" "github.com/ethereum-optimism/optimism/op-chain-ops/genesis"
"github.com/ethereum-optimism/optimism/op-e2e/config"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils" "github.com/ethereum-optimism/optimism/op-e2e/e2eutils"
"github.com/ethereum-optimism/optimism/op-node/chaincfg" "github.com/ethereum-optimism/optimism/op-node/chaincfg"
"github.com/ethereum-optimism/optimism/op-node/eth" "github.com/ethereum-optimism/optimism/op-node/eth"
...@@ -78,81 +80,27 @@ func newTxMgrConfig(l1Addr string, privKey *ecdsa.PrivateKey) txmgr.CLIConfig { ...@@ -78,81 +80,27 @@ func newTxMgrConfig(l1Addr string, privKey *ecdsa.PrivateKey) txmgr.CLIConfig {
func DefaultSystemConfig(t *testing.T) SystemConfig { func DefaultSystemConfig(t *testing.T) SystemConfig {
secrets, err := e2eutils.DefaultMnemonicConfig.Secrets() secrets, err := e2eutils.DefaultMnemonicConfig.Secrets()
require.NoError(t, err) require.NoError(t, err)
addresses := secrets.Addresses() deployConfig := config.DeployConfig.Copy()
deployConfig.L1GenesisBlockTimestamp = hexutil.Uint64(time.Now().Unix())
deployConfig := &genesis.DeployConfig{ require.NoError(t, deployConfig.Check())
L1ChainID: 900, l1Deployments := config.L1Deployments.Copy()
L2ChainID: 901, require.NoError(t, l1Deployments.Check())
L2BlockTime: 1,
require.Equal(t, secrets.Addresses().Batcher, deployConfig.BatchSenderAddress)
FinalizationPeriodSeconds: 60 * 60 * 24, require.Equal(t, secrets.Addresses().SequencerP2P, deployConfig.P2PSequencerAddress)
MaxSequencerDrift: 10, require.Equal(t, secrets.Addresses().Proposer, deployConfig.L2OutputOracleProposer)
SequencerWindowSize: 30,
ChannelTimeout: 10, // Tests depend on premine being filled with secrets addresses
P2PSequencerAddress: addresses.SequencerP2P, premine := make(map[common.Address]*big.Int)
BatchInboxAddress: common.Address{0: 0x52, 19: 0xff}, // tbd for _, addr := range secrets.Addresses().All() {
BatchSenderAddress: addresses.Batcher, premine[addr] = new(big.Int).Mul(big.NewInt(1000), big.NewInt(params.Ether))
L2OutputOracleSubmissionInterval: 4,
L2OutputOracleStartingTimestamp: -1,
L2OutputOracleProposer: addresses.Proposer,
L2OutputOracleChallenger: common.Address{}, // tbd
FinalSystemOwner: addresses.SysCfgOwner,
L1BlockTime: 2,
L1GenesisBlockNonce: 4660,
CliqueSignerAddress: common.Address{}, // op-e2e used to run Clique, but now uses fake Proof of Stake.
L1GenesisBlockTimestamp: hexutil.Uint64(time.Now().Unix()),
L1GenesisBlockGasLimit: 30_000_000,
L1GenesisBlockDifficulty: uint642big(1),
L1GenesisBlockMixHash: common.Hash{},
L1GenesisBlockCoinbase: common.Address{},
L1GenesisBlockNumber: 0,
L1GenesisBlockGasUsed: 0,
L1GenesisBlockParentHash: common.Hash{},
L1GenesisBlockBaseFeePerGas: uint642big(7),
L2GenesisBlockNonce: 0,
L2GenesisBlockGasLimit: 30_000_000,
L2GenesisBlockDifficulty: uint642big(1),
L2GenesisBlockMixHash: common.Hash{},
L2GenesisBlockNumber: 0,
L2GenesisBlockGasUsed: 0,
L2GenesisBlockParentHash: common.Hash{},
L2GenesisBlockBaseFeePerGas: uint642big(7),
GasPriceOracleOverhead: 2100,
GasPriceOracleScalar: 1_000_000,
SequencerFeeVaultRecipient: common.Address{19: 1},
BaseFeeVaultRecipient: common.Address{19: 2},
L1FeeVaultRecipient: common.Address{19: 3},
BaseFeeVaultMinimumWithdrawalAmount: uint642big(1000_000_000), // 1 gwei
L1FeeVaultMinimumWithdrawalAmount: uint642big(1000_000_000), // 1 gwei
SequencerFeeVaultMinimumWithdrawalAmount: uint642big(1000_000_000), // 1 gwei
BaseFeeVaultWithdrawalNetwork: genesis.WithdrawalNetwork("local"), // L2 withdrawal network
L1FeeVaultWithdrawalNetwork: genesis.WithdrawalNetwork("local"), // L2 withdrawal network
SequencerFeeVaultWithdrawalNetwork: genesis.WithdrawalNetwork("local"), // L2 withdrawal network
DeploymentWaitConfirmations: 1,
EIP1559Elasticity: 2,
EIP1559Denominator: 8,
FundDevAccounts: true,
}
if err := deployConfig.InitDeveloperDeployedAddresses(); err != nil {
panic(err)
} }
return SystemConfig{ return SystemConfig{
Secrets: secrets, Secrets: secrets,
Premine: premine,
Premine: make(map[common.Address]*big.Int),
DeployConfig: deployConfig, DeployConfig: deployConfig,
L1Deployments: config.L1Deployments,
L1InfoPredeployAddress: predeploys.L1BlockAddr, L1InfoPredeployAddress: predeploys.L1BlockAddr,
JWTFilePath: writeDefaultJWT(t), JWTFilePath: writeDefaultJWT(t),
JWTSecret: testingJWTSecret, JWTSecret: testingJWTSecret,
...@@ -169,7 +117,7 @@ func DefaultSystemConfig(t *testing.T) SystemConfig { ...@@ -169,7 +117,7 @@ func DefaultSystemConfig(t *testing.T) SystemConfig {
ListenPort: 0, ListenPort: 0,
EnableAdmin: true, EnableAdmin: true,
}, },
L1EpochPollInterval: time.Second * 4, L1EpochPollInterval: time.Second * 2,
ConfigPersistence: &rollupNode.DisabledConfigPersistence{}, ConfigPersistence: &rollupNode.DisabledConfigPersistence{},
}, },
"verifier": { "verifier": {
...@@ -214,6 +162,7 @@ type SystemConfig struct { ...@@ -214,6 +162,7 @@ type SystemConfig struct {
L1InfoPredeployAddress common.Address L1InfoPredeployAddress common.Address
DeployConfig *genesis.DeployConfig DeployConfig *genesis.DeployConfig
L1Deployments *genesis.L1Deployments
JWTFilePath string JWTFilePath string
JWTSecret [32]byte JWTSecret [32]byte
...@@ -355,7 +304,11 @@ func (cfg SystemConfig) Start(_opts ...SystemConfigOption) (*System, error) { ...@@ -355,7 +304,11 @@ func (cfg SystemConfig) Start(_opts ...SystemConfigOption) (*System, error) {
c = sys.TimeTravelClock c = sys.TimeTravelClock
} }
l1Genesis, err := genesis.BuildL1DeveloperGenesis(cfg.DeployConfig) if err := cfg.DeployConfig.Check(); err != nil {
return nil, err
}
l1Genesis, err := genesis.BuildL1DeveloperGenesis(cfg.DeployConfig, config.L1Allocs, config.L1Deployments, true)
if err != nil { if err != nil {
return nil, err return nil, err
} }
...@@ -419,12 +372,15 @@ func (cfg SystemConfig) Start(_opts ...SystemConfigOption) (*System, error) { ...@@ -419,12 +372,15 @@ func (cfg SystemConfig) Start(_opts ...SystemConfigOption) (*System, error) {
L1ChainID: cfg.L1ChainIDBig(), L1ChainID: cfg.L1ChainIDBig(),
L2ChainID: cfg.L2ChainIDBig(), L2ChainID: cfg.L2ChainIDBig(),
BatchInboxAddress: cfg.DeployConfig.BatchInboxAddress, BatchInboxAddress: cfg.DeployConfig.BatchInboxAddress,
DepositContractAddress: predeploys.DevOptimismPortalAddr, DepositContractAddress: cfg.DeployConfig.OptimismPortalProxy,
L1SystemConfigAddress: predeploys.DevSystemConfigAddr, L1SystemConfigAddress: cfg.DeployConfig.SystemConfigProxy,
RegolithTime: cfg.DeployConfig.RegolithTime(uint64(cfg.DeployConfig.L1GenesisBlockTimestamp)), RegolithTime: cfg.DeployConfig.RegolithTime(uint64(cfg.DeployConfig.L1GenesisBlockTimestamp)),
} }
} }
defaultConfig := makeRollupConfig() defaultConfig := makeRollupConfig()
if err := defaultConfig.Check(); err != nil {
return nil, err
}
sys.RollupConfig = &defaultConfig sys.RollupConfig = &defaultConfig
// Initialize nodes // Initialize nodes
...@@ -620,11 +576,12 @@ func (cfg SystemConfig) Start(_opts ...SystemConfigOption) (*System, error) { ...@@ -620,11 +576,12 @@ func (cfg SystemConfig) Start(_opts ...SystemConfigOption) (*System, error) {
if sys.RollupNodes["sequencer"] == nil { if sys.RollupNodes["sequencer"] == nil {
return sys, nil return sys, nil
} }
// L2Output Submitter // L2Output Submitter
sys.L2OutputSubmitter, err = l2os.NewL2OutputSubmitterFromCLIConfig(l2os.CLIConfig{ sys.L2OutputSubmitter, err = l2os.NewL2OutputSubmitterFromCLIConfig(l2os.CLIConfig{
L1EthRpc: sys.Nodes["l1"].WSEndpoint(), L1EthRpc: sys.Nodes["l1"].WSEndpoint(),
RollupRpc: sys.RollupNodes["sequencer"].HTTPEndpoint(), RollupRpc: sys.RollupNodes["sequencer"].HTTPEndpoint(),
L2OOAddress: predeploys.DevL2OutputOracleAddr.String(), L2OOAddress: config.L1Deployments.L2OutputOracleProxy.Hex(),
PollInterval: 50 * time.Millisecond, PollInterval: 50 * time.Millisecond,
TxMgrConfig: newTxMgrConfig(sys.Nodes["l1"].WSEndpoint(), cfg.Secrets.Proposer), TxMgrConfig: newTxMgrConfig(sys.Nodes["l1"].WSEndpoint(), cfg.Secrets.Proposer),
AllowNonFinalized: cfg.NonFinalizedProposals, AllowNonFinalized: cfg.NonFinalizedProposals,
...@@ -648,7 +605,7 @@ func (cfg SystemConfig) Start(_opts ...SystemConfigOption) (*System, error) { ...@@ -648,7 +605,7 @@ func (cfg SystemConfig) Start(_opts ...SystemConfigOption) (*System, error) {
RollupRpc: sys.RollupNodes["sequencer"].HTTPEndpoint(), RollupRpc: sys.RollupNodes["sequencer"].HTTPEndpoint(),
MaxPendingTransactions: 0, MaxPendingTransactions: 0,
MaxChannelDuration: 1, MaxChannelDuration: 1,
MaxL1TxSize: 120_000, MaxL1TxSize: 240_000,
CompressorConfig: compressor.CLIConfig{ CompressorConfig: compressor.CLIConfig{
TargetL1TxSizeBytes: cfg.BatcherTargetL1TxSizeBytes, TargetL1TxSizeBytes: cfg.BatcherTargetL1TxSizeBytes,
TargetNumFrames: 1, TargetNumFrames: 1,
...@@ -762,12 +719,6 @@ func (cfg SystemConfig) L2ChainIDBig() *big.Int { ...@@ -762,12 +719,6 @@ func (cfg SystemConfig) L2ChainIDBig() *big.Int {
return new(big.Int).SetUint64(cfg.DeployConfig.L2ChainID) return new(big.Int).SetUint64(cfg.DeployConfig.L2ChainID)
} }
func uint642big(in uint64) *hexutil.Big {
b := new(big.Int).SetUint64(in)
hu := hexutil.Big(*b)
return &hu
}
func hexPriv(in *ecdsa.PrivateKey) string { func hexPriv(in *ecdsa.PrivateKey) string {
b := e2eutils.EncodePrivKey(in) b := e2eutils.EncodePrivKey(in)
return hexutil.Encode(b) return hexutil.Encode(b)
......
...@@ -11,12 +11,14 @@ import ( ...@@ -11,12 +11,14 @@ import (
"github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/accounts/keystore"
"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/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth/ethconfig" "github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/core/peer"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
...@@ -54,7 +56,7 @@ func TestL2OutputSubmitter(t *testing.T) { ...@@ -54,7 +56,7 @@ func TestL2OutputSubmitter(t *testing.T) {
rollupClient := sources.NewRollupClient(client.NewBaseRPCClient(rollupRPCClient)) rollupClient := sources.NewRollupClient(client.NewBaseRPCClient(rollupRPCClient))
// OutputOracle is already deployed // OutputOracle is already deployed
l2OutputOracle, err := bindings.NewL2OutputOracleCaller(predeploys.DevL2OutputOracleAddr, l1Client) l2OutputOracle, err := bindings.NewL2OutputOracleCaller(cfg.L1Deployments.L2OutputOracleProxy, l1Client)
require.Nil(t, err) require.Nil(t, err)
initialOutputBlockNumber, err := l2OutputOracle.LatestBlockNumber(&bind.CallOpts{}) initialOutputBlockNumber, err := l2OutputOracle.LatestBlockNumber(&bind.CallOpts{})
...@@ -979,24 +981,6 @@ func calcGasFees(gasUsed uint64, gasTipCap *big.Int, gasFeeCap *big.Int, baseFee ...@@ -979,24 +981,6 @@ func calcGasFees(gasUsed uint64, gasTipCap *big.Int, gasFeeCap *big.Int, baseFee
return x.Mul(x, new(big.Int).SetUint64(gasUsed)) return x.Mul(x, new(big.Int).SetUint64(gasUsed))
} }
// calcL1GasUsed returns the gas used to include the transaction data in
// the calldata on L1
func calcL1GasUsed(data []byte, overhead *big.Int) *big.Int {
var zeroes, ones uint64
for _, byt := range data {
if byt == 0 {
zeroes++
} else {
ones++
}
}
zeroesGas := zeroes * 4 // params.TxDataZeroGas
onesGas := (ones + 68) * 16 // params.TxDataNonZeroGasEIP2028
l1Gas := new(big.Int).SetUint64(zeroesGas + onesGas)
return new(big.Int).Add(l1Gas, overhead)
}
// TestWithdrawals checks that a deposit and then withdrawal execution succeeds. It verifies the // TestWithdrawals checks that a deposit and then withdrawal execution succeeds. It verifies the
// balance changes on L1 and L2 and has to include gas fees in the balance checks. // balance changes on L1 and L2 and has to include gas fees in the balance checks.
// It does not check that the withdrawal can be executed prior to the end of the finality period. // It does not check that the withdrawal can be executed prior to the end of the finality period.
...@@ -1105,10 +1089,9 @@ func TestFees(t *testing.T) { ...@@ -1105,10 +1089,9 @@ func TestFees(t *testing.T) {
InitParallel(t) InitParallel(t)
cfg := DefaultSystemConfig(t) cfg := DefaultSystemConfig(t)
// TODO: after we have the system config contract and new op-geth L1 cost utils, // This test only works with these config values modified
// we can pull in l1 costs into every e2e test and account for it in assertions easily etc. cfg.DeployConfig.L2GenesisRegolithTimeOffset = nil
cfg.DeployConfig.GasPriceOracleOverhead = 2100 cfg.DeployConfig.L1GenesisBlockBaseFeePerGas = (*hexutil.Big)(big.NewInt(7))
cfg.DeployConfig.GasPriceOracleScalar = 1000_000
sys, err := cfg.Start() sys, err := cfg.Start()
require.Nil(t, err, "Error starting up system") require.Nil(t, err, "Error starting up system")
...@@ -1116,11 +1099,21 @@ func TestFees(t *testing.T) { ...@@ -1116,11 +1099,21 @@ func TestFees(t *testing.T) {
l2Seq := sys.Clients["sequencer"] l2Seq := sys.Clients["sequencer"]
l2Verif := sys.Clients["verifier"] l2Verif := sys.Clients["verifier"]
l1 := sys.Clients["l1"]
seqBackend := sys.Backends["sequencer"]
seqState, err := seqBackend.BlockChain().State()
require.Nil(t, err, "Error getting sequencer state")
l1CostFn := types.NewL1CostFunc(seqBackend.BlockChain().Config(), seqState)
// Transactor Account // Transactor Account
ethPrivKey := cfg.Secrets.Alice ethPrivKey := cfg.Secrets.Alice
fromAddr := crypto.PubkeyToAddress(ethPrivKey.PublicKey) fromAddr := crypto.PubkeyToAddress(ethPrivKey.PublicKey)
require.NotEqual(t, cfg.DeployConfig.L2OutputOracleProposer, fromAddr)
require.NotEqual(t, cfg.DeployConfig.BatchSenderAddress, fromAddr)
// Find gaspriceoracle contract // Find gaspriceoracle contract
gpoContract, err := bindings.NewGasPriceOracle(predeploys.GasPriceOracleAddr, l2Seq) gpoContract, err := bindings.NewGasPriceOracle(predeploys.GasPriceOracleAddr, l2Seq)
require.Nil(t, err) require.Nil(t, err)
...@@ -1132,29 +1125,33 @@ func TestFees(t *testing.T) { ...@@ -1132,29 +1125,33 @@ func TestFees(t *testing.T) {
scalar, err := gpoContract.Scalar(&bind.CallOpts{}) scalar, err := gpoContract.Scalar(&bind.CallOpts{})
require.Nil(t, err, "reading gpo scalar") require.Nil(t, err, "reading gpo scalar")
require.Equal(t, overhead.Uint64(), uint64(2100), "wrong gpo overhead") require.Equal(t, overhead.Uint64(), cfg.DeployConfig.GasPriceOracleOverhead, "wrong gpo overhead")
require.Equal(t, decimals.Uint64(), uint64(6), "wrong gpo decimals") require.Equal(t, decimals.Uint64(), uint64(6), "wrong gpo decimals")
require.Equal(t, scalar.Uint64(), uint64(1_000_000), "wrong gpo scalar") require.Equal(t, scalar.Uint64(), cfg.DeployConfig.GasPriceOracleScalar, "wrong gpo scalar")
// BaseFee Recipient // BaseFee Recipient
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) baseFeeRecipientStartBalance, err := l2Seq.BalanceAt(context.Background(), predeploys.BaseFeeVaultAddr, big.NewInt(rpc.EarliestBlockNumber.Int64()))
defer cancel()
baseFeeRecipientStartBalance, err := l2Seq.BalanceAt(ctx, predeploys.BaseFeeVaultAddr, nil)
require.Nil(t, err) require.Nil(t, err)
// L1Fee Recipient // L1Fee Recipient
ctx, cancel = context.WithTimeout(context.Background(), 1*time.Second) l1FeeRecipientStartBalance, err := l2Seq.BalanceAt(context.Background(), predeploys.L1FeeVaultAddr, big.NewInt(rpc.EarliestBlockNumber.Int64()))
defer cancel() require.Nil(t, err)
l1FeeRecipientStartBalance, err := l2Seq.BalanceAt(ctx, predeploys.L1FeeVaultAddr, nil)
sequencerFeeVaultStartBalance, err := l2Seq.BalanceAt(context.Background(), predeploys.SequencerFeeVaultAddr, big.NewInt(rpc.EarliestBlockNumber.Int64()))
require.Nil(t, err) require.Nil(t, err)
genesisBlock, err := l2Seq.BlockByNumber(context.Background(), big.NewInt(rpc.EarliestBlockNumber.Int64()))
require.NoError(t, err)
coinbaseStartBalance, err := l2Seq.BalanceAt(context.Background(), genesisBlock.Coinbase(), big.NewInt(rpc.EarliestBlockNumber.Int64()))
require.NoError(t, err)
// Simple transfer from signer to random account // Simple transfer from signer to random account
ctx, cancel = context.WithTimeout(context.Background(), 1*time.Second) startBalance, err := l2Seq.BalanceAt(context.Background(), fromAddr, big.NewInt(rpc.EarliestBlockNumber.Int64()))
defer cancel()
startBalance, err := l2Verif.BalanceAt(ctx, fromAddr, nil)
require.Nil(t, err) require.Nil(t, err)
require.Greater(t, startBalance.Uint64(), big.NewInt(params.Ether).Uint64())
transferAmount := big.NewInt(1_000_000_000) transferAmount := big.NewInt(params.Ether)
gasTip := big.NewInt(10) gasTip := big.NewInt(10)
receipt := SendL2Tx(t, cfg, l2Seq, ethPrivKey, func(opts *TxOpts) { receipt := SendL2Tx(t, cfg, l2Seq, ethPrivKey, func(opts *TxOpts) {
opts.ToAddr = &common.Address{0xff, 0xff} opts.ToAddr = &common.Address{0xff, 0xff}
...@@ -1165,69 +1162,57 @@ func TestFees(t *testing.T) { ...@@ -1165,69 +1162,57 @@ func TestFees(t *testing.T) {
opts.VerifyOnClients(l2Verif) opts.VerifyOnClients(l2Verif)
}) })
ctx, cancel = context.WithTimeout(context.Background(), 1*time.Second) require.Equal(t, receipt.Status, types.ReceiptStatusSuccessful)
defer cancel()
header, err := l2Seq.HeaderByNumber(ctx, receipt.BlockNumber) header, err := l2Seq.HeaderByNumber(context.Background(), receipt.BlockNumber)
require.Nil(t, err) require.Nil(t, err)
ctx, cancel = context.WithTimeout(context.Background(), 1*time.Second) coinbaseEndBalance, err := l2Seq.BalanceAt(context.Background(), header.Coinbase, header.Number)
defer cancel()
coinbaseStartBalance, err := l2Seq.BalanceAt(ctx, header.Coinbase, safeAddBig(header.Number, big.NewInt(-1)))
require.Nil(t, err) require.Nil(t, err)
ctx, cancel = context.WithTimeout(context.Background(), 1*time.Second) endBalance, err := l2Seq.BalanceAt(context.Background(), fromAddr, header.Number)
defer cancel()
coinbaseEndBalance, err := l2Seq.BalanceAt(ctx, header.Coinbase, header.Number)
require.Nil(t, err) require.Nil(t, err)
ctx, cancel = context.WithTimeout(context.Background(), 1*time.Second) baseFeeRecipientEndBalance, err := l2Seq.BalanceAt(context.Background(), predeploys.BaseFeeVaultAddr, header.Number)
defer cancel()
endBalance, err := l2Seq.BalanceAt(ctx, fromAddr, header.Number)
require.Nil(t, err) require.Nil(t, err)
ctx, cancel = context.WithTimeout(context.Background(), 1*time.Second) l1Header, err := l1.HeaderByNumber(context.Background(), nil)
defer cancel()
baseFeeRecipientEndBalance, err := l2Seq.BalanceAt(ctx, predeploys.BaseFeeVaultAddr, header.Number)
require.Nil(t, err) require.Nil(t, err)
l1Header, err := sys.Clients["l1"].HeaderByNumber(ctx, nil) l1FeeRecipientEndBalance, err := l2Seq.BalanceAt(context.Background(), predeploys.L1FeeVaultAddr, header.Number)
require.Nil(t, err) require.Nil(t, err)
ctx, cancel = context.WithTimeout(context.Background(), 1*time.Second) sequencerFeeVaultEndBalance, err := l2Seq.BalanceAt(context.Background(), predeploys.SequencerFeeVaultAddr, header.Number)
defer cancel()
l1FeeRecipientEndBalance, err := l2Seq.BalanceAt(ctx, predeploys.L1FeeVaultAddr, header.Number)
require.Nil(t, err) require.Nil(t, err)
// Diff fee recipient + coinbase balances // Diff fee recipient + coinbase balances
baseFeeRecipientDiff := new(big.Int).Sub(baseFeeRecipientEndBalance, baseFeeRecipientStartBalance) baseFeeRecipientDiff := new(big.Int).Sub(baseFeeRecipientEndBalance, baseFeeRecipientStartBalance)
l1FeeRecipientDiff := new(big.Int).Sub(l1FeeRecipientEndBalance, l1FeeRecipientStartBalance) l1FeeRecipientDiff := new(big.Int).Sub(l1FeeRecipientEndBalance, l1FeeRecipientStartBalance)
sequencerFeeVaultDiff := new(big.Int).Sub(sequencerFeeVaultEndBalance, sequencerFeeVaultStartBalance)
coinbaseDiff := new(big.Int).Sub(coinbaseEndBalance, coinbaseStartBalance) coinbaseDiff := new(big.Int).Sub(coinbaseEndBalance, coinbaseStartBalance)
// Tally L2 Fee // Tally L2 Fee
l2Fee := gasTip.Mul(gasTip, new(big.Int).SetUint64(receipt.GasUsed)) l2Fee := gasTip.Mul(gasTip, new(big.Int).SetUint64(receipt.GasUsed))
require.Equal(t, sequencerFeeVaultDiff, coinbaseDiff, "coinbase is always sequencer fee vault")
require.Equal(t, l2Fee, coinbaseDiff, "l2 fee mismatch") require.Equal(t, l2Fee, coinbaseDiff, "l2 fee mismatch")
require.Equal(t, l2Fee, sequencerFeeVaultDiff)
// Tally BaseFee // Tally BaseFee
baseFee := new(big.Int).Mul(header.BaseFee, new(big.Int).SetUint64(receipt.GasUsed)) baseFee := new(big.Int).Mul(header.BaseFee, new(big.Int).SetUint64(receipt.GasUsed))
require.Equal(t, baseFee, baseFeeRecipientDiff, "base fee fee mismatch") require.Equal(t, baseFee, baseFeeRecipientDiff, "base fee fee mismatch")
// Tally L1 Fee // Tally L1 Fee
tx, _, err := l2Seq.TransactionByHash(ctx, receipt.TxHash) tx, _, err := l2Seq.TransactionByHash(context.Background(), receipt.TxHash)
require.NoError(t, err, "Should be able to get transaction") require.NoError(t, err, "Should be able to get transaction")
bytes, err := tx.MarshalBinary() bytes, err := tx.MarshalBinary()
require.Nil(t, err) require.Nil(t, err)
l1GasUsed := calcL1GasUsed(bytes, overhead)
divisor := new(big.Int).Exp(big.NewInt(10), decimals, nil)
l1Fee := new(big.Int).Mul(l1GasUsed, l1Header.BaseFee)
l1Fee = l1Fee.Mul(l1Fee, scalar)
l1Fee = l1Fee.Div(l1Fee, divisor)
require.Equal(t, l1Fee, l1FeeRecipientDiff, "l1 fee mismatch") l1Fee := l1CostFn(receipt.BlockNumber.Uint64(), header.Time, tx.RollupDataGas(), tx.IsSystemTx())
require.Equalf(t, l1Fee, l1FeeRecipientDiff, "L1 fee mismatch: start balance %v, end balance %v", l1FeeRecipientStartBalance, l1FeeRecipientEndBalance)
// Tally L1 fee against GasPriceOracle
gpoL1Fee, err := gpoContract.GetL1Fee(&bind.CallOpts{}, bytes) gpoL1Fee, err := gpoContract.GetL1Fee(&bind.CallOpts{}, bytes)
require.Nil(t, err) require.Nil(t, err)
require.Equal(t, l1Fee, gpoL1Fee, "l1 fee mismatch") require.Equal(t, l1Fee, gpoL1Fee, "GPO reports L1 fee mismatch")
require.Equal(t, receipt.L1Fee, l1Fee, "l1 fee in receipt is correct") require.Equal(t, receipt.L1Fee, l1Fee, "l1 fee in receipt is correct")
require.Equal(t, require.Equal(t,
...@@ -1368,10 +1353,6 @@ func TestBatcherMultiTx(t *testing.T) { ...@@ -1368,10 +1353,6 @@ func TestBatcherMultiTx(t *testing.T) {
t.Fatal("Expected at least 10 transactions from the batcher") t.Fatal("Expected at least 10 transactions from the batcher")
} }
func safeAddBig(a *big.Int, b *big.Int) *big.Int {
return new(big.Int).Add(a, b)
}
func latestBlock(t *testing.T, client *ethclient.Client) uint64 { func latestBlock(t *testing.T, client *ethclient.Client) uint64 {
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel() defer cancel()
......
...@@ -52,7 +52,7 @@ func TestGasPriceOracleFeeUpdates(t *testing.T) { ...@@ -52,7 +52,7 @@ func TestGasPriceOracleFeeUpdates(t *testing.T) {
ethPrivKey := cfg.Secrets.SysCfgOwner ethPrivKey := cfg.Secrets.SysCfgOwner
// Bind to the SystemConfig & GasPriceOracle contracts // Bind to the SystemConfig & GasPriceOracle contracts
sysconfig, err := bindings.NewSystemConfig(predeploys.DevSystemConfigAddr, l1Client) sysconfig, err := bindings.NewSystemConfig(cfg.L1Deployments.SystemConfigProxy, l1Client)
require.Nil(t, err) require.Nil(t, err)
gpoContract, err := bindings.NewGasPriceOracleCaller(predeploys.GasPriceOracleAddr, l2Seq) gpoContract, err := bindings.NewGasPriceOracleCaller(predeploys.GasPriceOracleAddr, l2Seq)
require.Nil(t, err) require.Nil(t, err)
...@@ -247,7 +247,7 @@ func TestMixedDepositValidity(t *testing.T) { ...@@ -247,7 +247,7 @@ func TestMixedDepositValidity(t *testing.T) {
txTimeoutDuration := 10 * time.Duration(cfg.DeployConfig.L1BlockTime) * time.Second txTimeoutDuration := 10 * time.Duration(cfg.DeployConfig.L1BlockTime) * time.Second
// Bind to the deposit contract // Bind to the deposit contract
depositContract, err := bindings.NewOptimismPortal(predeploys.DevOptimismPortalAddr, l1Client) depositContract, err := bindings.NewOptimismPortal(cfg.L1Deployments.OptimismPortalProxy, l1Client)
require.NoError(t, err) require.NoError(t, err)
// Create a struct used to track our transactors and their transactions sent. // Create a struct used to track our transactors and their transactions sent.
...@@ -410,7 +410,9 @@ func TestMixedWithdrawalValidity(t *testing.T) { ...@@ -410,7 +410,9 @@ func TestMixedWithdrawalValidity(t *testing.T) {
// Create our system configuration, funding all accounts we created for L1/L2, and start it // Create our system configuration, funding all accounts we created for L1/L2, and start it
cfg := DefaultSystemConfig(t) cfg := DefaultSystemConfig(t)
cfg.DeployConfig.FinalizationPeriodSeconds = 6 cfg.DeployConfig.L2BlockTime = 2
require.LessOrEqual(t, cfg.DeployConfig.FinalizationPeriodSeconds, uint64(6))
require.Equal(t, cfg.DeployConfig.FundDevAccounts, true)
sys, err := cfg.Start() sys, err := cfg.Start()
require.NoError(t, err, "error starting up system") require.NoError(t, err, "error starting up system")
defer sys.Close() defer sys.Close()
...@@ -421,14 +423,37 @@ func TestMixedWithdrawalValidity(t *testing.T) { ...@@ -421,14 +423,37 @@ func TestMixedWithdrawalValidity(t *testing.T) {
l2Verif := sys.Clients["verifier"] l2Verif := sys.Clients["verifier"]
require.NoError(t, err) require.NoError(t, err)
systemConfig, err := bindings.NewSystemConfigCaller(cfg.L1Deployments.SystemConfig, l1Client)
require.NoError(t, err)
unsafeBlockSigner, err := systemConfig.UnsafeBlockSigner(nil)
require.NoError(t, err)
require.Equal(t, cfg.DeployConfig.P2PSequencerAddress, unsafeBlockSigner)
// The batcher has balance on L1
batcherBalance, err := l1Client.BalanceAt(context.Background(), cfg.DeployConfig.BatchSenderAddress, nil)
require.NoError(t, err)
require.NotEqual(t, batcherBalance, big.NewInt(0))
// The proposer has balance on L1
proposerBalance, err := l1Client.BalanceAt(context.Background(), cfg.DeployConfig.L2OutputOracleProposer, nil)
require.NoError(t, err)
require.NotEqual(t, proposerBalance, big.NewInt(0))
// Define our L1 transaction timeout duration. // Define our L1 transaction timeout duration.
txTimeoutDuration := 10 * time.Duration(cfg.DeployConfig.L1BlockTime) * time.Second txTimeoutDuration := 10 * time.Duration(cfg.DeployConfig.L1BlockTime) * time.Second
// Bind to the deposit contract // Bind to the deposit contract
depositContract, err := bindings.NewOptimismPortal(predeploys.DevOptimismPortalAddr, l1Client) depositContract, err := bindings.NewOptimismPortal(cfg.L1Deployments.OptimismPortalProxy, l1Client)
_ = depositContract _ = depositContract
require.NoError(t, err) require.NoError(t, err)
l2OutputOracle, err := bindings.NewL2OutputOracleCaller(cfg.L1Deployments.L2OutputOracleProxy, l1Client)
require.NoError(t, err)
finalizationPeriod, err := l2OutputOracle.FINALIZATIONPERIODSECONDS(nil)
require.NoError(t, err)
require.Equal(t, cfg.DeployConfig.FinalizationPeriodSeconds, finalizationPeriod.Uint64())
// Create a struct used to track our transactors and their transactions sent. // Create a struct used to track our transactors and their transactions sent.
type TestAccountState struct { type TestAccountState struct {
Account *TestAccount Account *TestAccount
...@@ -439,11 +464,10 @@ func TestMixedWithdrawalValidity(t *testing.T) { ...@@ -439,11 +464,10 @@ func TestMixedWithdrawalValidity(t *testing.T) {
} }
// Create a test account state for our transactor. // Create a test account state for our transactor.
transactorKey := cfg.Secrets.Alice
transactor := &TestAccountState{ transactor := &TestAccountState{
Account: &TestAccount{ Account: &TestAccount{
HDPath: e2eutils.DefaultMnemonicConfig.Alice, HDPath: e2eutils.DefaultMnemonicConfig.Alice,
Key: transactorKey, Key: cfg.Secrets.Alice,
L1Opts: nil, L1Opts: nil,
L2Opts: nil, L2Opts: nil,
}, },
...@@ -482,6 +506,9 @@ func TestMixedWithdrawalValidity(t *testing.T) { ...@@ -482,6 +506,9 @@ func TestMixedWithdrawalValidity(t *testing.T) {
// Determine the address our request will come from // Determine the address our request will come from
fromAddr := crypto.PubkeyToAddress(transactor.Account.Key.PublicKey) fromAddr := crypto.PubkeyToAddress(transactor.Account.Key.PublicKey)
fromBalance, err := l2Verif.BalanceAt(context.Background(), fromAddr, nil)
require.NoError(t, err)
require.Greaterf(t, fromBalance.Uint64(), uint64(700_000_000_000), "insufficient balance for %s", fromAddr)
// Initiate Withdrawal // Initiate Withdrawal
withdrawAmount := big.NewInt(500_000_000_000) withdrawAmount := big.NewInt(500_000_000_000)
...@@ -489,9 +516,18 @@ func TestMixedWithdrawalValidity(t *testing.T) { ...@@ -489,9 +516,18 @@ func TestMixedWithdrawalValidity(t *testing.T) {
tx, err := l2l1MessagePasser.InitiateWithdrawal(transactor.Account.L2Opts, fromAddr, big.NewInt(21000), nil) tx, err := l2l1MessagePasser.InitiateWithdrawal(transactor.Account.L2Opts, fromAddr, big.NewInt(21000), nil)
require.Nil(t, err, "sending initiate withdraw tx") require.Nil(t, err, "sending initiate withdraw tx")
t.Logf("Waiting for tx %s to be in sequencer", tx.Hash().Hex())
receiptSeq, err := waitForTransaction(tx.Hash(), l2Seq, txTimeoutDuration)
require.Nil(t, err, "withdrawal initiated on L2 sequencer")
require.Equal(t, receiptSeq.Status, types.ReceiptStatusSuccessful, "transaction failed")
verifierTip, err := l2Verif.BlockByNumber(context.Background(), nil)
require.Nil(t, err)
t.Logf("Waiting for tx %s to be in verifier. Verifier tip is %s:%d. Included in sequencer in block %s:%d", tx.Hash().Hex(), verifierTip.Hash().Hex(), verifierTip.NumberU64(), receiptSeq.BlockHash.Hex(), receiptSeq.BlockNumber)
// Wait for the transaction to appear in L2 verifier // Wait for the transaction to appear in L2 verifier
receipt, err := waitForTransaction(tx.Hash(), l2Verif, txTimeoutDuration) receipt, err := waitForTransaction(tx.Hash(), l2Verif, txTimeoutDuration)
require.Nil(t, err, "withdrawal initiated on L2 sequencer") require.Nilf(t, err, "withdrawal tx %s not found in verifier. included in block %s:%d", tx.Hash().Hex(), receiptSeq.BlockHash.Hex(), receiptSeq.BlockNumber)
require.Equal(t, receipt.Status, types.ReceiptStatusSuccessful, "transaction failed") require.Equal(t, receipt.Status, types.ReceiptStatusSuccessful, "transaction failed")
// Obtain the header for the block containing the transaction (used to calculate gas fees) // Obtain the header for the block containing the transaction (used to calculate gas fees)
...@@ -511,7 +547,8 @@ func TestMixedWithdrawalValidity(t *testing.T) { ...@@ -511,7 +547,8 @@ func TestMixedWithdrawalValidity(t *testing.T) {
// Wait for the finalization period, then we can finalize this withdrawal. // Wait for the finalization period, then we can finalize this withdrawal.
ctx, cancel = context.WithTimeout(context.Background(), 40*time.Duration(cfg.DeployConfig.L1BlockTime)*time.Second) ctx, cancel = context.WithTimeout(context.Background(), 40*time.Duration(cfg.DeployConfig.L1BlockTime)*time.Second)
blockNumber, err := withdrawals.WaitForOutputRootPublished(ctx, l1Client, predeploys.DevOptimismPortalAddr, receipt.BlockNumber) require.NotEqual(t, cfg.L1Deployments.L2OutputOracleProxy, common.Address{})
blockNumber, err := withdrawals.WaitForOutputRootPublished(ctx, l1Client, cfg.L1Deployments.L2OutputOracleProxy, receipt.BlockNumber)
cancel() cancel()
require.Nil(t, err) require.Nil(t, err)
...@@ -520,9 +557,6 @@ func TestMixedWithdrawalValidity(t *testing.T) { ...@@ -520,9 +557,6 @@ func TestMixedWithdrawalValidity(t *testing.T) {
cancel() cancel()
require.Nil(t, err) require.Nil(t, err)
l2OutputOracle, err := bindings.NewL2OutputOracleCaller(predeploys.DevL2OutputOracleAddr, l1Client)
require.Nil(t, err)
rpcClient, err := rpc.Dial(sys.Nodes["verifier"].WSEndpoint()) rpcClient, err := rpc.Dial(sys.Nodes["verifier"].WSEndpoint())
require.Nil(t, err) require.Nil(t, err)
proofCl := gethclient.New(rpcClient) proofCl := gethclient.New(rpcClient)
...@@ -642,7 +676,7 @@ func TestMixedWithdrawalValidity(t *testing.T) { ...@@ -642,7 +676,7 @@ func TestMixedWithdrawalValidity(t *testing.T) {
// Wait for finalization and then create the Finalized Withdrawal Transaction // Wait for finalization and then create the Finalized Withdrawal Transaction
ctx, cancel = context.WithTimeout(context.Background(), 45*time.Duration(cfg.DeployConfig.L1BlockTime)*time.Second) ctx, cancel = context.WithTimeout(context.Background(), 45*time.Duration(cfg.DeployConfig.L1BlockTime)*time.Second)
defer cancel() defer cancel()
err = withdrawals.WaitForFinalizationPeriod(ctx, l1Client, predeploys.DevOptimismPortalAddr, header.Number) err = withdrawals.WaitForFinalizationPeriod(ctx, l1Client, cfg.L1Deployments.OptimismPortalProxy, header.Number)
require.Nil(t, err) require.Nil(t, err)
// Finalize withdrawal // Finalize withdrawal
......
...@@ -8,7 +8,7 @@ import ( ...@@ -8,7 +8,7 @@ import (
"time" "time"
"github.com/ethereum-optimism/optimism/op-bindings/bindings" "github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys" "github.com/ethereum-optimism/optimism/op-e2e/config"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive" "github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
...@@ -24,8 +24,9 @@ import ( ...@@ -24,8 +24,9 @@ import (
func SendDepositTx(t *testing.T, cfg SystemConfig, l1Client *ethclient.Client, l2Client *ethclient.Client, l1Opts *bind.TransactOpts, applyL2Opts DepositTxOptsFn) { func SendDepositTx(t *testing.T, cfg SystemConfig, l1Client *ethclient.Client, l2Client *ethclient.Client, l1Opts *bind.TransactOpts, applyL2Opts DepositTxOptsFn) {
l2Opts := defaultDepositTxOpts(l1Opts) l2Opts := defaultDepositTxOpts(l1Opts)
applyL2Opts(l2Opts) applyL2Opts(l2Opts)
// Find deposit contract // Find deposit contract
depositContract, err := bindings.NewOptimismPortal(predeploys.DevOptimismPortalAddr, l1Client) depositContract, err := bindings.NewOptimismPortal(config.L1Deployments.OptimismPortalProxy, l1Client)
require.Nil(t, err) require.Nil(t, err)
// Finally send TX // Finally send TX
......
...@@ -9,6 +9,7 @@ import ( ...@@ -9,6 +9,7 @@ import (
"github.com/ethereum-optimism/optimism/op-bindings/bindings" "github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys" "github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum-optimism/optimism/op-e2e/config"
"github.com/ethereum-optimism/optimism/op-node/withdrawals" "github.com/ethereum-optimism/optimism/op-node/withdrawals"
"github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
...@@ -88,7 +89,7 @@ func ProveWithdrawal(t *testing.T, cfg SystemConfig, l1Client *ethclient.Client, ...@@ -88,7 +89,7 @@ func ProveWithdrawal(t *testing.T, cfg SystemConfig, l1Client *ethclient.Client,
ctx, cancel := context.WithTimeout(context.Background(), 40*time.Duration(cfg.DeployConfig.L1BlockTime)*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 40*time.Duration(cfg.DeployConfig.L1BlockTime)*time.Second)
defer cancel() defer cancel()
blockNumber, err := withdrawals.WaitForOutputRootPublished(ctx, l1Client, predeploys.DevOptimismPortalAddr, l2WithdrawalReceipt.BlockNumber) blockNumber, err := withdrawals.WaitForOutputRootPublished(ctx, l1Client, config.L1Deployments.L2OutputOracleProxy, l2WithdrawalReceipt.BlockNumber)
require.Nil(t, err) require.Nil(t, err)
rpcClient, err := rpc.Dial(l2Node.WSEndpoint()) rpcClient, err := rpc.Dial(l2Node.WSEndpoint())
...@@ -103,13 +104,13 @@ func ProveWithdrawal(t *testing.T, cfg SystemConfig, l1Client *ethclient.Client, ...@@ -103,13 +104,13 @@ func ProveWithdrawal(t *testing.T, cfg SystemConfig, l1Client *ethclient.Client,
require.Nil(t, err) require.Nil(t, err)
// Now create withdrawal // Now create withdrawal
oracle, err := bindings.NewL2OutputOracleCaller(predeploys.DevL2OutputOracleAddr, l1Client) oracle, err := bindings.NewL2OutputOracleCaller(config.L1Deployments.L2OutputOracleProxy, l1Client)
require.Nil(t, err) require.Nil(t, err)
params, err := withdrawals.ProveWithdrawalParameters(context.Background(), proofCl, receiptCl, l2WithdrawalReceipt.TxHash, header, oracle) params, err := withdrawals.ProveWithdrawalParameters(context.Background(), proofCl, receiptCl, l2WithdrawalReceipt.TxHash, header, oracle)
require.Nil(t, err) require.Nil(t, err)
portal, err := bindings.NewOptimismPortal(predeploys.DevOptimismPortalAddr, l1Client) portal, err := bindings.NewOptimismPortal(config.L1Deployments.OptimismPortalProxy, l1Client)
require.Nil(t, err) require.Nil(t, err)
opts, err := bind.NewKeyedTransactorWithChainID(ethPrivKey, cfg.L1ChainIDBig()) opts, err := bind.NewKeyedTransactorWithChainID(ethPrivKey, cfg.L1ChainIDBig())
...@@ -143,12 +144,13 @@ func FinalizeWithdrawal(t *testing.T, cfg SystemConfig, l1Client *ethclient.Clie ...@@ -143,12 +144,13 @@ func FinalizeWithdrawal(t *testing.T, cfg SystemConfig, l1Client *ethclient.Clie
// Wait for finalization and then create the Finalized Withdrawal Transaction // Wait for finalization and then create the Finalized Withdrawal Transaction
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Duration(cfg.DeployConfig.L1BlockTime)*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 30*time.Duration(cfg.DeployConfig.L1BlockTime)*time.Second)
defer cancel() defer cancel()
err := withdrawals.WaitForFinalizationPeriod(ctx, l1Client, predeploys.DevOptimismPortalAddr, withdrawalProofReceipt.BlockNumber)
err := withdrawals.WaitForFinalizationPeriod(ctx, l1Client, config.L1Deployments.OptimismPortalProxy, withdrawalProofReceipt.BlockNumber)
require.Nil(t, err) require.Nil(t, err)
opts, err := bind.NewKeyedTransactorWithChainID(privKey, cfg.L1ChainIDBig()) opts, err := bind.NewKeyedTransactorWithChainID(privKey, cfg.L1ChainIDBig())
require.Nil(t, err) require.Nil(t, err)
portal, err := bindings.NewOptimismPortal(predeploys.DevOptimismPortalAddr, l1Client) portal, err := bindings.NewOptimismPortal(config.L1Deployments.OptimismPortalProxy, l1Client)
require.Nil(t, err) require.Nil(t, err)
// Finalize withdrawal // Finalize withdrawal
tx, err := portal.FinalizeWithdrawalTransaction( tx, err := portal.FinalizeWithdrawalTransaction(
......
...@@ -11,6 +11,7 @@ import ( ...@@ -11,6 +11,7 @@ import (
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"github.com/ethereum/go-ethereum/core/state"
"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/ethclient"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
...@@ -22,24 +23,25 @@ import ( ...@@ -22,24 +23,25 @@ import (
var Subcommands = cli.Commands{ var Subcommands = cli.Commands{
{ {
Name: "devnet", Name: "l1",
Usage: "Initialize new L1 and L2 genesis files and rollup config suitable for a local devnet", Usage: "Generates a L1 genesis state file",
Flags: []cli.Flag{ Flags: []cli.Flag{
&cli.StringFlag{ &cli.StringFlag{
Name: "deploy-config", Name: "deploy-config",
Usage: "Path to hardhat deploy config file", Usage: "Path to hardhat deploy config file",
Required: true,
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "outfile.l1", Name: "l1-allocs",
Usage: "Path to L1 genesis output file", Usage: "Path to L1 genesis state dump",
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "outfile.l2", Name: "l1-deployments",
Usage: "Path to L2 genesis output file", Usage: "Path to L1 deployments file",
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "outfile.rollup", Name: "outfile.l1",
Usage: "Path to rollup output file", Usage: "Path to L1 genesis output file",
}, },
}, },
Action: func(ctx *cli.Context) error { Action: func(ctx *cli.Context) error {
...@@ -49,39 +51,36 @@ var Subcommands = cli.Commands{ ...@@ -49,39 +51,36 @@ var Subcommands = cli.Commands{
return err return err
} }
// Add the developer L1 addresses to the config var deployments *genesis.L1Deployments
if err := config.InitDeveloperDeployedAddresses(); err != nil { if l1Deployments := ctx.String("l1-deployments"); l1Deployments != "" {
deployments, err = genesis.NewL1Deployments(l1Deployments)
if err != nil {
return err return err
} }
}
if err := config.Check(); err != nil { if deployments != nil {
return err config.SetDeployments(deployments)
} }
l1Genesis, err := genesis.BuildL1DeveloperGenesis(config) if err := config.Check(); err != nil {
if err != nil { return fmt.Errorf("deploy config at %s invalid: %w", deployConfig, err)
return err
} }
l1StartBlock := l1Genesis.ToBlock() var dump *state.Dump
l2Genesis, err := genesis.BuildL2Genesis(config, l1StartBlock) if l1Allocs := ctx.String("l1-allocs"); l1Allocs != "" {
dump, err = genesis.NewStateDump(l1Allocs)
if err != nil { if err != nil {
return err return err
} }
}
l2GenesisBlock := l2Genesis.ToBlock() l1Genesis, err := genesis.BuildL1DeveloperGenesis(config, dump, deployments, true)
rollupConfig, err := config.RollupConfig(l1StartBlock, l2GenesisBlock.Hash(), l2GenesisBlock.Number().Uint64())
if err != nil { if err != nil {
return err return err
} }
if err := writeGenesisFile(ctx.String("outfile.l1"), l1Genesis); err != nil { return writeGenesisFile(ctx.String("outfile.l1"), l1Genesis)
return err
}
if err := writeGenesisFile(ctx.String("outfile.l2"), l2Genesis); err != nil {
return err
}
return writeGenesisFile(ctx.String("outfile.rollup"), rollupConfig)
}, },
}, },
{ {
...@@ -161,10 +160,10 @@ var Subcommands = cli.Commands{ ...@@ -161,10 +160,10 @@ var Subcommands = cli.Commands{
log.Info("Using L1 Start Block", "number", l1StartBlock.Number(), "hash", l1StartBlock.Hash().Hex()) log.Info("Using L1 Start Block", "number", l1StartBlock.Number(), "hash", l1StartBlock.Hash().Hex())
// Build the developer L2 genesis block // Build the L2 genesis block
l2Genesis, err := genesis.BuildL2Genesis(config, l1StartBlock) l2Genesis, err := genesis.BuildL2Genesis(config, l1StartBlock)
if err != nil { if err != nil {
return fmt.Errorf("error creating l2 developer genesis: %w", err) return fmt.Errorf("error creating l2 genesis: %w", err)
} }
l2GenesisBlock := l2Genesis.ToBlock() l2GenesisBlock := l2Genesis.ToBlock()
......
...@@ -403,6 +403,11 @@ func (s *EthClient) GetStorageAt(ctx context.Context, address common.Address, st ...@@ -403,6 +403,11 @@ func (s *EthClient) GetStorageAt(ctx context.Context, address common.Address, st
// ReadStorageAt is a convenience method to read a single storage value at the given slot in the given account. // ReadStorageAt is a convenience method to read a single storage value at the given slot in the given account.
// The storage slot value is verified against the state-root of the given block if we do not trust the RPC provider, or directly retrieved without proof if we do trust the RPC. // The storage slot value is verified against the state-root of the given block if we do not trust the RPC provider, or directly retrieved without proof if we do trust the RPC.
func (s *EthClient) ReadStorageAt(ctx context.Context, address common.Address, storageSlot common.Hash, blockHash common.Hash) (common.Hash, error) { func (s *EthClient) ReadStorageAt(ctx context.Context, address common.Address, storageSlot common.Hash, blockHash common.Hash) (common.Hash, error) {
// TODO: temp
if true {
return s.GetStorageAt(ctx, address, storageSlot, blockHash.String())
}
if s.trustRPC { if s.trustRPC {
return s.GetStorageAt(ctx, address, storageSlot, blockHash.String()) return s.GetStorageAt(ctx, address, storageSlot, blockHash.String())
} }
......
...@@ -26,11 +26,11 @@ var MessagePassedTopic = crypto.Keccak256Hash([]byte("MessagePassed(uint256,addr ...@@ -26,11 +26,11 @@ var MessagePassedTopic = crypto.Keccak256Hash([]byte("MessagePassed(uint256,addr
// WaitForOutputRootPublished waits until there is an output published for an L2 block number larger than the supplied l2BlockNumber // WaitForOutputRootPublished waits until there is an output published for an L2 block number larger than the supplied l2BlockNumber
// This function polls and can block for a very long time if used on mainnet. // This function polls and can block for a very long time if used on mainnet.
// This returns the block number to use for proof generation. // This returns the block number to use for proof generation.
func WaitForOutputRootPublished(ctx context.Context, client *ethclient.Client, portalAddr common.Address, l2BlockNumber *big.Int) (uint64, error) { func WaitForOutputRootPublished(ctx context.Context, client *ethclient.Client, l2OutputOracleAddr common.Address, l2BlockNumber *big.Int) (uint64, error) {
l2BlockNumber = new(big.Int).Set(l2BlockNumber) // Don't clobber caller owned l2BlockNumber l2BlockNumber = new(big.Int).Set(l2BlockNumber) // Don't clobber caller owned l2BlockNumber
opts := &bind.CallOpts{Context: ctx} opts := &bind.CallOpts{Context: ctx}
l2OO, err := createL2OOCaller(ctx, client, portalAddr) l2OO, err := bindings.NewL2OutputOracleCaller(l2OutputOracleAddr, client)
if err != nil { if err != nil {
return 0, err return 0, err
} }
......
...@@ -132,6 +132,15 @@ services: ...@@ -132,6 +132,15 @@ services:
OP_BATCHER_METRICS_ENABLED: "true" OP_BATCHER_METRICS_ENABLED: "true"
OP_BATCHER_RPC_ENABLE_ADMIN: "true" OP_BATCHER_RPC_ENABLE_ADMIN: "true"
artifact-server:
depends_on:
- l1
image: nginx:1.25-alpine
ports:
- "8080:80"
volumes:
- "${PWD}/../.devnet/:/usr/share/nginx/html/"
stateviz: stateviz:
build: build:
context: ../ context: ../
......
...@@ -7,8 +7,8 @@ GETH_CHAINDATA_DIR="$GETH_DATA_DIR/geth/chaindata" ...@@ -7,8 +7,8 @@ GETH_CHAINDATA_DIR="$GETH_DATA_DIR/geth/chaindata"
GETH_KEYSTORE_DIR="$GETH_DATA_DIR/keystore" GETH_KEYSTORE_DIR="$GETH_DATA_DIR/keystore"
GENESIS_FILE_PATH="${GENESIS_FILE_PATH:-/genesis.json}" GENESIS_FILE_PATH="${GENESIS_FILE_PATH:-/genesis.json}"
CHAIN_ID=$(cat "$GENESIS_FILE_PATH" | jq -r .config.chainId) CHAIN_ID=$(cat "$GENESIS_FILE_PATH" | jq -r .config.chainId)
BLOCK_SIGNER_PRIVATE_KEY="3e4bde571b86929bf08e2aaad9a6a1882664cd5e65b96fff7d03e1c4e6dfa15c" BLOCK_SIGNER_PRIVATE_KEY="ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"
BLOCK_SIGNER_ADDRESS="0xca062b0fd91172d89bcd4bb084ac4e21972cc467" BLOCK_SIGNER_ADDRESS="0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"
RPC_PORT="${RPC_PORT:-8545}" RPC_PORT="${RPC_PORT:-8545}"
WS_PORT="${WS_PORT:-8546}" WS_PORT="${WS_PORT:-8546}"
......
...@@ -25,3 +25,4 @@ deployments/901 ...@@ -25,3 +25,4 @@ deployments/901
deployments/hardhat deployments/hardhat
deployments/getting-started deployments/getting-started
deployments/*/.deploy deployments/*/.deploy
deployments/1337
{ {
"l1ChainID": 900, "l1ChainID": 901,
"l2ChainID": 901, "l2ChainID": 902,
"l2BlockTime": 2, "l2BlockTime": 2,
"maxSequencerDrift": 300, "maxSequencerDrift": 300,
"sequencerWindowSize": 200, "sequencerWindowSize": 200,
"channelTimeout": 120, "channelTimeout": 120,
"p2pSequencerAddress": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc", "p2pSequencerAddress": "0x9965507D1a55bcC2695C58ba16FB37d819B0A4dc",
"batchInboxAddress": "0xff00000000000000000000000000000000000000", "batchInboxAddress": "0xff00000000000000000000000000000000000901",
"batchSenderAddress": "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC", "batchSenderAddress": "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC",
"l2OutputOracleSubmissionInterval": 20, "cliqueSignerAddress": "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
"l2OutputOracleStartingTimestamp": -1, "l1UseClique": true,
"l1StartingBlockTag": "earliest",
"l2OutputOracleSubmissionInterval": 6,
"l2OutputOracleStartingTimestamp": 0,
"l2OutputOracleStartingBlockNumber": 0, "l2OutputOracleStartingBlockNumber": 0,
"l2OutputOracleProposer": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", "l2OutputOracleProposer": "0x70997970C51812dc3A010C7d01b50e0d17dc79C8",
"l2OutputOracleChallenger": "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65", "l2OutputOracleChallenger": "0x15d34AAf54267DB7D7c367839AAf71A00a2C6A65",
"l2GenesisBlockGasLimit": "0x1c9c380", "l2GenesisBlockGasLimit": "0x1c9c380",
"l1BlockTime": 3, "l1BlockTime": 3,
"cliqueSignerAddress": "0xca062b0fd91172d89bcd4bb084ac4e21972cc467", "baseFeeVaultRecipient": "0x14dC79964da2C08b23698B3D3cc7Ca32193d9955",
"baseFeeVaultRecipient": "0xBcd4042DE499D14e55001CcbB24a551F3b954096", "l1FeeVaultRecipient": "0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f",
"l1FeeVaultRecipient": "0x71bE63f3384f5fb98995898A86B02Fb2426c5788", "sequencerFeeVaultRecipient": "0xa0Ee7A142d267C1f36714E4a8F75612F20a79720",
"sequencerFeeVaultRecipient": "0xfabb0ac9d68b0b445fb7357272ff202c5651694a",
"baseFeeVaultMinimumWithdrawalAmount": "0x8ac7230489e80000", "baseFeeVaultMinimumWithdrawalAmount": "0x8ac7230489e80000",
"l1FeeVaultMinimumWithdrawalAmount": "0x8ac7230489e80000", "l1FeeVaultMinimumWithdrawalAmount": "0x8ac7230489e80000",
"sequencerFeeVaultMinimumWithdrawalAmount": "0x8ac7230489e80000", "sequencerFeeVaultMinimumWithdrawalAmount": "0x8ac7230489e80000",
"baseFeeVaultWithdrawalNetwork": 0, "baseFeeVaultWithdrawalNetwork": "remote",
"l1FeeVaultWithdrawalNetwork": 0, "l1FeeVaultWithdrawalNetwork": "remote",
"sequencerFeeVaultWithdrawalNetwork": 0, "sequencerFeeVaultWithdrawalNetwork": "remote",
"proxyAdminOwner": "0xBcd4042DE499D14e55001CcbB24a551F3b954096", "proxyAdminOwner": "0xa0Ee7A142d267C1f36714E4a8F75612F20a79720",
"finalSystemOwner": "0xBcd4042DE499D14e55001CcbB24a551F3b954096", "finalSystemOwner": "0xa0Ee7A142d267C1f36714E4a8F75612F20a79720",
"portalGuardian": "0xBcd4042DE499D14e55001CcbB24a551F3b954096", "portalGuardian": "0xa0Ee7A142d267C1f36714E4a8F75612F20a79720",
"finalizationPeriodSeconds": 2, "finalizationPeriodSeconds": 2,
"deploymentWaitConfirmations": 1,
"fundDevAccounts": true, "fundDevAccounts": true,
"l2GenesisBlockBaseFeePerGas": "0x3B9ACA00", "l2GenesisBlockBaseFeePerGas": "0x1",
"gasPriceOracleOverhead": 2100, "gasPriceOracleOverhead": 2100,
"gasPriceOracleScalar": 1000000, "gasPriceOracleScalar": 1000000,
"enableGovernance": true, "enableGovernance": true,
"governanceTokenSymbol": "OP", "governanceTokenSymbol": "OP",
"governanceTokenName": "Optimism", "governanceTokenName": "Optimism",
"governanceTokenOwner": "0xBcd4042DE499D14e55001CcbB24a551F3b954096", "governanceTokenOwner": "0xa0Ee7A142d267C1f36714E4a8F75612F20a79720",
"eip1559Denominator": 8, "eip1559Denominator": 50,
"eip1559Elasticity": 2, "eip1559Elasticity": 6,
"l1GenesisBlockTimestamp": "0x64935846", "l1GenesisBlockTimestamp": "0x64c811bf",
"l1StartingBlockTag": "earliest",
"l2GenesisRegolithTimeOffset": "0x0", "l2GenesisRegolithTimeOffset": "0x0",
"faultGameAbsolutePrestate": 96, "faultGameAbsolutePrestate": 96,
"faultGameMaxDepth": 4, "faultGameMaxDepth": 4,
......
...@@ -246,7 +246,7 @@ contract Deploy is Deployer { ...@@ -246,7 +246,7 @@ contract Deploy is Deployer {
/// @notice Deploy the DisputeGameFactoryProxy /// @notice Deploy the DisputeGameFactoryProxy
function deployDisputeGameFactoryProxy() broadcast() public returns (address) { function deployDisputeGameFactoryProxy() broadcast() public returns (address) {
if (block.chainid == 900) { if (block.chainid == 901 || block.chainid == 1337) {
address proxyAdmin = mustGetAddress("ProxyAdmin"); address proxyAdmin = mustGetAddress("ProxyAdmin");
Proxy proxy = new Proxy({ Proxy proxy = new Proxy({
_admin: proxyAdmin _admin: proxyAdmin
...@@ -347,7 +347,7 @@ contract Deploy is Deployer { ...@@ -347,7 +347,7 @@ contract Deploy is Deployer {
/// @notice Deploy the DisputeGameFactory /// @notice Deploy the DisputeGameFactory
function deployDisputeGameFactory() broadcast() public returns (address) { function deployDisputeGameFactory() broadcast() public returns (address) {
if (block.chainid == 900) { if (block.chainid == 901 || block.chainid == 1337) {
DisputeGameFactory factory = new DisputeGameFactory(); DisputeGameFactory factory = new DisputeGameFactory();
save("DisputeGameFactory", address(factory)); save("DisputeGameFactory", address(factory));
console.log("DisputeGameFactory deployed at %s", address(factory)); console.log("DisputeGameFactory deployed at %s", address(factory));
...@@ -442,7 +442,7 @@ contract Deploy is Deployer { ...@@ -442,7 +442,7 @@ contract Deploy is Deployer {
/// @notice Initialize the DisputeGameFactory /// @notice Initialize the DisputeGameFactory
function initializeDisputeGameFactory() broadcast() public { function initializeDisputeGameFactory() broadcast() public {
if (block.chainid == 900) { if (block.chainid == 901 || block.chainid == 1337) {
ProxyAdmin proxyAdmin = ProxyAdmin(mustGetAddress("ProxyAdmin")); ProxyAdmin proxyAdmin = ProxyAdmin(mustGetAddress("ProxyAdmin"));
address disputeGameFactoryProxy = mustGetAddress("DisputeGameFactoryProxy"); address disputeGameFactoryProxy = mustGetAddress("DisputeGameFactoryProxy");
address disputeGameFactory = mustGetAddress("DisputeGameFactory"); address disputeGameFactory = mustGetAddress("DisputeGameFactory");
...@@ -676,7 +676,7 @@ contract Deploy is Deployer { ...@@ -676,7 +676,7 @@ contract Deploy is Deployer {
/// @notice Transfer ownership of the DisputeGameFactory contract to the final system owner /// @notice Transfer ownership of the DisputeGameFactory contract to the final system owner
function transferDisputeGameFactoryOwnership() broadcast() public { function transferDisputeGameFactoryOwnership() broadcast() public {
if (block.chainid == 900) { if (block.chainid == 901 || block.chainid == 1337) {
DisputeGameFactory disputeGameFactory = DisputeGameFactory(mustGetAddress("DisputeGameFactoryProxy")); DisputeGameFactory disputeGameFactory = DisputeGameFactory(mustGetAddress("DisputeGameFactoryProxy"));
address owner = disputeGameFactory.owner(); address owner = disputeGameFactory.owner();
address finalSystemOwner = cfg.finalSystemOwner(); address finalSystemOwner = cfg.finalSystemOwner();
...@@ -689,7 +689,7 @@ contract Deploy is Deployer { ...@@ -689,7 +689,7 @@ contract Deploy is Deployer {
/// @notice Sets the implementation for the `FAULT` game type in the `DisputeGameFactory` /// @notice Sets the implementation for the `FAULT` game type in the `DisputeGameFactory`
function setFaultGameImplementation() broadcast() public { function setFaultGameImplementation() broadcast() public {
if (block.chainid == 900) { if (block.chainid == 901 || block.chainid == 1337) {
DisputeGameFactory factory = DisputeGameFactory(mustGetAddress("DisputeGameFactoryProxy")); DisputeGameFactory factory = DisputeGameFactory(mustGetAddress("DisputeGameFactoryProxy"));
Claim absolutePrestate = Claim.wrap(bytes32(cfg.faultGameAbsolutePrestate())); Claim absolutePrestate = Claim.wrap(bytes32(cfg.faultGameAbsolutePrestate()));
IBigStepper faultVm = IBigStepper(new AlphabetVM(absolutePrestate)); IBigStepper faultVm = IBigStepper(new AlphabetVM(absolutePrestate));
......
...@@ -87,7 +87,7 @@ contract DeployConfig is Script { ...@@ -87,7 +87,7 @@ contract DeployConfig is Script {
eip1559Denominator = stdJson.readUint(_json, "$.eip1559Denominator"); eip1559Denominator = stdJson.readUint(_json, "$.eip1559Denominator");
eip1559Elasticity = stdJson.readUint(_json, "$.eip1559Elasticity"); eip1559Elasticity = stdJson.readUint(_json, "$.eip1559Elasticity");
if (block.chainid == 900) { if (block.chainid == 901 || block.chainid == 1337) {
faultGameAbsolutePrestate = stdJson.readUint(_json, "$.faultGameAbsolutePrestate"); faultGameAbsolutePrestate = stdJson.readUint(_json, "$.faultGameAbsolutePrestate");
faultGameMaxDepth = stdJson.readUint(_json, "$.faultGameMaxDepth"); faultGameMaxDepth = stdJson.readUint(_json, "$.faultGameMaxDepth");
faultGameMaxDuration = stdJson.readUint(_json, "$.faultGameMaxDuration"); faultGameMaxDuration = stdJson.readUint(_json, "$.faultGameMaxDuration");
......
...@@ -71,19 +71,21 @@ abstract contract Deployer is Script { ...@@ -71,19 +71,21 @@ abstract contract Deployer is Script {
deploymentContext = _getDeploymentContext(); deploymentContext = _getDeploymentContext();
string memory deployFile = vm.envOr("DEPLOY_FILE", string("run-latest.json")); string memory deployFile = vm.envOr("DEPLOY_FILE", string("run-latest.json"));
deployPath = string.concat(root, "/broadcast/", deployScript, ".s.sol/", vm.toString(block.chainid), "/", deployFile); uint256 chainId = vm.envOr("CHAIN_ID", block.chainid);
deployPath = string.concat(root, "/broadcast/", deployScript, ".s.sol/", vm.toString(chainId), "/", deployFile);
deploymentsDir = string.concat(root, "/deployments/", deploymentContext); deploymentsDir = string.concat(root, "/deployments/", deploymentContext);
try vm.createDir(deploymentsDir, true) {} catch (bytes memory) {} try vm.createDir(deploymentsDir, true) {} catch (bytes memory) {}
string memory chainIdPath = string.concat(deploymentsDir, "/.chainId"); string memory chainIdPath = string.concat(deploymentsDir, "/.chainId");
try vm.readFile(chainIdPath) returns (string memory chainid) { try vm.readFile(chainIdPath) returns (string memory localChainId) {
uint256 chainId = vm.parseUint(chainid); if (vm.envOr("STRICT_DEPLOYMENT", true)) {
require(chainId == block.chainid, "Misconfigured networks"); require(vm.parseUint(localChainId) == chainId, "Misconfigured networks");
}
} catch { } catch {
vm.writeFile(chainIdPath, vm.toString(block.chainid)); vm.writeFile(chainIdPath, vm.toString(chainId));
} }
console.log("Connected to network with chainid %s", block.chainid); console.log("Connected to network with chainid %s", chainId);
tempDeploymentsPath = string.concat(deploymentsDir, "/.deploy"); tempDeploymentsPath = string.concat(deploymentsDir, "/.deploy");
try vm.readFile(tempDeploymentsPath) returns (string memory) {} catch { try vm.readFile(tempDeploymentsPath) returns (string memory) {} catch {
...@@ -432,7 +434,7 @@ abstract contract Deployer is Script { ...@@ -432,7 +434,7 @@ abstract contract Deployer is Script {
return context; return context;
} }
uint256 chainid = block.chainid; uint256 chainid = vm.envOr("CHAIN_ID", block.chainid);
if (chainid == 1) { if (chainid == 1) {
return "mainnet"; return "mainnet";
} else if (chainid == 5) { } else if (chainid == 5) {
...@@ -441,7 +443,7 @@ abstract contract Deployer is Script { ...@@ -441,7 +443,7 @@ abstract contract Deployer is Script {
return "optimism-goerli"; return "optimism-goerli";
} else if (chainid == 10) { } else if (chainid == 10) {
return "optimism-mainnet"; return "optimism-mainnet";
} else if (chainid == 900) { } else if (chainid == 901 || chainid == 1337) {
return "devnetL1"; return "devnetL1";
} else if (chainid == 31337) { } else if (chainid == 31337) {
return "hardhat"; return "hardhat";
......
...@@ -16,7 +16,7 @@ export enum L1ChainID { ...@@ -16,7 +16,7 @@ export enum L1ChainID {
MAINNET = 1, MAINNET = 1,
GOERLI = 5, GOERLI = 5,
HARDHAT_LOCAL = 31337, HARDHAT_LOCAL = 31337,
BEDROCK_LOCAL_DEVNET = 900, BEDROCK_LOCAL_DEVNET = 901,
} }
/** /**
...@@ -27,7 +27,6 @@ export enum L2ChainID { ...@@ -27,7 +27,6 @@ export enum L2ChainID {
OPTIMISM_GOERLI = 420, OPTIMISM_GOERLI = 420,
OPTIMISM_HARDHAT_LOCAL = 31337, OPTIMISM_HARDHAT_LOCAL = 31337,
OPTIMISM_HARDHAT_DEVNET = 17, OPTIMISM_HARDHAT_DEVNET = 17,
OPTIMISM_BEDROCK_LOCAL_DEVNET = 901,
OPTIMISM_BEDROCK_ALPHA_TESTNET = 28528, OPTIMISM_BEDROCK_ALPHA_TESTNET = 28528,
BASE_GOERLI = 84531, BASE_GOERLI = 84531,
BASE_MAINNET = 8453, BASE_MAINNET = 8453,
......
...@@ -69,7 +69,6 @@ export const DEPOSIT_CONFIRMATION_BLOCKS: { ...@@ -69,7 +69,6 @@ export const DEPOSIT_CONFIRMATION_BLOCKS: {
[L2ChainID.OPTIMISM_GOERLI]: 12 as const, [L2ChainID.OPTIMISM_GOERLI]: 12 as const,
[L2ChainID.OPTIMISM_HARDHAT_LOCAL]: 2 as const, [L2ChainID.OPTIMISM_HARDHAT_LOCAL]: 2 as const,
[L2ChainID.OPTIMISM_HARDHAT_DEVNET]: 2 as const, [L2ChainID.OPTIMISM_HARDHAT_DEVNET]: 2 as const,
[L2ChainID.OPTIMISM_BEDROCK_LOCAL_DEVNET]: 2 as const,
[L2ChainID.OPTIMISM_BEDROCK_ALPHA_TESTNET]: 12 as const, [L2ChainID.OPTIMISM_BEDROCK_ALPHA_TESTNET]: 12 as const,
[L2ChainID.BASE_GOERLI]: 12 as const, [L2ChainID.BASE_GOERLI]: 12 as const,
[L2ChainID.BASE_MAINNET]: 50 as const, [L2ChainID.BASE_MAINNET]: 50 as const,
...@@ -171,22 +170,6 @@ export const CONTRACT_ADDRESSES: { ...@@ -171,22 +170,6 @@ export const CONTRACT_ADDRESSES: {
}, },
l2: DEFAULT_L2_CONTRACT_ADDRESSES, l2: DEFAULT_L2_CONTRACT_ADDRESSES,
}, },
[L2ChainID.OPTIMISM_BEDROCK_LOCAL_DEVNET]: {
l1: {
AddressManager: '0x6900000000000000000000000000000000000005' as const,
L1CrossDomainMessenger:
'0x6900000000000000000000000000000000000002' as const,
L1StandardBridge: '0x6900000000000000000000000000000000000003' as const,
StateCommitmentChain:
'0x0000000000000000000000000000000000000000' as const,
CanonicalTransactionChain:
'0x0000000000000000000000000000000000000000' as const,
BondManager: '0x0000000000000000000000000000000000000000' as const,
OptimismPortal: '0x6900000000000000000000000000000000000001' as const,
L2OutputOracle: '0x6900000000000000000000000000000000000000' as const,
},
l2: DEFAULT_L2_CONTRACT_ADDRESSES,
},
[L2ChainID.OPTIMISM_BEDROCK_ALPHA_TESTNET]: { [L2ChainID.OPTIMISM_BEDROCK_ALPHA_TESTNET]: {
l1: { l1: {
AddressManager: '0xb4e08DcE1F323608229265c9d4125E22a4B9dbAF' as const, AddressManager: '0xb4e08DcE1F323608229265c9d4125E22a4B9dbAF' as const,
......
...@@ -4,7 +4,7 @@ import { task, types } from 'hardhat/config' ...@@ -4,7 +4,7 @@ import { task, types } from 'hardhat/config'
import { HardhatRuntimeEnvironment } from 'hardhat/types' import { HardhatRuntimeEnvironment } from 'hardhat/types'
import '@nomiclabs/hardhat-ethers' import '@nomiclabs/hardhat-ethers'
import 'hardhat-deploy' import 'hardhat-deploy'
import { Event, Contract, Wallet, providers, utils } from 'ethers' import { Event, Contract, Wallet, providers, utils, ethers } from 'ethers'
import { predeploys, sleep } from '@eth-optimism/core-utils' import { predeploys, sleep } from '@eth-optimism/core-utils'
import Artifact__WETH9 from '@eth-optimism/contracts-bedrock/forge-artifacts/WETH9.sol/WETH9.json' import Artifact__WETH9 from '@eth-optimism/contracts-bedrock/forge-artifacts/WETH9.sol/WETH9.json'
import Artifact__OptimismMintableERC20TokenFactory from '@eth-optimism/contracts-bedrock/forge-artifacts/OptimismMintableERC20Factory.sol/OptimismMintableERC20Factory.json' import Artifact__OptimismMintableERC20TokenFactory from '@eth-optimism/contracts-bedrock/forge-artifacts/OptimismMintableERC20Factory.sol/OptimismMintableERC20Factory.json'
...@@ -15,6 +15,7 @@ import Artifact__L2StandardBridge from '@eth-optimism/contracts-bedrock/forge-ar ...@@ -15,6 +15,7 @@ import Artifact__L2StandardBridge from '@eth-optimism/contracts-bedrock/forge-ar
import Artifact__OptimismPortal from '@eth-optimism/contracts-bedrock/forge-artifacts/OptimismPortal.sol/OptimismPortal.json' import Artifact__OptimismPortal from '@eth-optimism/contracts-bedrock/forge-artifacts/OptimismPortal.sol/OptimismPortal.json'
import Artifact__L1CrossDomainMessenger from '@eth-optimism/contracts-bedrock/forge-artifacts/L1CrossDomainMessenger.sol/L1CrossDomainMessenger.json' import Artifact__L1CrossDomainMessenger from '@eth-optimism/contracts-bedrock/forge-artifacts/L1CrossDomainMessenger.sol/L1CrossDomainMessenger.json'
import Artifact__L1StandardBridge from '@eth-optimism/contracts-bedrock/forge-artifacts/L1StandardBridge.sol/L1StandardBridge.json' import Artifact__L1StandardBridge from '@eth-optimism/contracts-bedrock/forge-artifacts/L1StandardBridge.sol/L1StandardBridge.json'
import Artifact__L2OutputOracle from '@eth-optimism/contracts-bedrock/forge-artifacts/L2OutputOracle.sol/L2OutputOracle.json'
import { import {
CrossChainMessenger, CrossChainMessenger,
...@@ -144,30 +145,51 @@ task('deposit-erc20', 'Deposits WETH9 onto L2.') ...@@ -144,30 +145,51 @@ task('deposit-erc20', 'Deposits WETH9 onto L2.')
let contractAddrs = CONTRACT_ADDRESSES[l2ChainId] let contractAddrs = CONTRACT_ADDRESSES[l2ChainId]
if (args.l1ContractsJsonPath) { if (args.l1ContractsJsonPath) {
const data = await fs.readFile(args.l1ContractsJsonPath) const data = await fs.readFile(args.l1ContractsJsonPath)
const json = JSON.parse(data.toString())
contractAddrs = { contractAddrs = {
l1: JSON.parse(data.toString()), l1: {
AddressManager: json.AddressManager,
L1CrossDomainMessenger: json.L1CrossDomainMessengerProxy,
L1StandardBridge: json.L1StandardBridgeProxy,
StateCommitmentChain: ethers.constants.AddressZero,
CanonicalTransactionChain: ethers.constants.AddressZero,
BondManager: ethers.constants.AddressZero,
OptimismPortal: json.OptimismPortalProxy,
L2OutputOracle: json.L2OutputOracleProxy,
},
l2: DEFAULT_L2_CONTRACT_ADDRESSES, l2: DEFAULT_L2_CONTRACT_ADDRESSES,
} as OEContractsLike } as OEContractsLike
} }
console.log(`OptimismPortal: ${contractAddrs.l1.OptimismPortal}`)
const OptimismPortal = new hre.ethers.Contract( const OptimismPortal = new hre.ethers.Contract(
contractAddrs.l1.OptimismPortal, contractAddrs.l1.OptimismPortal,
Artifact__OptimismPortal.abi, Artifact__OptimismPortal.abi,
signer signer
) )
console.log(
`L1CrossDomainMessenger: ${contractAddrs.l1.L1CrossDomainMessenger}`
)
const L1CrossDomainMessenger = new hre.ethers.Contract( const L1CrossDomainMessenger = new hre.ethers.Contract(
contractAddrs.l1.L1CrossDomainMessenger, contractAddrs.l1.L1CrossDomainMessenger,
Artifact__L1CrossDomainMessenger.abi, Artifact__L1CrossDomainMessenger.abi,
signer signer
) )
console.log(`L1StandardBridge: ${contractAddrs.l1.L1StandardBridge}`)
const L1StandardBridge = new hre.ethers.Contract( const L1StandardBridge = new hre.ethers.Contract(
contractAddrs.l1.L1StandardBridge, contractAddrs.l1.L1StandardBridge,
Artifact__L1StandardBridge.abi, Artifact__L1StandardBridge.abi,
signer signer
) )
const L2OutputOracle = new hre.ethers.Contract(
contractAddrs.l1.L2OutputOracle,
Artifact__L2OutputOracle.abi,
signer
)
const L2ToL1MessagePasser = new hre.ethers.Contract( const L2ToL1MessagePasser = new hre.ethers.Contract(
predeploys.L2ToL1MessagePasser, predeploys.L2ToL1MessagePasser,
Artifact__L2ToL1MessagePasser.abi Artifact__L2ToL1MessagePasser.abi
...@@ -192,6 +214,10 @@ task('deposit-erc20', 'Deposits WETH9 onto L2.') ...@@ -192,6 +214,10 @@ task('deposit-erc20', 'Deposits WETH9 onto L2.')
contracts: contractAddrs, contracts: contractAddrs,
}) })
const params = await OptimismPortal.params()
console.log('Intial OptimismPortal.params:')
console.log(params)
console.log('Deploying WETH9 to L1') console.log('Deploying WETH9 to L1')
const WETH9 = await deployWETH9(hre, true) const WETH9 = await deployWETH9(hre, true)
console.log(`Deployed to ${WETH9.address}`) console.log(`Deployed to ${WETH9.address}`)
...@@ -221,33 +247,37 @@ task('deposit-erc20', 'Deposits WETH9 onto L2.') ...@@ -221,33 +247,37 @@ task('deposit-erc20', 'Deposits WETH9 onto L2.')
await depositTx.wait() await depositTx.wait()
console.log(`ERC20 deposited - ${depositTx.hash}`) console.log(`ERC20 deposited - ${depositTx.hash}`)
// Deposit might get reorged, wait 30s and also log for reorgs. console.log('Checking to make sure deposit was successful')
// Deposit might get reorged, wait and also log for reorgs.
let prevBlockHash: string = '' let prevBlockHash: string = ''
for (let i = 0; i < 30; i++) { for (let i = 0; i < 12; i++) {
const messageReceipt = await messenger.waitForMessageReceipt(depositTx) const messageReceipt = await signer.provider!.getTransactionReceipt(
if (messageReceipt.receiptStatus !== 1) { depositTx.hash
)
if (messageReceipt.status !== 1) {
console.log(`Deposit failed, retrying...`) console.log(`Deposit failed, retrying...`)
} }
if ( // Wait for stability, we want some amount of time after any reorg
prevBlockHash !== '' && if (prevBlockHash !== '' && messageReceipt.blockHash !== prevBlockHash) {
messageReceipt.transactionReceipt.blockHash !== prevBlockHash
) {
console.log( console.log(
`Block hash changed from ${prevBlockHash} to ${messageReceipt.transactionReceipt.blockHash}` `Block hash changed from ${prevBlockHash} to ${messageReceipt.blockHash}`
) )
// Wait for stability, we want at least 30 seconds after any reorg
i = 0 i = 0
} else if (prevBlockHash !== '') {
console.log(`No reorg detected: ${i}`)
} }
prevBlockHash = messageReceipt.transactionReceipt.blockHash prevBlockHash = messageReceipt.blockHash
await sleep(1000) await sleep(1000)
} }
console.log(`Deposit confirmed`)
const l2Balance = await OptimismMintableERC20.balanceOf(address) const l2Balance = await OptimismMintableERC20.balanceOf(address)
if (l2Balance.lt(utils.parseEther('1'))) { if (l2Balance.lt(utils.parseEther('1'))) {
throw new Error('bad deposit') throw new Error(
`bad deposit. recipient balance on L2: ${utils.formatEther(l2Balance)}`
)
} }
console.log(`Deposit success`) console.log(`Deposit success`)
...@@ -291,6 +321,8 @@ task('deposit-erc20', 'Deposits WETH9 onto L2.') ...@@ -291,6 +321,8 @@ task('deposit-erc20', 'Deposits WETH9 onto L2.')
setInterval(async () => { setInterval(async () => {
const currentStatus = await messenger.getMessageStatus(withdraw) const currentStatus = await messenger.getMessageStatus(withdraw)
console.log(`Message status: ${MessageStatus[currentStatus]}`) console.log(`Message status: ${MessageStatus[currentStatus]}`)
const latest = await L2OutputOracle.latestBlockNumber()
console.log(`Latest L2OutputOracle commitment number: ${latest.toString()}`)
}, 3000) }, 3000)
const now = Math.floor(Date.now() / 1000) const now = Math.floor(Date.now() / 1000)
......
...@@ -12,6 +12,7 @@ import Artifact__L2StandardBridge from '@eth-optimism/contracts-bedrock/forge-ar ...@@ -12,6 +12,7 @@ import Artifact__L2StandardBridge from '@eth-optimism/contracts-bedrock/forge-ar
import Artifact__OptimismPortal from '@eth-optimism/contracts-bedrock/forge-artifacts/OptimismPortal.sol/OptimismPortal.json' import Artifact__OptimismPortal from '@eth-optimism/contracts-bedrock/forge-artifacts/OptimismPortal.sol/OptimismPortal.json'
import Artifact__L1CrossDomainMessenger from '@eth-optimism/contracts-bedrock/forge-artifacts/L1CrossDomainMessenger.sol/L1CrossDomainMessenger.json' import Artifact__L1CrossDomainMessenger from '@eth-optimism/contracts-bedrock/forge-artifacts/L1CrossDomainMessenger.sol/L1CrossDomainMessenger.json'
import Artifact__L1StandardBridge from '@eth-optimism/contracts-bedrock/forge-artifacts/L1StandardBridge.sol/L1StandardBridge.json' import Artifact__L1StandardBridge from '@eth-optimism/contracts-bedrock/forge-artifacts/L1StandardBridge.sol/L1StandardBridge.json'
import Artifact__L2OutputOracle from '@eth-optimism/contracts-bedrock/forge-artifacts/L2OutputOracle.sol/L2OutputOracle.json'
import { import {
CrossChainMessenger, CrossChainMessenger,
...@@ -88,8 +89,18 @@ task('deposit-eth', 'Deposits ether to L2.') ...@@ -88,8 +89,18 @@ task('deposit-eth', 'Deposits ether to L2.')
let contractAddrs = CONTRACT_ADDRESSES[l2ChainId] let contractAddrs = CONTRACT_ADDRESSES[l2ChainId]
if (args.l1ContractsJsonPath) { if (args.l1ContractsJsonPath) {
const data = await fs.readFile(args.l1ContractsJsonPath) const data = await fs.readFile(args.l1ContractsJsonPath)
const json = JSON.parse(data.toString())
contractAddrs = { contractAddrs = {
l1: JSON.parse(data.toString()), l1: {
AddressManager: json.AddressManager,
L1CrossDomainMessenger: json.L1CrossDomainMessengerProxy,
L1StandardBridge: json.L1StandardBridgeProxy,
StateCommitmentChain: ethers.constants.AddressZero,
CanonicalTransactionChain: ethers.constants.AddressZero,
BondManager: ethers.constants.AddressZero,
OptimismPortal: json.OptimismPortalProxy,
L2OutputOracle: json.L2OutputOracleProxy,
},
l2: DEFAULT_L2_CONTRACT_ADDRESSES, l2: DEFAULT_L2_CONTRACT_ADDRESSES,
} as OEContractsLike } as OEContractsLike
} else if (!contractAddrs) { } else if (!contractAddrs) {
...@@ -145,24 +156,36 @@ task('deposit-eth', 'Deposits ether to L2.') ...@@ -145,24 +156,36 @@ task('deposit-eth', 'Deposits ether to L2.')
} }
} }
console.log(`OptimismPortal: ${contractAddrs.l1.OptimismPortal}`)
const OptimismPortal = new hre.ethers.Contract( const OptimismPortal = new hre.ethers.Contract(
contractAddrs.l1.OptimismPortal, contractAddrs.l1.OptimismPortal,
Artifact__OptimismPortal.abi, Artifact__OptimismPortal.abi,
signer signer
) )
console.log(
`L1CrossDomainMessenger: ${contractAddrs.l1.L1CrossDomainMessenger}`
)
const L1CrossDomainMessenger = new hre.ethers.Contract( const L1CrossDomainMessenger = new hre.ethers.Contract(
contractAddrs.l1.L1CrossDomainMessenger, contractAddrs.l1.L1CrossDomainMessenger,
Artifact__L1CrossDomainMessenger.abi, Artifact__L1CrossDomainMessenger.abi,
signer signer
) )
console.log(`L1StandardBridge: ${contractAddrs.l1.L1StandardBridge}`)
const L1StandardBridge = new hre.ethers.Contract( const L1StandardBridge = new hre.ethers.Contract(
contractAddrs.l1.L1StandardBridge, contractAddrs.l1.L1StandardBridge,
Artifact__L1StandardBridge.abi, Artifact__L1StandardBridge.abi,
signer signer
) )
console.log(`L2OutputOracle: ${contractAddrs.l1.L2OutputOracle}`)
const L2OutputOracle = new hre.ethers.Contract(
contractAddrs.l1.L2OutputOracle,
Artifact__L2OutputOracle.abi,
signer
)
const L2ToL1MessagePasser = new hre.ethers.Contract( const L2ToL1MessagePasser = new hre.ethers.Contract(
predeploys.L2ToL1MessagePasser, predeploys.L2ToL1MessagePasser,
Artifact__L2ToL1MessagePasser.abi Artifact__L2ToL1MessagePasser.abi
...@@ -187,11 +210,11 @@ task('deposit-eth', 'Deposits ether to L2.') ...@@ -187,11 +210,11 @@ task('deposit-eth', 'Deposits ether to L2.')
contracts: contractAddrs, contracts: contractAddrs,
}) })
const opBalanceBefore = await signer.provider.getBalance( const opBalanceBefore = await signer!.provider!.getBalance(
OptimismPortal.address OptimismPortal.address
) )
const l1BridgeBalanceBefore = await signer.provider.getBalance( const l1BridgeBalanceBefore = await signer!.provider!.getBalance(
L1StandardBridge.address L1StandardBridge.address
) )
...@@ -210,11 +233,11 @@ task('deposit-eth', 'Deposits ether to L2.') ...@@ -210,11 +233,11 @@ task('deposit-eth', 'Deposits ether to L2.')
`Deposit complete - included in block ${depositMessageReceipt.transactionReceipt.blockNumber}` `Deposit complete - included in block ${depositMessageReceipt.transactionReceipt.blockNumber}`
) )
const opBalanceAfter = await signer.provider.getBalance( const opBalanceAfter = await signer!.provider!.getBalance(
OptimismPortal.address OptimismPortal.address
) )
const l1BridgeBalanceAfter = await signer.provider.getBalance( const l1BridgeBalanceAfter = await signer!.provider!.getBalance(
L1StandardBridge.address L1StandardBridge.address
) )
...@@ -291,6 +314,8 @@ task('deposit-eth', 'Deposits ether to L2.') ...@@ -291,6 +314,8 @@ task('deposit-eth', 'Deposits ether to L2.')
const proveInterval = setInterval(async () => { const proveInterval = setInterval(async () => {
const currentStatus = await messenger.getMessageStatus(ethWithdrawReceipt) const currentStatus = await messenger.getMessageStatus(ethWithdrawReceipt)
console.log(`Message status: ${MessageStatus[currentStatus]}`) console.log(`Message status: ${MessageStatus[currentStatus]}`)
const latest = await L2OutputOracle.latestBlockNumber()
console.log(`Latest L2OutputOracle commitment number: ${latest.toString()}`)
}, 3000) }, 3000)
try { try {
...@@ -394,7 +419,7 @@ task('deposit-eth', 'Deposits ether to L2.') ...@@ -394,7 +419,7 @@ task('deposit-eth', 'Deposits ether to L2.')
} }
} }
const opBalanceFinally = await signer.provider.getBalance( const opBalanceFinally = await signer!.provider!.getBalance(
OptimismPortal.address OptimismPortal.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